From a6b1d82d079a99d09761ee5fbc66c49f33b42324 Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Wed, 4 Oct 2006 07:57:18 -0400 Subject: [JFFS2] kill warning RE debug-only variables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gcc emits the following warning on a 'allmodconfig' build: fs/jffs2/xattr.c: In function ‘unrefer_xattr_datum’: fs/jffs2/xattr.c:402: warning: unused variable ‘version’ fs/jffs2/xattr.c:402: warning: unused variable ‘xid’ Given that these variables are only used in the debug printk, and they merely remove a deref, we can easily kill the warning by adding the derefs to the debug printk. Signed-off-by: Jeff Garzik Signed-off-by: David Woodhouse diff --git a/fs/jffs2/xattr.c b/fs/jffs2/xattr.c index 4da09ce..4bb3f18 100644 --- a/fs/jffs2/xattr.c +++ b/fs/jffs2/xattr.c @@ -399,8 +399,6 @@ static void unrefer_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datu { /* must be called under down_write(xattr_sem) */ if (atomic_dec_and_lock(&xd->refcnt, &c->erase_completion_lock)) { - uint32_t xid = xd->xid, version = xd->version; - unload_xattr_datum(c, xd); xd->flags |= JFFS2_XFLAGS_DEAD; if (xd->node == (void *)xd) { @@ -411,7 +409,8 @@ static void unrefer_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datu } spin_unlock(&c->erase_completion_lock); - dbg_xattr("xdatum(xid=%u, version=%u) was removed.\n", xid, version); + dbg_xattr("xdatum(xid=%u, version=%u) was removed.\n", + xd->xid, xd->version); } } -- cgit v0.10.2 From 5467fb025537eb92313fd3a557b2051cb41ba5e8 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 6 Oct 2006 15:36:29 +0100 Subject: =?UTF-8?q?[MTD=20NAND]=20Initial=20import=20of=20CAF=C3=89=20NAND?= =?UTF-8?q?=20driver.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index c99302e..5e97e63 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -232,6 +232,13 @@ config MTD_NAND_CS553X If you say "m", the module will be called "cs553x_nand.ko". +config MTD_NAND_CAFE + tristate "NAND support for OLPC CAFÉ chip" + depends on PCI + help + Use NAND flash attached to the CAFÉ chip designed for the $100 + laptop. + config MTD_NAND_NANDSIM tristate "Support for NAND Flash Simulator" depends on MTD_NAND && MTD_PARTITIONS diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index f747593..9346b83 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_MTD_NAND) += nand.o nand_ecc.o obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o +obj-$(CONFIG_MTD_NAND_CAFE) += cafe_nand.o obj-$(CONFIG_MTD_NAND_SPIA) += spia.o obj-$(CONFIG_MTD_NAND_AMS_DELTA) += ams-delta.o obj-$(CONFIG_MTD_NAND_TOTO) += toto.o diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c new file mode 100644 index 0000000..8d7a795 --- /dev/null +++ b/drivers/mtd/nand/cafe_nand.c @@ -0,0 +1,635 @@ +/* + * cafe_nand.c + * + * Copyright © 2006 Red Hat, Inc. + * Copyright © 2006 David Woodhouse + */ + +//#define DEBUG + +#include +#undef DEBUG +#include +#include +#include +#include +#include +#include + +#define CAFE_NAND_CTRL1 0x00 +#define CAFE_NAND_CTRL2 0x04 +#define CAFE_NAND_CTRL3 0x08 +#define CAFE_NAND_STATUS 0x0c +#define CAFE_NAND_IRQ 0x10 +#define CAFE_NAND_IRQ_MASK 0x14 +#define CAFE_NAND_DATA_LEN 0x18 +#define CAFE_NAND_ADDR1 0x1c +#define CAFE_NAND_ADDR2 0x20 +#define CAFE_NAND_TIMING1 0x24 +#define CAFE_NAND_TIMING2 0x28 +#define CAFE_NAND_TIMING3 0x2c +#define CAFE_NAND_NONMEM 0x30 +#define CAFE_NAND_DMA_CTRL 0x40 +#define CAFE_NAND_DMA_ADDR0 0x44 +#define CAFE_NAND_DMA_ADDR1 0x48 +#define CAFE_NAND_READ_DATA 0x1000 +#define CAFE_NAND_WRITE_DATA 0x2000 + +struct cafe_priv { + struct nand_chip nand; + struct pci_dev *pdev; + void __iomem *mmio; + uint32_t ctl1; + uint32_t ctl2; + int datalen; + int nr_data; + int data_pos; + int page_addr; + dma_addr_t dmaaddr; + unsigned char *dmabuf; + +}; + +static int usedma = 1; +module_param(usedma, int, 0644); + +static int cafe_device_ready(struct mtd_info *mtd) +{ + struct cafe_priv *cafe = mtd->priv; + int result = !!(readl(cafe->mmio + CAFE_NAND_STATUS) | 0x40000000); + + uint32_t irqs = readl(cafe->mmio + 0x10); + writel(irqs, cafe->mmio+0x10); + dev_dbg(&cafe->pdev->dev, "NAND device is%s ready, IRQ %x (%x) (%x,%x)\n", + result?"":" not", irqs, readl(cafe->mmio + 0x10), + readl(cafe->mmio + 0x3008), readl(cafe->mmio + 0x300c)); + return result; +} + + +static void cafe_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + struct cafe_priv *cafe = mtd->priv; + + if (usedma) + memcpy(cafe->dmabuf + cafe->datalen, buf, len); + else + memcpy_toio(cafe->mmio + CAFE_NAND_WRITE_DATA + cafe->datalen, buf, len); + cafe->datalen += len; + + dev_dbg(&cafe->pdev->dev, "Copy 0x%x bytes to write buffer. datalen 0x%x\n", + len, cafe->datalen); +} + +static void cafe_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct cafe_priv *cafe = mtd->priv; + + if (usedma) + memcpy(buf, cafe->dmabuf + cafe->datalen, len); + else + memcpy_fromio(buf, cafe->mmio + CAFE_NAND_READ_DATA + cafe->datalen, len); + + dev_dbg(&cafe->pdev->dev, "Copy 0x%x bytes from position 0x%x in read buffer.\n", + len, cafe->datalen); + cafe->datalen += len; +} + +static uint8_t cafe_read_byte(struct mtd_info *mtd) +{ + struct cafe_priv *cafe = mtd->priv; + uint8_t d; + + cafe_read_buf(mtd, &d, 1); + dev_dbg(&cafe->pdev->dev, "Read %02x\n", d); + + return d; +} + +static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, + int column, int page_addr) +{ + struct cafe_priv *cafe = mtd->priv; + int adrbytes = 0; + uint32_t ctl1; + uint32_t doneint = 0x80000000; + int i; + + dev_dbg(&cafe->pdev->dev, "cmdfunc %02x, 0x%x, 0x%x\n", + command, column, page_addr); + + if (command == NAND_CMD_ERASE2 || command == NAND_CMD_PAGEPROG) { + /* Second half of a command we already calculated */ + writel(cafe->ctl2 | 0x100 | command, cafe->mmio + 0x04); + ctl1 = cafe->ctl1; + dev_dbg(&cafe->pdev->dev, "Continue command, ctl1 %08x, #data %d\n", + cafe->ctl1, cafe->nr_data); + goto do_command; + } + /* Reset ECC engine */ + writel(0, cafe->mmio + CAFE_NAND_CTRL2); + + /* Emulate NAND_CMD_READOOB on large-page chips */ + if (mtd->writesize > 512 && + command == NAND_CMD_READOOB) { + column += mtd->writesize; + command = NAND_CMD_READ0; + } + + /* FIXME: Do we need to send read command before sending data + for small-page chips, to position the buffer correctly? */ + + if (column != -1) { + writel(column, cafe->mmio + 0x1c); + adrbytes = 2; + if (page_addr != -1) + goto write_adr2; + } else if (page_addr != -1) { + writel(page_addr & 0xffff, cafe->mmio + 0x1c); + page_addr >>= 16; + write_adr2: + writel(page_addr, cafe->mmio+0x20); + adrbytes += 2; + if (mtd->size > mtd->writesize << 16) + adrbytes++; + } + + cafe->data_pos = cafe->datalen = 0; + + /* Set command valid bit */ + ctl1 = 0x80000000 | command; + + /* Set RD or WR bits as appropriate */ + if (command == NAND_CMD_READID || command == NAND_CMD_STATUS) { + ctl1 |= (1<<26); /* rd */ + /* Always 5 bytes, for now */ + cafe->datalen = 5; + /* And one address cycle -- even for STATUS, since the controller doesn't work without */ + adrbytes = 1; + } else if (command == NAND_CMD_READ0 || command == NAND_CMD_READ1 || + command == NAND_CMD_READOOB || command == NAND_CMD_RNDOUT) { + ctl1 |= 1<<26; /* rd */ + /* For now, assume just read to end of page */ + cafe->datalen = mtd->writesize + mtd->oobsize - column; + } else if (command == NAND_CMD_SEQIN) + ctl1 |= 1<<25; /* wr */ + + /* Set number of address bytes */ + if (adrbytes) + ctl1 |= ((adrbytes-1)|8) << 27; + + if (command == NAND_CMD_SEQIN || command == NAND_CMD_ERASE1) { + /* Ignore the first command of a pair; the hardware + deals with them both at once, later */ + cafe->ctl1 = ctl1; + cafe->ctl2 = 0; + dev_dbg(&cafe->pdev->dev, "Setup for delayed command, ctl1 %08x, dlen %x\n", + cafe->ctl1, cafe->datalen); + return; + } + /* RNDOUT and READ0 commands need a following byte */ + if (command == NAND_CMD_RNDOUT) + writel(cafe->ctl2 | 0x100 | NAND_CMD_RNDOUTSTART, cafe->mmio + CAFE_NAND_CTRL2); + else if (command == NAND_CMD_READ0 && mtd->writesize > 512) + writel(cafe->ctl2 | 0x100 | NAND_CMD_READSTART, cafe->mmio + CAFE_NAND_CTRL2); + + do_command: + if (cafe->datalen == 2112) + cafe->datalen = 2062; + dev_dbg(&cafe->pdev->dev, "dlen %x, ctl1 %x, ctl2 %x\n", + cafe->datalen, ctl1, readl(cafe->mmio+CAFE_NAND_CTRL2)); + /* NB: The datasheet lies -- we really should be subtracting 1 here */ + writel(cafe->datalen, cafe->mmio + CAFE_NAND_DATA_LEN); + writel(0x90000000, cafe->mmio + 0x10); + if (usedma && (ctl1 & (3<<25))) { + uint32_t dmactl = 0xc0000000 + cafe->datalen; + /* If WR or RD bits set, set up DMA */ + if (ctl1 & (1<<26)) { + /* It's a read */ + dmactl |= (1<<29); + /* ... so it's done when the DMA is done, not just + the command. */ + doneint = 0x10000000; + } + writel(dmactl, cafe->mmio + 0x40); + } +#if 0 + printk("DMA setup is %x, status %x, ctl1 %x\n", readl(cafe->mmio + 0x40), readl(cafe->mmio + 0x0c), readl(cafe->mmio)); + printk("DMA setup is %x, status %x, ctl1 %x\n", readl(cafe->mmio + 0x40), readl(cafe->mmio + 0x0c), readl(cafe->mmio)); +#endif + cafe->datalen = 0; + +#if 0 + printk("About to write command %08x\n", ctl1); + for (i=0; i< 0x5c; i+=4) + printk("Register %x: %08x\n", i, readl(cafe->mmio + i)); +#endif + writel(ctl1, cafe->mmio + CAFE_NAND_CTRL1); + /* Apply this short delay always to ensure that we do wait tWB in + * any case on any machine. */ + ndelay(100); + + if (1) { + int c = 50000; + uint32_t irqs; + + while (c--) { + irqs = readl(cafe->mmio + 0x10); + if (irqs & doneint) + break; + udelay(1); + if (!(c & 1000)) + dev_dbg(&cafe->pdev->dev, "Wait for ready, IRQ %x\n", irqs); + cpu_relax(); + } + writel(doneint, cafe->mmio + 0x10); + dev_dbg(&cafe->pdev->dev, "Command %x completed after %d usec, irqs %x (%x)\n", command, 50000-c, irqs, readl(cafe->mmio + 0x10)); + } + + + cafe->ctl2 &= ~(1<<8); + cafe->ctl2 &= ~(1<<30); + + switch (command) { + + case NAND_CMD_CACHEDPROG: + case NAND_CMD_PAGEPROG: + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_SEQIN: + case NAND_CMD_RNDIN: + case NAND_CMD_STATUS: + case NAND_CMD_DEPLETE1: + case NAND_CMD_RNDOUT: + case NAND_CMD_STATUS_ERROR: + case NAND_CMD_STATUS_ERROR0: + case NAND_CMD_STATUS_ERROR1: + case NAND_CMD_STATUS_ERROR2: + case NAND_CMD_STATUS_ERROR3: + writel(cafe->ctl2, cafe->mmio + CAFE_NAND_CTRL2); + return; + } + nand_wait_ready(mtd); + writel(cafe->ctl2, cafe->mmio + CAFE_NAND_CTRL2); +} + +static void cafe_select_chip(struct mtd_info *mtd, int chipnr) +{ + //struct cafe_priv *cafe = mtd->priv; + // dev_dbg(&cafe->pdev->dev, "select_chip %d\n", chipnr); +} +static int cafe_nand_interrupt(int irq, void *id, struct pt_regs *regs) +{ + struct mtd_info *mtd = id; + struct cafe_priv *cafe = mtd->priv; + uint32_t irqs = readl(cafe->mmio + 0x10); + writel(irqs & ~0x90000000, cafe->mmio + 0x10); + if (!irqs) + return IRQ_NONE; + + dev_dbg(&cafe->pdev->dev, "irq, bits %x (%x)\n", irqs, readl(cafe->mmio + 0x10)); + return IRQ_HANDLED; +} + +static void cafe_nand_bug(struct mtd_info *mtd) +{ + BUG(); +} + +static int cafe_nand_write_oob(struct mtd_info *mtd, + struct nand_chip *chip, int page) +{ + int status = 0; + + WARN_ON(chip->oob_poi != chip->buffers->oobwbuf); + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + + return status & NAND_STATUS_FAIL ? -EIO : 0; +} + +/* Don't use -- use nand_read_oob_std for now */ +static int cafe_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page, int sndcmd) +{ + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + return 1; +} +/** + * cafe_nand_read_page_syndrome - {REPLACABLE] hardware ecc syndrom based page read + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * + * The hw generator calculates the error syndrome automatically. Therefor + * we need a special oob layout and handling. + */ +static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf) +{ + struct cafe_priv *cafe = mtd->priv; + + WARN_ON(chip->oob_poi != chip->buffers->oobrbuf); + + dev_dbg(&cafe->pdev->dev, "ECC result %08x SYN1,2 %08x\n", readl(cafe->mmio + 0x3c), readl(cafe->mmio + 0x50)); + + chip->read_buf(mtd, buf, mtd->writesize); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + + return 0; +} + +static char foo[14]; +static void cafe_nand_write_page_lowlevel(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf) +{ + struct cafe_priv *cafe = mtd->priv; + + WARN_ON(chip->oob_poi != chip->buffers->oobwbuf); + + chip->write_buf(mtd, buf, mtd->writesize); + chip->write_buf(mtd, foo, 14); + // chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + + /* Set up ECC autogeneration */ + cafe->ctl2 |= (1<<27) | (1<<30); + if (mtd->writesize == 2048) + cafe->ctl2 |= (1<<29); +} + +static int cafe_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int page, int cached, int raw) +{ + int status; + + WARN_ON(chip->oob_poi != chip->buffers->oobwbuf); + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); + + if (unlikely(raw)) + chip->ecc.write_page_raw(mtd, chip, buf); + else + chip->ecc.write_page(mtd, chip, buf); + + /* + * Cached progamming disabled for now, Not sure if its worth the + * trouble. The speed gain is not very impressive. (2.3->2.6Mib/s) + */ + cached = 0; + + if (!cached || !(chip->options & NAND_CACHEPRG)) { + + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + /* + * See if operation failed and additional status checks are + * available + */ + if ((status & NAND_STATUS_FAIL) && (chip->errstat)) + status = chip->errstat(mtd, chip, FL_WRITING, status, + page); + + if (status & NAND_STATUS_FAIL) + return -EIO; + } else { + chip->cmdfunc(mtd, NAND_CMD_CACHEDPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + } + +#ifdef CONFIG_MTD_NAND_VERIFY_WRITE + /* Send command to read back the data */ + chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + + if (chip->verify_buf(mtd, buf, mtd->writesize)) + return -EIO; +#endif + return 0; +} + + +static int __devinit cafe_nand_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct mtd_info *mtd; + struct cafe_priv *cafe; + uint32_t ctrl; + int err = 0; + + err = pci_enable_device(pdev); + if (err) + return err; + + pci_set_master(pdev); + + mtd = kzalloc(sizeof(*mtd) + sizeof(struct cafe_priv), GFP_KERNEL); + if (!mtd) { + dev_warn(&pdev->dev, "failed to alloc mtd_info\n"); + return -ENOMEM; + } + cafe = (void *)(&mtd[1]); + + mtd->priv = cafe; + mtd->owner = THIS_MODULE; + + cafe->pdev = pdev; + cafe->mmio = pci_iomap(pdev, 0, 0); + if (!cafe->mmio) { + dev_warn(&pdev->dev, "failed to iomap\n"); + err = -ENOMEM; + goto out_free_mtd; + } + cafe->dmabuf = dma_alloc_coherent(&cafe->pdev->dev, 2112 + sizeof(struct nand_buffers), + &cafe->dmaaddr, GFP_KERNEL); + if (!cafe->dmabuf) { + err = -ENOMEM; + goto out_ior; + } + cafe->nand.buffers = (void *)cafe->dmabuf + 2112; + + cafe->nand.cmdfunc = cafe_nand_cmdfunc; + cafe->nand.dev_ready = cafe_device_ready; + cafe->nand.read_byte = cafe_read_byte; + cafe->nand.read_buf = cafe_read_buf; + cafe->nand.write_buf = cafe_write_buf; + cafe->nand.select_chip = cafe_select_chip; + + cafe->nand.chip_delay = 0; + + /* Enable the following for a flash based bad block table */ + cafe->nand.options = NAND_USE_FLASH_BBT | NAND_NO_AUTOINCR | NAND_OWN_BUFFERS; + + /* Timings from Marvell's test code (not verified or calculated by us) */ + writel(0xffffffff, cafe->mmio + CAFE_NAND_IRQ_MASK); +#if 1 + writel(0x01010a0a, cafe->mmio + CAFE_NAND_TIMING1); + writel(0x24121212, cafe->mmio + CAFE_NAND_TIMING2); + writel(0x11000000, cafe->mmio + CAFE_NAND_TIMING3); +#else + writel(0xffffffff, cafe->mmio + CAFE_NAND_TIMING1); + writel(0xffffffff, cafe->mmio + CAFE_NAND_TIMING2); + writel(0xffffffff, cafe->mmio + CAFE_NAND_TIMING3); +#endif + writel(0xdfffffff, cafe->mmio + 0x14); + err = request_irq(pdev->irq, &cafe_nand_interrupt, SA_SHIRQ, "CAFE NAND", mtd); + if (err) { + dev_warn(&pdev->dev, "Could not register IRQ %d\n", pdev->irq); + + goto out_free_dma; + } +#if 1 + /* Disable master reset, enable NAND clock */ + ctrl = readl(cafe->mmio + 0x3004); + ctrl &= 0xffffeff0; + ctrl |= 0x00007000; + writel(ctrl | 0x05, cafe->mmio + 0x3004); + writel(ctrl | 0x0a, cafe->mmio + 0x3004); + writel(0, cafe->mmio + 0x40); + + writel(0x7006, cafe->mmio + 0x3004); + writel(0x700a, cafe->mmio + 0x3004); + + /* Set up DMA address */ + writel(cafe->dmaaddr & 0xffffffff, cafe->mmio + 0x44); + if (sizeof(cafe->dmaaddr) > 4) + writel((cafe->dmaaddr >> 16) >> 16, cafe->mmio + 0x48); + else + writel(0, cafe->mmio + 0x48); + dev_dbg(&cafe->pdev->dev, "Set DMA address to %x (virt %p)\n", + readl(cafe->mmio+0x44), cafe->dmabuf); + + /* Enable NAND IRQ in global IRQ mask register */ + writel(0x80000007, cafe->mmio + 0x300c); + dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n", + readl(cafe->mmio + 0x3004), readl(cafe->mmio + 0x300c)); +#endif +#if 1 + mtd->writesize=2048; + mtd->oobsize = 0x40; + memset(cafe->dmabuf, 0xa5, 2112); + cafe->nand.cmdfunc(mtd, NAND_CMD_READID, 0, -1); + cafe->nand.read_byte(mtd); + cafe->nand.read_byte(mtd); + cafe->nand.read_byte(mtd); + cafe->nand.read_byte(mtd); + cafe->nand.read_byte(mtd); +#endif +#if 0 + cafe->nand.cmdfunc(mtd, NAND_CMD_READ0, 0, 0); + // nand_wait_ready(mtd); + cafe->nand.read_byte(mtd); + cafe->nand.read_byte(mtd); + cafe->nand.read_byte(mtd); + cafe->nand.read_byte(mtd); +#endif +#if 0 + writel(0x84600070, cafe->mmio); + udelay(10); + dev_dbg(&cafe->pdev->dev, "Status %x\n", readl(cafe->mmio + 0x30)); +#endif + /* Scan to find existance of the device */ + if (nand_scan_ident(mtd, 1)) { + err = -ENXIO; + goto out_irq; + } + + cafe->ctl2 = 1<<27; /* Reed-Solomon ECC */ + if (mtd->writesize == 2048) + cafe->ctl2 |= 1<<29; /* 2KiB page size */ + + /* Set up ECC according to the type of chip we found */ + if (mtd->writesize == 512 || mtd->writesize == 2048) { + cafe->nand.ecc.mode = NAND_ECC_HW_SYNDROME; + cafe->nand.ecc.size = mtd->writesize; + cafe->nand.ecc.bytes = 14; + cafe->nand.ecc.hwctl = (void *)cafe_nand_bug; + cafe->nand.ecc.calculate = (void *)cafe_nand_bug; + cafe->nand.ecc.correct = (void *)cafe_nand_bug; + cafe->nand.write_page = cafe_nand_write_page; + cafe->nand.ecc.write_page = cafe_nand_write_page_lowlevel; + cafe->nand.ecc.write_oob = cafe_nand_write_oob; + cafe->nand.ecc.read_page = cafe_nand_read_page; + cafe->nand.ecc.read_oob = cafe_nand_read_oob; + + } else { + printk(KERN_WARNING "Unexpected NAND flash writesize %d. Using software ECC\n", + mtd->writesize); + cafe->nand.ecc.mode = NAND_ECC_NONE; + } + + err = nand_scan_tail(mtd); + if (err) + goto out_irq; + + + pci_set_drvdata(pdev, mtd); + add_mtd_device(mtd); + goto out; + + out_irq: + /* Disable NAND IRQ in global IRQ mask register */ + writel(~1 & readl(cafe->mmio + 0x300c), cafe->mmio + 0x300c); + free_irq(pdev->irq, mtd); + out_free_dma: + dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr); + out_ior: + pci_iounmap(pdev, cafe->mmio); + out_free_mtd: + kfree(mtd); + out: + return err; +} + +static void __devexit cafe_nand_remove(struct pci_dev *pdev) +{ + struct mtd_info *mtd = pci_get_drvdata(pdev); + struct cafe_priv *cafe = mtd->priv; + + del_mtd_device(mtd); + /* Disable NAND IRQ in global IRQ mask register */ + writel(~1 & readl(cafe->mmio + 0x300c), cafe->mmio + 0x300c); + free_irq(pdev->irq, mtd); + nand_release(mtd); + pci_iounmap(pdev, cafe->mmio); + dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr); + kfree(mtd); +} + +static struct pci_device_id cafe_nand_tbl[] = { + { 0x11ab, 0x4100, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MEMORY_FLASH << 8, 0xFFFF0 } +}; + +MODULE_DEVICE_TABLE(pci, cafe_nand_tbl); + +static struct pci_driver cafe_nand_pci_driver = { + .name = "CAFÉ NAND", + .id_table = cafe_nand_tbl, + .probe = cafe_nand_probe, + .remove = __devexit_p(cafe_nand_remove), +#ifdef CONFIG_PMx + .suspend = cafe_nand_suspend, + .resume = cafe_nand_resume, +#endif +}; + +static int cafe_nand_init(void) +{ + return pci_register_driver(&cafe_nand_pci_driver); +} + +static void cafe_nand_exit(void) +{ + pci_unregister_driver(&cafe_nand_pci_driver); +} +module_init(cafe_nand_init); +module_exit(cafe_nand_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Woodhouse "); +MODULE_DESCRIPTION("NAND flash driver for OLPC CAFE chip"); + +/* Correct ECC for 2048 bytes of 0xff: + 41 a0 71 65 54 27 f3 93 ec a9 be ed 0b a1 */ -- cgit v0.10.2 From 1ef93a0f668c8736cb6b6c3a43a5b8101efa24af Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Mon, 9 Oct 2006 01:16:38 +0200 Subject: [MTD] SSFDC must depend on BLOCK MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch fixes the following compile error with CONFIG_SSFDC=m, CONFIG_BLOCK=n: <-- snip --> ... CC [M] drivers/mtd/mtd_blkdevs.o /home/bunk/linux/kernel-2.6/git/linux-2.6/drivers/mtd/mtd_blkdevs.c:40: warning: ‘struct request’ declared inside parameter list /home/bunk/linux/kernel-2.6/git/linux-2.6/drivers/mtd/mtd_blkdevs.c:40: warning: its scope is only this definition or declaration, which is probably not what you want /home/bunk/linux/kernel-2.6/git/linux-2.6/drivers/mtd/mtd_blkdevs.c: In function ‘do_blktrans_request’: /home/bunk/linux/kernel-2.6/git/linux-2.6/drivers/mtd/mtd_blkdevs.c:45: error: dereferencing pointer to incomplete type ... make[3]: *** [drivers/mtd/mtd_blkdevs.o] Error 1 <-- snip --> Bug report by Jesper Juhl. This patch also removes a pointless "default n" from the SSFDC option. Signed-off-by: Adrian Bunk Signed-off-by: David Woodhouse diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index a304b34..291660a 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -265,8 +265,7 @@ config RFD_FTL config SSFDC tristate "NAND SSFDC (SmartMedia) read only translation layer" - depends on MTD - default n + depends on MTD && BLOCK help This enables read only access to SmartMedia formatted NAND flash. You can mount it with FAT file system. -- cgit v0.10.2 From 50dd096973f1d95aa03c6a6d9e148d706b62b68e Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Sun, 1 Oct 2006 00:28:50 +0200 Subject: ACPI: Remove unnecessary from/to-void* and to-void casts in drivers/acpi Signed-off-by: Jan Engelhardt Signed-off-by: Len Brown diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c index 11abc7b..46e5866 100644 --- a/drivers/acpi/ac.c +++ b/drivers/acpi/ac.c @@ -109,7 +109,7 @@ static struct proc_dir_entry *acpi_ac_dir; static int acpi_ac_seq_show(struct seq_file *seq, void *offset) { - struct acpi_ac *ac = (struct acpi_ac *)seq->private; + struct acpi_ac *ac = seq->private; if (!ac) @@ -187,7 +187,7 @@ static int acpi_ac_remove_fs(struct acpi_device *device) static void acpi_ac_notify(acpi_handle handle, u32 event, void *data) { - struct acpi_ac *ac = (struct acpi_ac *)data; + struct acpi_ac *ac = data; struct acpi_device *device = NULL; @@ -269,7 +269,7 @@ static int acpi_ac_remove(struct acpi_device *device, int type) if (!device || !acpi_driver_data(device)) return -EINVAL; - ac = (struct acpi_ac *)acpi_driver_data(device); + ac = acpi_driver_data(device); status = acpi_remove_notify_handler(device->handle, ACPI_ALL_NOTIFY, acpi_ac_notify); diff --git a/drivers/acpi/acpi_memhotplug.c b/drivers/acpi/acpi_memhotplug.c index 98099de..1368783 100644 --- a/drivers/acpi/acpi_memhotplug.c +++ b/drivers/acpi/acpi_memhotplug.c @@ -427,7 +427,7 @@ static int acpi_memory_device_remove(struct acpi_device *device, int type) if (!device || !acpi_driver_data(device)) return -EINVAL; - mem_device = (struct acpi_memory_device *)acpi_driver_data(device); + mem_device = acpi_driver_data(device); kfree(mem_device); return 0; diff --git a/drivers/acpi/asus_acpi.c b/drivers/acpi/asus_acpi.c index e9ee4c5..f7db8ea 100644 --- a/drivers/acpi/asus_acpi.c +++ b/drivers/acpi/asus_acpi.c @@ -1128,7 +1128,7 @@ static int asus_hotk_get_info(void) if (ACPI_FAILURE(status)) printk(KERN_WARNING " Couldn't get the DSDT table header\n"); else - asus_info = (struct acpi_table_header *)dsdt.pointer; + asus_info = dsdt.pointer; /* We have to write 0 on init this far for all ASUS models */ if (!write_acpi_int(hotk->handle, "INIT", 0, &buffer)) { @@ -1150,7 +1150,7 @@ static int asus_hotk_get_info(void) * asus_model_match() and try something completely different. */ if (buffer.pointer) { - model = (union acpi_object *)buffer.pointer; + model = buffer.pointer; switch (model->type) { case ACPI_TYPE_STRING: string = model->string.pointer; @@ -1245,8 +1245,7 @@ static int asus_hotk_add(struct acpi_device *device) printk(KERN_NOTICE "Asus Laptop ACPI Extras version %s\n", ASUS_ACPI_VERSION); - hotk = - (struct asus_hotk *)kmalloc(sizeof(struct asus_hotk), GFP_KERNEL); + hotk = kmalloc(sizeof(struct asus_hotk), GFP_KERNEL); if (!hotk) return -ENOMEM; memset(hotk, 0, sizeof(struct asus_hotk)); diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 9810e2a..adb0d27 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -147,7 +147,7 @@ acpi_battery_get_info(struct acpi_battery *battery, return -ENODEV; } - package = (union acpi_object *)buffer.pointer; + package = buffer.pointer; /* Extract Package Data */ @@ -177,7 +177,7 @@ acpi_battery_get_info(struct acpi_battery *battery, kfree(buffer.pointer); if (!result) - (*bif) = (struct acpi_battery_info *)data.pointer; + (*bif) = data.pointer; return result; } @@ -207,7 +207,7 @@ acpi_battery_get_status(struct acpi_battery *battery, return -ENODEV; } - package = (union acpi_object *)buffer.pointer; + package = buffer.pointer; /* Extract Package Data */ @@ -237,7 +237,7 @@ acpi_battery_get_status(struct acpi_battery *battery, kfree(buffer.pointer); if (!result) - (*bst) = (struct acpi_battery_status *)data.pointer; + (*bst) = data.pointer; return result; } @@ -332,7 +332,7 @@ static struct proc_dir_entry *acpi_battery_dir; static int acpi_battery_read_info(struct seq_file *seq, void *offset) { int result = 0; - struct acpi_battery *battery = (struct acpi_battery *)seq->private; + struct acpi_battery *battery = seq->private; struct acpi_battery_info *bif = NULL; char *units = "?"; @@ -416,7 +416,7 @@ static int acpi_battery_info_open_fs(struct inode *inode, struct file *file) static int acpi_battery_read_state(struct seq_file *seq, void *offset) { int result = 0; - struct acpi_battery *battery = (struct acpi_battery *)seq->private; + struct acpi_battery *battery = seq->private; struct acpi_battery_status *bst = NULL; char *units = "?"; @@ -492,7 +492,7 @@ static int acpi_battery_state_open_fs(struct inode *inode, struct file *file) static int acpi_battery_read_alarm(struct seq_file *seq, void *offset) { - struct acpi_battery *battery = (struct acpi_battery *)seq->private; + struct acpi_battery *battery = seq->private; char *units = "?"; @@ -529,8 +529,8 @@ acpi_battery_write_alarm(struct file *file, { int result = 0; char alarm_string[12] = { '\0' }; - struct seq_file *m = (struct seq_file *)file->private_data; - struct acpi_battery *battery = (struct acpi_battery *)m->private; + struct seq_file *m = file->private_data; + struct acpi_battery *battery = m->private; if (!battery || (count > sizeof(alarm_string) - 1)) @@ -656,7 +656,7 @@ static int acpi_battery_remove_fs(struct acpi_device *device) static void acpi_battery_notify(acpi_handle handle, u32 event, void *data) { - struct acpi_battery *battery = (struct acpi_battery *)data; + struct acpi_battery *battery = data; struct acpi_device *device = NULL; @@ -740,7 +740,7 @@ static int acpi_battery_remove(struct acpi_device *device, int type) if (!device || !acpi_driver_data(device)) return -EINVAL; - battery = (struct acpi_battery *)acpi_driver_data(device); + battery = acpi_driver_data(device); status = acpi_remove_notify_handler(device->handle, ACPI_ALL_NOTIFY, diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index 5ef885e..fe914eb 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -109,7 +109,7 @@ static struct proc_dir_entry *acpi_button_dir; static int acpi_button_info_seq_show(struct seq_file *seq, void *offset) { - struct acpi_button *button = (struct acpi_button *)seq->private; + struct acpi_button *button = seq->private; if (!button || !button->device) @@ -128,7 +128,7 @@ static int acpi_button_info_open_fs(struct inode *inode, struct file *file) static int acpi_button_state_seq_show(struct seq_file *seq, void *offset) { - struct acpi_button *button = (struct acpi_button *)seq->private; + struct acpi_button *button = seq->private; acpi_status status; unsigned long state; @@ -253,7 +253,7 @@ static int acpi_button_remove_fs(struct acpi_device *device) static void acpi_button_notify(acpi_handle handle, u32 event, void *data) { - struct acpi_button *button = (struct acpi_button *)data; + struct acpi_button *button = data; if (!button || !button->device) @@ -275,7 +275,7 @@ static void acpi_button_notify(acpi_handle handle, u32 event, void *data) static acpi_status acpi_button_notify_fixed(void *data) { - struct acpi_button *button = (struct acpi_button *)data; + struct acpi_button *button = data; if (!button) diff --git a/drivers/acpi/container.c b/drivers/acpi/container.c index 871aa52..a153817 100644 --- a/drivers/acpi/container.c +++ b/drivers/acpi/container.c @@ -117,7 +117,7 @@ static int acpi_container_remove(struct acpi_device *device, int type) acpi_status status = AE_OK; struct acpi_container *pc = NULL; - pc = (struct acpi_container *)acpi_driver_data(device); + pc = acpi_driver_data(device); kfree(pc); return status; } diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 578b99b..3c3dee8 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -524,7 +524,7 @@ EXPORT_SYMBOL_GPL(unregister_hotplug_dock_device); */ static void dock_notify(acpi_handle handle, u32 event, void *data) { - struct dock_station *ds = (struct dock_station *)data; + struct dock_station *ds = data; switch (event) { case ACPI_NOTIFY_BUS_CHECK: @@ -587,7 +587,7 @@ find_dock_devices(acpi_handle handle, u32 lvl, void *context, void **rv) { acpi_status status; acpi_handle tmp; - struct dock_station *ds = (struct dock_station *)context; + struct dock_station *ds = context; struct dock_dependent_device *dd; status = acpi_bus_get_ejd(handle, &tmp); @@ -702,7 +702,7 @@ static int dock_remove(void) static acpi_status find_dock(acpi_handle handle, u32 lvl, void *context, void **rv) { - int *count = (int *)context; + int *count = context; acpi_status status = AE_OK; if (is_dock(handle)) { diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index e5d7963..d560b5e 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -716,7 +716,7 @@ static void acpi_ec_gpe_poll_query(void *ec_cxt) } static void acpi_ec_gpe_intr_query(void *ec_cxt) { - union acpi_ec *ec = (union acpi_ec *)ec_cxt; + union acpi_ec *ec = ec_cxt; u32 value; int result = -ENODATA; static char object_name[5] = { '_', 'Q', '0', '0', '\0' }; @@ -752,7 +752,7 @@ static u32 acpi_ec_gpe_handler(void *data) static u32 acpi_ec_gpe_poll_handler(void *data) { acpi_status status = AE_OK; - union acpi_ec *ec = (union acpi_ec *)data; + union acpi_ec *ec = data; if (!ec) return ACPI_INTERRUPT_NOT_HANDLED; @@ -770,7 +770,7 @@ static u32 acpi_ec_gpe_intr_handler(void *data) { acpi_status status = AE_OK; u32 value; - union acpi_ec *ec = (union acpi_ec *)data; + union acpi_ec *ec = data; if (!ec) return ACPI_INTERRUPT_NOT_HANDLED; @@ -848,7 +848,7 @@ acpi_ec_space_handler(u32 function, return AE_BAD_PARAMETER; } - ec = (union acpi_ec *)handler_context; + ec = handler_context; next_byte: switch (function) { @@ -905,7 +905,7 @@ static struct proc_dir_entry *acpi_ec_dir; static int acpi_ec_read_info(struct seq_file *seq, void *offset) { - union acpi_ec *ec = (union acpi_ec *)seq->private; + union acpi_ec *ec = seq->private; if (!ec) @@ -1136,7 +1136,7 @@ static int acpi_ec_remove(struct acpi_device *device, int type) static acpi_status acpi_ec_io_ports(struct acpi_resource *resource, void *context) { - union acpi_ec *ec = (union acpi_ec *)context; + union acpi_ec *ec = context; struct acpi_generic_address *addr; if (resource->type != ACPI_RESOURCE_TYPE_IO) { diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c index 045c894..c413e69 100644 --- a/drivers/acpi/fan.c +++ b/drivers/acpi/fan.c @@ -99,8 +99,8 @@ acpi_fan_write_state(struct file *file, const char __user * buffer, size_t count, loff_t * ppos) { int result = 0; - struct seq_file *m = (struct seq_file *)file->private_data; - struct acpi_fan *fan = (struct acpi_fan *)m->private; + struct seq_file *m = file->private_data; + struct acpi_fan *fan = m->private; char state_string[12] = { '\0' }; @@ -229,7 +229,7 @@ static int acpi_fan_remove(struct acpi_device *device, int type) if (!device || !acpi_driver_data(device)) return -EINVAL; - fan = (struct acpi_fan *)acpi_driver_data(device); + fan = acpi_driver_data(device); acpi_fan_remove_fs(device); diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index 10f160d..ba5686f 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -96,7 +96,7 @@ struct acpi_find_pci_root { static acpi_status do_root_bridge_busnr_callback(struct acpi_resource *resource, void *data) { - unsigned long *busnr = (unsigned long *)data; + unsigned long *busnr = data; struct acpi_resource_address64 address; if (resource->type != ACPI_RESOURCE_TYPE_ADDRESS16 && @@ -217,7 +217,7 @@ do_acpi_find_child(acpi_handle handle, u32 lvl, void *context, void **rv) acpi_status status; struct acpi_device_info *info; struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - struct acpi_find_child *find = (struct acpi_find_child *)context; + struct acpi_find_child *find = context; status = acpi_get_object_info(handle, &buffer); if (ACPI_SUCCESS(status)) { diff --git a/drivers/acpi/hotkey.c b/drivers/acpi/hotkey.c index 1ba2db6..8edfb92 100644 --- a/drivers/acpi/hotkey.c +++ b/drivers/acpi/hotkey.c @@ -265,8 +265,7 @@ static char *format_result(union acpi_object *object) static int hotkey_polling_seq_show(struct seq_file *seq, void *offset) { - struct acpi_polling_hotkey *poll_hotkey = - (struct acpi_polling_hotkey *)seq->private; + struct acpi_polling_hotkey *poll_hotkey = seq->private; char *buf; @@ -577,7 +576,7 @@ init_poll_hotkey_device(union acpi_hotkey *key, char **config_entry, if (ACPI_FAILURE(status)) goto do_fail_zero; key->poll_hotkey.poll_result = - (union acpi_object *)kmalloc(sizeof(union acpi_object), GFP_KERNEL); + kmalloc(sizeof(union acpi_object), GFP_KERNEL); if (!key->poll_hotkey.poll_result) goto do_fail_zero; return AE_OK; diff --git a/drivers/acpi/i2c_ec.c b/drivers/acpi/i2c_ec.c index 6342e61..82e3e64 100644 --- a/drivers/acpi/i2c_ec.c +++ b/drivers/acpi/i2c_ec.c @@ -393,7 +393,7 @@ static void __exit acpi_ec_hc_exit(void) struct acpi_ec_hc *acpi_get_ec_hc(struct acpi_device *device) { - return ((struct acpi_ec_hc *)acpi_driver_data(device->parent)); + return acpi_driver_data(device->parent); } EXPORT_SYMBOL(acpi_get_ec_hc); diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index 15fc124..6fbb420 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -1721,7 +1721,7 @@ static struct ibm_struct ibms[] = { static int dispatch_read(char *page, char **start, off_t off, int count, int *eof, void *data) { - struct ibm_struct *ibm = (struct ibm_struct *)data; + struct ibm_struct *ibm = data; int len; if (!ibm || !ibm->read) @@ -1746,7 +1746,7 @@ static int dispatch_read(char *page, char **start, off_t off, int count, static int dispatch_write(struct file *file, const char __user * userbuf, unsigned long count, void *data) { - struct ibm_struct *ibm = (struct ibm_struct *)data; + struct ibm_struct *ibm = data; char *kernbuf; int ret; @@ -1775,7 +1775,7 @@ static int dispatch_write(struct file *file, const char __user * userbuf, static void dispatch_notify(acpi_handle handle, u32 event, void *data) { - struct ibm_struct *ibm = (struct ibm_struct *)data; + struct ibm_struct *ibm = data; if (!ibm || !ibm->notify) return; diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c index e5e448e..bd96a70 100644 --- a/drivers/acpi/numa.c +++ b/drivers/acpi/numa.c @@ -248,7 +248,7 @@ int acpi_get_pxm(acpi_handle h) handle = phandle; status = acpi_evaluate_integer(handle, "_PXM", NULL, &pxm); if (ACPI_SUCCESS(status)) - return (int)pxm; + return pxm; status = acpi_get_parent(handle, &phandle); } while (ACPI_SUCCESS(status)); return -1; diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 068fe4f..2ed2d70 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -569,7 +569,7 @@ static void acpi_os_execute_deferred(void *context) struct acpi_os_dpc *dpc = NULL; - dpc = (struct acpi_os_dpc *)context; + dpc = context; if (!dpc) { printk(KERN_ERR PREFIX "Invalid (NULL) context\n"); return; @@ -1060,7 +1060,7 @@ acpi_os_create_cache(char *name, u16 size, u16 depth, acpi_cache_t ** cache) acpi_status acpi_os_purge_cache(acpi_cache_t * cache) { - (void)kmem_cache_shrink(cache); + kmem_cache_shrink(cache); return (AE_OK); } diff --git a/drivers/acpi/pci_bind.c b/drivers/acpi/pci_bind.c index 1e2ae6e..70b440f 100644 --- a/drivers/acpi/pci_bind.c +++ b/drivers/acpi/pci_bind.c @@ -281,7 +281,7 @@ int acpi_pci_unbind(struct acpi_device *device) if (!device || !device->parent) return -EINVAL; - pathname = (char *)kmalloc(ACPI_PATHNAME_MAX, GFP_KERNEL); + pathname = kmalloc(ACPI_PATHNAME_MAX, GFP_KERNEL); if (!pathname) return -ENOMEM; memset(pathname, 0, ACPI_PATHNAME_MAX); @@ -332,7 +332,7 @@ acpi_pci_bind_root(struct acpi_device *device, struct acpi_buffer buffer = { 0, NULL }; - pathname = (char *)kmalloc(ACPI_PATHNAME_MAX, GFP_KERNEL); + pathname = kmalloc(ACPI_PATHNAME_MAX, GFP_KERNEL); if (!pathname) return -ENOMEM; memset(pathname, 0, ACPI_PATHNAME_MAX); diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c index feda034..226892e 100644 --- a/drivers/acpi/pci_irq.c +++ b/drivers/acpi/pci_irq.c @@ -161,7 +161,7 @@ int acpi_pci_irq_add_prt(acpi_handle handle, int segment, int bus) static int first_time = 1; - pathname = (char *)kmalloc(ACPI_PATHNAME_MAX, GFP_KERNEL); + pathname = kmalloc(ACPI_PATHNAME_MAX, GFP_KERNEL); if (!pathname) return -ENOMEM; memset(pathname, 0, ACPI_PATHNAME_MAX); diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c index 7f3e7e7..6718198 100644 --- a/drivers/acpi/pci_link.c +++ b/drivers/acpi/pci_link.c @@ -103,7 +103,7 @@ DEFINE_MUTEX(acpi_link_lock); static acpi_status acpi_pci_link_check_possible(struct acpi_resource *resource, void *context) { - struct acpi_pci_link *link = (struct acpi_pci_link *)context; + struct acpi_pci_link *link = context; u32 i = 0; @@ -613,7 +613,7 @@ acpi_pci_link_allocate_irq(acpi_handle handle, return -1; } - link = (struct acpi_pci_link *)acpi_driver_data(device); + link = acpi_driver_data(device); if (!link) { printk(KERN_ERR PREFIX "Invalid link context\n"); return -1; @@ -668,7 +668,7 @@ int acpi_pci_link_free_irq(acpi_handle handle) return -1; } - link = (struct acpi_pci_link *)acpi_driver_data(device); + link = acpi_driver_data(device); if (!link) { printk(KERN_ERR PREFIX "Invalid link context\n"); return -1; @@ -808,7 +808,7 @@ static int acpi_pci_link_remove(struct acpi_device *device, int type) if (!device || !acpi_driver_data(device)) return -EINVAL; - link = (struct acpi_pci_link *)acpi_driver_data(device); + link = acpi_driver_data(device); mutex_lock(&acpi_link_lock); list_del(&link->node); diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 0984a1e..03e0374 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -119,7 +119,7 @@ EXPORT_SYMBOL(acpi_pci_unregister_driver); static acpi_status get_root_bridge_busnr_callback(struct acpi_resource *resource, void *data) { - int *busnr = (int *)data; + int *busnr = data; struct acpi_resource_address64 address; if (resource->type != ACPI_RESOURCE_TYPE_ADDRESS16 && @@ -331,7 +331,7 @@ static int acpi_pci_root_remove(struct acpi_device *device, int type) if (!device || !acpi_driver_data(device)) return -EINVAL; - root = (struct acpi_pci_root *)acpi_driver_data(device); + root = acpi_driver_data(device); kfree(root); diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index fec225d..e9dab54 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -108,7 +108,7 @@ acpi_power_get_context(acpi_handle handle, return result; } - *resource = (struct acpi_power_resource *)acpi_driver_data(device); + *resource = acpi_driver_data(device); if (!resource) return -ENODEV; @@ -445,7 +445,7 @@ static int acpi_power_seq_show(struct seq_file *seq, void *offset) struct acpi_power_resource *resource = NULL; - resource = (struct acpi_power_resource *)seq->private; + resource = seq->private; if (!resource) goto end; @@ -593,7 +593,7 @@ static int acpi_power_remove(struct acpi_device *device, int type) if (!device || !acpi_driver_data(device)) return -EINVAL; - resource = (struct acpi_power_resource *)acpi_driver_data(device); + resource = acpi_driver_data(device); acpi_power_remove_fs(device); diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index b13d644..44c624b 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -277,7 +277,7 @@ static struct proc_dir_entry *acpi_processor_dir = NULL; static int acpi_processor_info_seq_show(struct seq_file *seq, void *offset) { - struct acpi_processor *pr = (struct acpi_processor *)seq->private; + struct acpi_processor *pr = seq->private; if (!pr) @@ -542,12 +542,12 @@ static int acpi_processor_start(struct acpi_device *device) * Don't trust it blindly */ if (processor_device_array[pr->id] != NULL && - processor_device_array[pr->id] != (void *)device) { + processor_device_array[pr->id] != device) { printk(KERN_WARNING "BIOS reported wrong ACPI id" "for the processor\n"); return -ENODEV; } - processor_device_array[pr->id] = (void *)device; + processor_device_array[pr->id] = device; processors[pr->id] = pr; @@ -578,7 +578,7 @@ static int acpi_processor_start(struct acpi_device *device) static void acpi_processor_notify(acpi_handle handle, u32 event, void *data) { - struct acpi_processor *pr = (struct acpi_processor *)data; + struct acpi_processor *pr = data; struct acpi_device *device = NULL; @@ -637,7 +637,7 @@ static int acpi_processor_remove(struct acpi_device *device, int type) if (!device || !acpi_driver_data(device)) return -EINVAL; - pr = (struct acpi_processor *)acpi_driver_data(device); + pr = acpi_driver_data(device); if (pr->id >= NR_CPUS) { kfree(pr); diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 0a395fc..4504684 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -671,7 +671,7 @@ static int acpi_processor_get_power_info_cst(struct acpi_processor *pr) return -ENODEV; } - cst = (union acpi_object *)buffer.pointer; + cst = buffer.pointer; /* There must be at least 2 elements */ if (!cst || (cst->type != ACPI_TYPE_PACKAGE) || cst->package.count < 2) { @@ -700,14 +700,14 @@ static int acpi_processor_get_power_info_cst(struct acpi_processor *pr) memset(&cx, 0, sizeof(cx)); - element = (union acpi_object *)&(cst->package.elements[i]); + element = &(cst->package.elements[i]); if (element->type != ACPI_TYPE_PACKAGE) continue; if (element->package.count != 4) continue; - obj = (union acpi_object *)&(element->package.elements[0]); + obj = &(element->package.elements[0]); if (obj->type != ACPI_TYPE_BUFFER) continue; @@ -722,7 +722,7 @@ static int acpi_processor_get_power_info_cst(struct acpi_processor *pr) 0 : reg->address; /* There should be an easy way to extract an integer... */ - obj = (union acpi_object *)&(element->package.elements[1]); + obj = &(element->package.elements[1]); if (obj->type != ACPI_TYPE_INTEGER) continue; @@ -735,13 +735,13 @@ static int acpi_processor_get_power_info_cst(struct acpi_processor *pr) if ((cx.type < ACPI_STATE_C2) || (cx.type > ACPI_STATE_C3)) continue; - obj = (union acpi_object *)&(element->package.elements[2]); + obj = &(element->package.elements[2]); if (obj->type != ACPI_TYPE_INTEGER) continue; cx.latency = obj->integer.value; - obj = (union acpi_object *)&(element->package.elements[3]); + obj = &(element->package.elements[3]); if (obj->type != ACPI_TYPE_INTEGER) continue; @@ -1004,7 +1004,7 @@ int acpi_processor_cst_has_changed(struct acpi_processor *pr) static int acpi_processor_power_seq_show(struct seq_file *seq, void *offset) { - struct acpi_processor *pr = (struct acpi_processor *)seq->private; + struct acpi_processor *pr = seq->private; unsigned int i; diff --git a/drivers/acpi/processor_perflib.c b/drivers/acpi/processor_perflib.c index 7ba5e49..39fdbcc 100644 --- a/drivers/acpi/processor_perflib.c +++ b/drivers/acpi/processor_perflib.c @@ -238,7 +238,7 @@ static int acpi_processor_get_performance_states(struct acpi_processor *pr) return -ENODEV; } - pss = (union acpi_object *)buffer.pointer; + pss = buffer.pointer; if (!pss || (pss->type != ACPI_TYPE_PACKAGE)) { printk(KERN_ERR PREFIX "Invalid _PSS data\n"); result = -EFAULT; @@ -412,7 +412,7 @@ static struct file_operations acpi_processor_perf_fops = { static int acpi_processor_perf_seq_show(struct seq_file *seq, void *offset) { - struct acpi_processor *pr = (struct acpi_processor *)seq->private; + struct acpi_processor *pr = seq->private; int i; @@ -453,8 +453,8 @@ acpi_processor_write_performance(struct file *file, size_t count, loff_t * data) { int result = 0; - struct seq_file *m = (struct seq_file *)file->private_data; - struct acpi_processor *pr = (struct acpi_processor *)m->private; + struct seq_file *m = file->private_data; + struct acpi_processor *pr = m->private; struct acpi_processor_performance *perf; char state_string[12] = { '\0' }; unsigned int new_state = 0; @@ -553,7 +553,7 @@ static int acpi_processor_get_psd(struct acpi_processor *pr) return -ENODEV; } - psd = (union acpi_object *) buffer.pointer; + psd = buffer.pointer; if (!psd || (psd->type != ACPI_TYPE_PACKAGE)) { ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _PSD data\n")); result = -EFAULT; diff --git a/drivers/acpi/processor_thermal.c b/drivers/acpi/processor_thermal.c index ef5e0f6..40fecd6 100644 --- a/drivers/acpi/processor_thermal.c +++ b/drivers/acpi/processor_thermal.c @@ -208,7 +208,7 @@ int acpi_processor_set_thermal_limit(acpi_handle handle, int type) if (result) return result; - pr = (struct acpi_processor *)acpi_driver_data(device); + pr = acpi_driver_data(device); if (!pr) return -ENODEV; @@ -348,8 +348,8 @@ static ssize_t acpi_processor_write_limit(struct file * file, size_t count, loff_t * data) { int result = 0; - struct seq_file *m = (struct seq_file *)file->private_data; - struct acpi_processor *pr = (struct acpi_processor *)m->private; + struct seq_file *m = file->private_data; + struct acpi_processor *pr = m->private; char limit_string[25] = { '\0' }; int px = 0; int tx = 0; diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c index d044ec5..0ec7dcd 100644 --- a/drivers/acpi/processor_throttling.c +++ b/drivers/acpi/processor_throttling.c @@ -259,7 +259,7 @@ int acpi_processor_get_throttling_info(struct acpi_processor *pr) static int acpi_processor_throttling_seq_show(struct seq_file *seq, void *offset) { - struct acpi_processor *pr = (struct acpi_processor *)seq->private; + struct acpi_processor *pr = seq->private; int i = 0; int result = 0; @@ -307,8 +307,8 @@ static ssize_t acpi_processor_write_throttling(struct file * file, size_t count, loff_t * data) { int result = 0; - struct seq_file *m = (struct seq_file *)file->private_data; - struct acpi_processor *pr = (struct acpi_processor *)m->private; + struct seq_file *m = file->private_data; + struct acpi_processor *pr = m->private; char state_string[12] = { '\0' }; diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c index 62bef0b..c6a819a 100644 --- a/drivers/acpi/sbs.c +++ b/drivers/acpi/sbs.c @@ -923,7 +923,7 @@ static struct proc_dir_entry *acpi_battery_dir = NULL; static int acpi_battery_read_info(struct seq_file *seq, void *offset) { - struct acpi_battery *battery = (struct acpi_battery *)seq->private; + struct acpi_battery *battery = seq->private; int cscale; int result = 0; @@ -1076,7 +1076,7 @@ static int acpi_battery_state_open_fs(struct inode *inode, struct file *file) static int acpi_battery_read_alarm(struct seq_file *seq, void *offset) { - struct acpi_battery *battery = (struct acpi_battery *)seq->private; + struct acpi_battery *battery = seq->private; int result = 0; int cscale; @@ -1125,8 +1125,8 @@ static ssize_t acpi_battery_write_alarm(struct file *file, const char __user * buffer, size_t count, loff_t * ppos) { - struct seq_file *seq = (struct seq_file *)file->private_data; - struct acpi_battery *battery = (struct acpi_battery *)seq->private; + struct seq_file *seq = file->private_data; + struct acpi_battery *battery = seq->private; char alarm_string[12] = { '\0' }; int result, old_alarm, new_alarm; @@ -1160,14 +1160,14 @@ acpi_battery_write_alarm(struct file *file, const char __user * buffer, if (result) { ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "acpi_battery_set_alarm() failed\n")); - (void)acpi_battery_set_alarm(battery, old_alarm); + acpi_battery_set_alarm(battery, old_alarm); goto end; } result = acpi_battery_get_alarm(battery); if (result) { ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "acpi_battery_get_alarm() failed\n")); - (void)acpi_battery_set_alarm(battery, old_alarm); + acpi_battery_set_alarm(battery, old_alarm); goto end; } @@ -1217,7 +1217,7 @@ static struct proc_dir_entry *acpi_ac_dir = NULL; static int acpi_ac_read_state(struct seq_file *seq, void *offset) { - struct acpi_sbs *sbs = (struct acpi_sbs *)seq->private; + struct acpi_sbs *sbs = seq->private; int result; if (sbs->zombie) { @@ -1302,7 +1302,7 @@ static int acpi_battery_add(struct acpi_sbs *sbs, int id) battery->init_state = 1; } - (void)sprintf(dir_name, ACPI_BATTERY_DIR_NAME, id); + sprintf(dir_name, ACPI_BATTERY_DIR_NAME, id); result = acpi_sbs_generic_add_fs(&battery->battery_entry, acpi_battery_dir, @@ -1485,7 +1485,7 @@ static int acpi_sbs_update_run(struct acpi_sbs *sbs, int data_type) } if (old_battery_present != new_battery_present) { - (void)sprintf(dir_name, ACPI_BATTERY_DIR_NAME, id); + sprintf(dir_name, ACPI_BATTERY_DIR_NAME, id); result = acpi_sbs_generate_event(sbs->device, ACPI_SBS_BATTERY_NOTIFY_STATUS, new_battery_present, @@ -1498,7 +1498,7 @@ static int acpi_sbs_update_run(struct acpi_sbs *sbs, int data_type) } } if (old_remaining_capacity != battery->state.remaining_capacity) { - (void)sprintf(dir_name, ACPI_BATTERY_DIR_NAME, id); + sprintf(dir_name, ACPI_BATTERY_DIR_NAME, id); result = acpi_sbs_generate_event(sbs->device, ACPI_SBS_BATTERY_NOTIFY_STATUS, new_battery_present, @@ -1659,7 +1659,7 @@ static int acpi_sbs_add(struct acpi_device *device) init_timer(&sbs->update_timer); if (update_mode == QUEUE_UPDATE_MODE) { status = acpi_os_execute(OSL_GPE_HANDLER, - acpi_sbs_update_queue, (void *)sbs); + acpi_sbs_update_queue, sbs); if (status != AE_OK) { ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "acpi_os_execute() failed\n")); @@ -1685,7 +1685,7 @@ static int acpi_sbs_add(struct acpi_device *device) int acpi_sbs_remove(struct acpi_device *device, int type) { - struct acpi_sbs *sbs = (struct acpi_sbs *)acpi_driver_data(device); + struct acpi_sbs *sbs = acpi_driver_data(device); int id; if (!device || !sbs) { diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c index bfb3bfc..ffa30c9 100644 --- a/drivers/acpi/tables.c +++ b/drivers/acpi/tables.c @@ -228,7 +228,7 @@ void acpi_table_print_madt_entry(acpi_table_entry_header * header) static int acpi_table_compute_checksum(void *table_pointer, unsigned long length) { - u8 *p = (u8 *) table_pointer; + u8 *p = table_pointer; unsigned long remains = length; unsigned long sum = 0; diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 5753d06..4d75085 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -663,7 +663,7 @@ static void acpi_thermal_run(unsigned long data) static void acpi_thermal_check(void *data) { int result = 0; - struct acpi_thermal *tz = (struct acpi_thermal *)data; + struct acpi_thermal *tz = data; unsigned long sleep_time = 0; int i = 0; struct acpi_thermal_state state; @@ -778,7 +778,7 @@ static struct proc_dir_entry *acpi_thermal_dir; static int acpi_thermal_state_seq_show(struct seq_file *seq, void *offset) { - struct acpi_thermal *tz = (struct acpi_thermal *)seq->private; + struct acpi_thermal *tz = seq->private; if (!tz) @@ -813,7 +813,7 @@ static int acpi_thermal_state_open_fs(struct inode *inode, struct file *file) static int acpi_thermal_temp_seq_show(struct seq_file *seq, void *offset) { int result = 0; - struct acpi_thermal *tz = (struct acpi_thermal *)seq->private; + struct acpi_thermal *tz = seq->private; if (!tz) @@ -837,7 +837,7 @@ static int acpi_thermal_temp_open_fs(struct inode *inode, struct file *file) static int acpi_thermal_trip_seq_show(struct seq_file *seq, void *offset) { - struct acpi_thermal *tz = (struct acpi_thermal *)seq->private; + struct acpi_thermal *tz = seq->private; int i = 0; int j = 0; @@ -893,8 +893,8 @@ acpi_thermal_write_trip_points(struct file *file, const char __user * buffer, size_t count, loff_t * ppos) { - struct seq_file *m = (struct seq_file *)file->private_data; - struct acpi_thermal *tz = (struct acpi_thermal *)m->private; + struct seq_file *m = file->private_data; + struct acpi_thermal *tz = m->private; char *limit_string; int num, critical, hot, passive; @@ -953,7 +953,7 @@ acpi_thermal_write_trip_points(struct file *file, static int acpi_thermal_cooling_seq_show(struct seq_file *seq, void *offset) { - struct acpi_thermal *tz = (struct acpi_thermal *)seq->private; + struct acpi_thermal *tz = seq->private; if (!tz) @@ -984,8 +984,8 @@ acpi_thermal_write_cooling_mode(struct file *file, const char __user * buffer, size_t count, loff_t * ppos) { - struct seq_file *m = (struct seq_file *)file->private_data; - struct acpi_thermal *tz = (struct acpi_thermal *)m->private; + struct seq_file *m = file->private_data; + struct acpi_thermal *tz = m->private; int result = 0; char mode_string[12] = { '\0' }; @@ -1014,7 +1014,7 @@ acpi_thermal_write_cooling_mode(struct file *file, static int acpi_thermal_polling_seq_show(struct seq_file *seq, void *offset) { - struct acpi_thermal *tz = (struct acpi_thermal *)seq->private; + struct acpi_thermal *tz = seq->private; if (!tz) @@ -1043,8 +1043,8 @@ acpi_thermal_write_polling(struct file *file, const char __user * buffer, size_t count, loff_t * ppos) { - struct seq_file *m = (struct seq_file *)file->private_data; - struct acpi_thermal *tz = (struct acpi_thermal *)m->private; + struct seq_file *m = file->private_data; + struct acpi_thermal *tz = m->private; int result = 0; char polling_string[12] = { '\0' }; int seconds = 0; @@ -1170,7 +1170,7 @@ static int acpi_thermal_remove_fs(struct acpi_device *device) static void acpi_thermal_notify(acpi_handle handle, u32 event, void *data) { - struct acpi_thermal *tz = (struct acpi_thermal *)data; + struct acpi_thermal *tz = data; struct acpi_device *device = NULL; @@ -1324,7 +1324,7 @@ static int acpi_thermal_remove(struct acpi_device *device, int type) if (!device || !acpi_driver_data(device)) return -EINVAL; - tz = (struct acpi_thermal *)acpi_driver_data(device); + tz = acpi_driver_data(device); /* avoid timer adding new defer task */ tz->zombie = 1; @@ -1364,7 +1364,7 @@ static int acpi_thermal_resume(struct acpi_device *device, int state) if (!device || !acpi_driver_data(device)) return -EINVAL; - tz = (struct acpi_thermal *)acpi_driver_data(device); + tz = acpi_driver_data(device); acpi_thermal_get_temperature(tz); diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c index d0d84c4..91fed70 100644 --- a/drivers/acpi/utils.c +++ b/drivers/acpi/utils.c @@ -83,7 +83,7 @@ acpi_extract_package(union acpi_object *package, return AE_BAD_DATA; } - format_string = (char *)format->pointer; + format_string = format->pointer; /* * Calculate size_required. @@ -361,7 +361,7 @@ acpi_evaluate_reference(acpi_handle handle, if (ACPI_FAILURE(status)) goto end; - package = (union acpi_object *)buffer.pointer; + package = buffer.pointer; if ((buffer.length == 0) || !package) { printk(KERN_ERR PREFIX "No return object (len %X ptr %p)\n", diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index 56666a9..53a9eb0 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -386,7 +386,7 @@ acpi_video_device_EDID(struct acpi_video_device *device, if (ACPI_FAILURE(status)) return -ENODEV; - obj = (union acpi_object *)buffer.pointer; + obj = buffer.pointer; if (obj && obj->type == ACPI_TYPE_BUFFER) *edid = obj; @@ -654,8 +654,7 @@ static struct proc_dir_entry *acpi_video_dir; static int acpi_video_device_info_seq_show(struct seq_file *seq, void *offset) { - struct acpi_video_device *dev = - (struct acpi_video_device *)seq->private; + struct acpi_video_device *dev = seq->private; if (!dev) @@ -688,8 +687,7 @@ acpi_video_device_info_open_fs(struct inode *inode, struct file *file) static int acpi_video_device_state_seq_show(struct seq_file *seq, void *offset) { int status; - struct acpi_video_device *dev = - (struct acpi_video_device *)seq->private; + struct acpi_video_device *dev = seq->private; unsigned long state; @@ -727,8 +725,8 @@ acpi_video_device_write_state(struct file *file, size_t count, loff_t * data) { int status; - struct seq_file *m = (struct seq_file *)file->private_data; - struct acpi_video_device *dev = (struct acpi_video_device *)m->private; + struct seq_file *m = file->private_data; + struct acpi_video_device *dev = m->private; char str[12] = { 0 }; u32 state = 0; @@ -754,8 +752,7 @@ acpi_video_device_write_state(struct file *file, static int acpi_video_device_brightness_seq_show(struct seq_file *seq, void *offset) { - struct acpi_video_device *dev = - (struct acpi_video_device *)seq->private; + struct acpi_video_device *dev = seq->private; int i; @@ -784,8 +781,8 @@ acpi_video_device_write_brightness(struct file *file, const char __user * buffer, size_t count, loff_t * data) { - struct seq_file *m = (struct seq_file *)file->private_data; - struct acpi_video_device *dev = (struct acpi_video_device *)m->private; + struct seq_file *m = file->private_data; + struct acpi_video_device *dev = m->private; char str[4] = { 0 }; unsigned int level = 0; int i; @@ -817,8 +814,7 @@ acpi_video_device_write_brightness(struct file *file, static int acpi_video_device_EDID_seq_show(struct seq_file *seq, void *offset) { - struct acpi_video_device *dev = - (struct acpi_video_device *)seq->private; + struct acpi_video_device *dev = seq->private; int status; int i; union acpi_object *edid = NULL; @@ -866,7 +862,7 @@ static int acpi_video_device_add_fs(struct acpi_device *device) if (!device) return -ENODEV; - vid_dev = (struct acpi_video_device *)acpi_driver_data(device); + vid_dev = acpi_driver_data(device); if (!vid_dev) return -ENODEV; @@ -931,7 +927,7 @@ static int acpi_video_device_remove_fs(struct acpi_device *device) { struct acpi_video_device *vid_dev; - vid_dev = (struct acpi_video_device *)acpi_driver_data(device); + vid_dev = acpi_driver_data(device); if (!vid_dev || !vid_dev->video || !vid_dev->video->dir) return -ENODEV; @@ -950,7 +946,7 @@ static int acpi_video_device_remove_fs(struct acpi_device *device) /* video bus */ static int acpi_video_bus_info_seq_show(struct seq_file *seq, void *offset) { - struct acpi_video_bus *video = (struct acpi_video_bus *)seq->private; + struct acpi_video_bus *video = seq->private; if (!video) @@ -975,7 +971,7 @@ static int acpi_video_bus_info_open_fs(struct inode *inode, struct file *file) static int acpi_video_bus_ROM_seq_show(struct seq_file *seq, void *offset) { - struct acpi_video_bus *video = (struct acpi_video_bus *)seq->private; + struct acpi_video_bus *video = seq->private; if (!video) @@ -995,7 +991,7 @@ static int acpi_video_bus_ROM_open_fs(struct inode *inode, struct file *file) static int acpi_video_bus_POST_info_seq_show(struct seq_file *seq, void *offset) { - struct acpi_video_bus *video = (struct acpi_video_bus *)seq->private; + struct acpi_video_bus *video = seq->private; unsigned long options; int status; @@ -1033,7 +1029,7 @@ acpi_video_bus_POST_info_open_fs(struct inode *inode, struct file *file) static int acpi_video_bus_POST_seq_show(struct seq_file *seq, void *offset) { - struct acpi_video_bus *video = (struct acpi_video_bus *)seq->private; + struct acpi_video_bus *video = seq->private; int status; unsigned long id; @@ -1054,7 +1050,7 @@ static int acpi_video_bus_POST_seq_show(struct seq_file *seq, void *offset) static int acpi_video_bus_DOS_seq_show(struct seq_file *seq, void *offset) { - struct acpi_video_bus *video = (struct acpi_video_bus *)seq->private; + struct acpi_video_bus *video = seq->private; seq_printf(seq, "DOS setting: <%d>\n", video->dos_setting); @@ -1079,8 +1075,8 @@ acpi_video_bus_write_POST(struct file *file, size_t count, loff_t * data) { int status; - struct seq_file *m = (struct seq_file *)file->private_data; - struct acpi_video_bus *video = (struct acpi_video_bus *)m->private; + struct seq_file *m = file->private_data; + struct acpi_video_bus *video = m->private; char str[12] = { 0 }; unsigned long opt, options; @@ -1119,8 +1115,8 @@ acpi_video_bus_write_DOS(struct file *file, size_t count, loff_t * data) { int status; - struct seq_file *m = (struct seq_file *)file->private_data; - struct acpi_video_bus *video = (struct acpi_video_bus *)m->private; + struct seq_file *m = file->private_data; + struct acpi_video_bus *video = m->private; char str[12] = { 0 }; unsigned long opt; @@ -1150,7 +1146,7 @@ static int acpi_video_bus_add_fs(struct acpi_device *device) struct acpi_video_bus *video; - video = (struct acpi_video_bus *)acpi_driver_data(device); + video = acpi_driver_data(device); if (!acpi_device_dir(device)) { acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), @@ -1226,7 +1222,7 @@ static int acpi_video_bus_remove_fs(struct acpi_device *device) struct acpi_video_bus *video; - video = (struct acpi_video_bus *)acpi_driver_data(device); + video = acpi_driver_data(device); if (acpi_device_dir(device)) { remove_proc_entry("info", acpi_device_dir(device)); @@ -1403,7 +1399,7 @@ static int acpi_video_device_enumerate(struct acpi_video_bus *video) return status; } - dod = (union acpi_object *)buffer.pointer; + dod = buffer.pointer; if (!dod || (dod->type != ACPI_TYPE_PACKAGE)) { ACPI_EXCEPTION((AE_INFO, status, "Invalid _DOD data")); status = -EFAULT; @@ -1426,7 +1422,7 @@ static int acpi_video_device_enumerate(struct acpi_video_bus *video) count = 0; for (i = 0; i < dod->package.count; i++) { - obj = (union acpi_object *)&dod->package.elements[i]; + obj = &dod->package.elements[i]; if (obj->type != ACPI_TYPE_INTEGER) { printk(KERN_ERR PREFIX "Invalid _DOD data\n"); @@ -1612,7 +1608,7 @@ static int acpi_video_bus_stop_devices(struct acpi_video_bus *video) static void acpi_video_bus_notify(acpi_handle handle, u32 event, void *data) { - struct acpi_video_bus *video = (struct acpi_video_bus *)data; + struct acpi_video_bus *video = data; struct acpi_device *device = NULL; printk("video bus notify\n"); @@ -1654,8 +1650,7 @@ static void acpi_video_bus_notify(acpi_handle handle, u32 event, void *data) static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data) { - struct acpi_video_device *video_device = - (struct acpi_video_device *)data; + struct acpi_video_device *video_device = data; struct acpi_device *device = NULL; @@ -1757,7 +1752,7 @@ static int acpi_video_bus_remove(struct acpi_device *device, int type) if (!device || !acpi_driver_data(device)) return -EINVAL; - video = (struct acpi_video_bus *)acpi_driver_data(device); + video = acpi_driver_data(device); acpi_video_bus_stop_devices(video); -- cgit v0.10.2 From eff0df65da81c75084d936e86854a3418347c27f Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Mon, 2 Oct 2006 19:26:47 -0400 Subject: [CPUFREQ] Documentation fix Fix reference to where the code actually is. Noted by Hero Wanders. Signed-off-by: Dominik Brodowski Signed-off-by: Dave Jones diff --git a/Documentation/cpu-freq/core.txt b/Documentation/cpu-freq/core.txt index 29b3f9f..ce0666e 100644 --- a/Documentation/cpu-freq/core.txt +++ b/Documentation/cpu-freq/core.txt @@ -24,7 +24,7 @@ Contents: 1. General Information ======================= -The CPUFreq core code is located in linux/kernel/cpufreq.c. This +The CPUFreq core code is located in drivers/cpufreq/cpufreq.c. This cpufreq code offers a standardized interface for the CPUFreq architecture drivers (those pieces of code that do actual frequency transitions), as well as to "notifiers". These are device -- cgit v0.10.2 From 519ce3ec76bf5c068e575800a9977659f7cccec4 Mon Sep 17 00:00:00 2001 From: Venkatesh Pallipadi Date: Tue, 3 Oct 2006 12:27:10 -0700 Subject: [CPUFREQ][1/8] acpi-cpufreq: software coordination and handle all CPUs in the group This patchset has refresh/rebase of a bunch of patches/bugfixes related to acpi-cpufreq that were sent earlier on this list. patch 1/8 Patch that fixes a bug in swcoordination code in acpi-cpufreq patch 2/8 through patch 7/8 Grand unification of ACPI based speedstep-centrino and acpi-cpufreq drivers. ACPI allows P-state transitions in multiple ways. Like using IO ports or using processor native method (MSR). Without this patch, IO port based P-state transitions are handled in acpi-cpufreq driver and MSR based transitions on Intel CPUs are handled in speedstep-centrino driver. Even though most of the code in these two drivers should be similar, except for final changing/checking of frequency (one driver does it using IO port and other does it through MSR), we have duplicated code in these two drivers. There are also issues around BIOSes supporting both MSR and IO port and which driver should be loaded first in standard installations. The patchset combines functionality of these two driver into acpi-cpufreq driver. ACPI based functionality in speedstep-centrino is marked deprecated and will be removed in future. speedstep-centrino will continue to work on systems that depend on older non-ACPI table based P-state chanes. * 2/8 - Patch that reorganizes the code in acpi-cpufreq, cleaning it up a little and making it easier to add MSR support later. * 3/8 - Pull in the MSR based transition support into acpi-cpufreq. * 4/8 - Mark speedstep-centrino deprecated. Change the order in Makefile to load acpi-cpufreq first and speedstep-centrino later, in cases where both are configured in. * 5/8 - lindent acpi-cpufreq.c * 6/8 - Minor change to eliminate the check of current frequency on notifications. We can use last set frequency instead. * 7/8 - Make cpufreq->get of acpi_cpufreq work correctly again. There will be a patch in future that removes ACPI based support in speedstep-centrino in coming months. patch 8/8 Add support for IA32_APERF and IA32_MPERF MSR and get the actual frequency from these MSRs and use it to determine the next frequency target in ondemand governor This patch: There is a bug in software coordination patch in acpi-cpufreq, due to which frequency will only be set on first CPU of any coordinated group. Bug identified by Denis, was not recognised earlier as there are no platforms yet that use software coordination with acpi-cpufreq driver. Signed-off-by: Denis Sadykov Signed-off-by: Venkatesh Pallipadi Signed-off-by: Dave Jones diff --git a/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c b/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c index 57c880b..e902d97 100644 --- a/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c +++ b/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c @@ -197,6 +197,7 @@ acpi_cpufreq_target ( unsigned int relation) { struct cpufreq_acpi_io *data = acpi_io_data[policy->cpu]; + struct cpufreq_acpi_io *cpudata; struct acpi_processor_performance *perf; struct cpufreq_freqs freqs; cpumask_t online_policy_cpus; @@ -260,7 +261,8 @@ acpi_cpufreq_target ( break; } - result = acpi_processor_set_performance (data, j, next_state); + cpudata = acpi_io_data[j]; + result = acpi_processor_set_performance(cpudata, j, next_state); if (result) { result = -EAGAIN; break; @@ -287,8 +289,11 @@ acpi_cpufreq_target ( if (!cpus_empty(covered_cpus)) { for_each_cpu_mask(j, covered_cpus) { - policy->cpu = j; - acpi_processor_set_performance (data, + cpus_clear(set_mask); + cpu_set(j, set_mask); + set_cpus_allowed(current, set_mask); + cpudata = acpi_io_data[j]; + acpi_processor_set_performance(cpudata, j, cur_state); } -- cgit v0.10.2 From fe27cb358835cfa525b5831ec8ddb9b9bfda3c73 Mon Sep 17 00:00:00 2001 From: Venkatesh Pallipadi Date: Tue, 3 Oct 2006 12:29:15 -0700 Subject: [CPUFREQ][2/8] acpi: reorganize code to make MSR support addition easier Some clean up and redsign of the driver. Mainly making it easier to add support for multiple sub-mechanisms of changing frequency. Currently this driver supports only ACPI SYSTEM_IO address space. With the changes below it is easier to add support for other address spaces like Intel Enhanced Speedstep which uses MSR (ACPI FIXED_FEATURE_HARDWARE) to do the transitions. Signed-off-by: Denis Sadykov Signed-off-by: Venkatesh Pallipadi Signed-off-by: Alexey Starikovskiy Signed-off-by: Dave Jones diff --git a/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c b/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c index e902d97..ebc9fe2 100644 --- a/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c +++ b/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c @@ -1,9 +1,10 @@ /* - * acpi-cpufreq.c - ACPI Processor P-States Driver ($Revision: 1.3 $) + * acpi-cpufreq.c - ACPI Processor P-States Driver ($Revision: 1.4 $) * * Copyright (C) 2001, 2002 Andy Grover * Copyright (C) 2001, 2002 Paul Diefenbaugh * Copyright (C) 2002 - 2004 Dominik Brodowski + * Copyright (C) 2006 Denis Sadykov * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -27,19 +28,22 @@ #include #include #include +#include +#include #include -#include -#include #include #include /* current */ #include -#include -#include -#include #include #include +#include +#include +#include +#include +#include + #define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "acpi-cpufreq", msg) MODULE_AUTHOR("Paul Diefenbaugh, Dominik Brodowski"); @@ -47,24 +51,35 @@ MODULE_DESCRIPTION("ACPI Processor P-States Driver"); MODULE_LICENSE("GPL"); -struct cpufreq_acpi_io { +struct acpi_cpufreq_data { struct acpi_processor_performance *acpi_data; struct cpufreq_frequency_table *freq_table; unsigned int resume; }; -static struct cpufreq_acpi_io *acpi_io_data[NR_CPUS]; +static struct acpi_cpufreq_data *drv_data[NR_CPUS]; static struct acpi_processor_performance *acpi_perf_data[NR_CPUS]; static struct cpufreq_driver acpi_cpufreq_driver; static unsigned int acpi_pstate_strict; -static int -acpi_processor_write_port( - u16 port, - u8 bit_width, - u32 value) +static unsigned extract_freq(u32 value, struct acpi_cpufreq_data *data) +{ + struct acpi_processor_performance *perf; + int i; + + perf = data->acpi_data; + + for (i = 0; i < perf->state_count; i++) { + if (value == perf->states[i].status) + return data->freq_table[i].frequency; + } + return 0; +} + + +static void wrport(u16 port, u8 bit_width, u32 value) { if (bit_width <= 8) { outb(value, port); @@ -72,17 +87,10 @@ acpi_processor_write_port( outw(value, port); } else if (bit_width <= 32) { outl(value, port); - } else { - return -ENODEV; } - return 0; } -static int -acpi_processor_read_port( - u16 port, - u8 bit_width, - u32 *ret) +static void rdport(u16 port, u8 bit_width, u32 *ret) { *ret = 0; if (bit_width <= 8) { @@ -91,139 +99,141 @@ acpi_processor_read_port( *ret = inw(port); } else if (bit_width <= 32) { *ret = inl(port); - } else { - return -ENODEV; } - return 0; } -static int -acpi_processor_set_performance ( - struct cpufreq_acpi_io *data, - unsigned int cpu, - int state) +struct io_addr { + u16 port; + u8 bit_width; +}; + +struct drv_cmd { + cpumask_t mask; + struct io_addr addr; + u32 val; +}; + +static void do_drv_read(struct drv_cmd *cmd) { - u16 port = 0; - u8 bit_width = 0; - int i = 0; - int ret = 0; - u32 value = 0; - int retval; - struct acpi_processor_performance *perf; + rdport(cmd->addr.port, cmd->addr.bit_width, &cmd->val); + return; +} - dprintk("acpi_processor_set_performance\n"); +static void do_drv_write(struct drv_cmd *cmd) +{ + wrport(cmd->addr.port, cmd->addr.bit_width, cmd->val); + return; +} - retval = 0; - perf = data->acpi_data; - if (state == perf->state) { - if (unlikely(data->resume)) { - dprintk("Called after resume, resetting to P%d\n", state); - data->resume = 0; - } else { - dprintk("Already at target state (P%d)\n", state); - return (retval); - } +static inline void drv_read(struct drv_cmd *cmd) +{ + cpumask_t saved_mask = current->cpus_allowed; + cmd->val = 0; + + set_cpus_allowed(current, cmd->mask); + do_drv_read(cmd); + set_cpus_allowed(current, saved_mask); + +} + +static void drv_write(struct drv_cmd *cmd) +{ + cpumask_t saved_mask = current->cpus_allowed; + unsigned int i; + + for_each_cpu_mask(i, cmd->mask) { + set_cpus_allowed(current, cpumask_of_cpu(i)); + do_drv_write(cmd); } - dprintk("Transitioning from P%d to P%d\n", perf->state, state); + set_cpus_allowed(current, saved_mask); + return; +} - /* - * First we write the target state's 'control' value to the - * control_register. - */ +static u32 get_cur_val(cpumask_t mask) +{ + struct acpi_processor_performance *perf; + struct drv_cmd cmd; - port = perf->control_register.address; - bit_width = perf->control_register.bit_width; - value = (u32) perf->states[state].control; + if (unlikely(cpus_empty(mask))) + return 0; - dprintk("Writing 0x%08x to port 0x%04x\n", value, port); + perf = drv_data[first_cpu(mask)]->acpi_data; + cmd.addr.port = perf->control_register.address; + cmd.addr.bit_width = perf->control_register.bit_width; + cmd.mask = mask; - ret = acpi_processor_write_port(port, bit_width, value); - if (ret) { - dprintk("Invalid port width 0x%04x\n", bit_width); - return (ret); - } + drv_read(&cmd); - /* - * Assume the write went through when acpi_pstate_strict is not used. - * As read status_register is an expensive operation and there - * are no specific error cases where an IO port write will fail. - */ - if (acpi_pstate_strict) { - /* Then we read the 'status_register' and compare the value - * with the target state's 'status' to make sure the - * transition was successful. - * Note that we'll poll for up to 1ms (100 cycles of 10us) - * before giving up. - */ - - port = perf->status_register.address; - bit_width = perf->status_register.bit_width; - - dprintk("Looking for 0x%08x from port 0x%04x\n", - (u32) perf->states[state].status, port); - - for (i = 0; i < 100; i++) { - ret = acpi_processor_read_port(port, bit_width, &value); - if (ret) { - dprintk("Invalid port width 0x%04x\n", bit_width); - return (ret); - } - if (value == (u32) perf->states[state].status) - break; - udelay(10); - } - } else { - value = (u32) perf->states[state].status; - } + dprintk("get_cur_val = %u\n", cmd.val); + + return cmd.val; +} - if (unlikely(value != (u32) perf->states[state].status)) { - printk(KERN_WARNING "acpi-cpufreq: Transition failed\n"); - retval = -ENODEV; - return (retval); +static unsigned int get_cur_freq_on_cpu(unsigned int cpu) +{ + struct acpi_cpufreq_data *data = drv_data[cpu]; + unsigned int freq; + + dprintk("get_cur_freq_on_cpu (%d)\n", cpu); + + if (unlikely(data == NULL || + data->acpi_data == NULL || + data->freq_table == NULL)) { + return 0; } - dprintk("Transition successful after %d microseconds\n", i * 10); + freq = extract_freq(get_cur_val(cpumask_of_cpu(cpu)), data); + dprintk("cur freq = %u\n", freq); - perf->state = state; - return (retval); + return freq; } +static unsigned int check_freqs(cpumask_t mask, unsigned int freq, + struct acpi_cpufreq_data *data) +{ + unsigned int cur_freq; + unsigned int i; -static int -acpi_cpufreq_target ( - struct cpufreq_policy *policy, - unsigned int target_freq, - unsigned int relation) + for (i = 0; i < 100; i++) { + cur_freq = extract_freq(get_cur_val(mask), data); + if (cur_freq == freq) + return 1; + udelay(10); + } + return 0; +} + +static int acpi_cpufreq_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) { - struct cpufreq_acpi_io *data = acpi_io_data[policy->cpu]; - struct cpufreq_acpi_io *cpudata; - struct acpi_processor_performance *perf; - struct cpufreq_freqs freqs; - cpumask_t online_policy_cpus; - cpumask_t saved_mask; - cpumask_t set_mask; - cpumask_t covered_cpus; - unsigned int cur_state = 0; - unsigned int next_state = 0; - unsigned int result = 0; - unsigned int j; - unsigned int tmp; - - dprintk("acpi_cpufreq_setpolicy\n"); + struct acpi_cpufreq_data *data = drv_data[policy->cpu]; + struct acpi_processor_performance *perf; + struct cpufreq_freqs freqs; + cpumask_t online_policy_cpus; + struct drv_cmd cmd; + unsigned int next_state = 0; + unsigned int next_perf_state = 0; + unsigned int i; + int result = 0; + + dprintk("acpi_cpufreq_target %d (%d)\n", target_freq, policy->cpu); + + if (unlikely(data == NULL || + data->acpi_data == NULL || + data->freq_table == NULL)) { + return -ENODEV; + } + perf = data->acpi_data; result = cpufreq_frequency_table_target(policy, - data->freq_table, - target_freq, - relation, - &next_state); + data->freq_table, + target_freq, + relation, + &next_state); if (unlikely(result)) - return (result); - - perf = data->acpi_data; - cur_state = perf->state; - freqs.old = data->freq_table[cur_state].frequency; - freqs.new = data->freq_table[next_state].frequency; + return -ENODEV; #ifdef CONFIG_HOTPLUG_CPU /* cpufreq holds the hotplug lock, so we are safe from here on */ @@ -232,85 +242,53 @@ acpi_cpufreq_target ( online_policy_cpus = policy->cpus; #endif - for_each_cpu_mask(j, online_policy_cpus) { - freqs.cpu = j; - cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + cmd.val = get_cur_val(online_policy_cpus); + freqs.old = extract_freq(cmd.val, data); + freqs.new = data->freq_table[next_state].frequency; + next_perf_state = data->freq_table[next_state].index; + if (freqs.new == freqs.old) { + if (unlikely(data->resume)) { + dprintk("Called after resume, resetting to P%d\n", next_perf_state); + data->resume = 0; + } else { + dprintk("Already at target state (P%d)\n", next_perf_state); + return 0; + } } - /* - * We need to call driver->target() on all or any CPU in - * policy->cpus, depending on policy->shared_type. - */ - saved_mask = current->cpus_allowed; - cpus_clear(covered_cpus); - for_each_cpu_mask(j, online_policy_cpus) { - /* - * Support for SMP systems. - * Make sure we are running on CPU that wants to change freq - */ - cpus_clear(set_mask); - if (policy->shared_type == CPUFREQ_SHARED_TYPE_ANY) - cpus_or(set_mask, set_mask, online_policy_cpus); - else - cpu_set(j, set_mask); - - set_cpus_allowed(current, set_mask); - if (unlikely(!cpu_isset(smp_processor_id(), set_mask))) { - dprintk("couldn't limit to CPUs in this domain\n"); - result = -EAGAIN; - break; - } + cmd.addr.port = perf->control_register.address; + cmd.addr.bit_width = perf->control_register.bit_width; + cmd.val = (u32) perf->states[next_perf_state].control; - cpudata = acpi_io_data[j]; - result = acpi_processor_set_performance(cpudata, j, next_state); - if (result) { - result = -EAGAIN; - break; - } + cpus_clear(cmd.mask); - if (policy->shared_type == CPUFREQ_SHARED_TYPE_ANY) - break; - - cpu_set(j, covered_cpus); - } + if (policy->shared_type != CPUFREQ_SHARED_TYPE_ANY) + cmd.mask = online_policy_cpus; + else + cpu_set(policy->cpu, cmd.mask); - for_each_cpu_mask(j, online_policy_cpus) { - freqs.cpu = j; - cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + for_each_cpu_mask(i, cmd.mask) { + freqs.cpu = i; + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); } - if (unlikely(result)) { - /* - * We have failed halfway through the frequency change. - * We have sent callbacks to online_policy_cpus and - * acpi_processor_set_performance() has been called on - * coverd_cpus. Best effort undo.. - */ - - if (!cpus_empty(covered_cpus)) { - for_each_cpu_mask(j, covered_cpus) { - cpus_clear(set_mask); - cpu_set(j, set_mask); - set_cpus_allowed(current, set_mask); - cpudata = acpi_io_data[j]; - acpi_processor_set_performance(cpudata, - j, - cur_state); - } - } + drv_write(&cmd); - tmp = freqs.new; - freqs.new = freqs.old; - freqs.old = tmp; - for_each_cpu_mask(j, online_policy_cpus) { - freqs.cpu = j; - cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); - cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + if (acpi_pstate_strict) { + if (!check_freqs(cmd.mask, freqs.new, data)) { + dprintk("acpi_cpufreq_target failed (%d)\n", + policy->cpu); + return -EAGAIN; } } - set_cpus_allowed(current, saved_mask); - return (result); + for_each_cpu_mask(i, cmd.mask) { + freqs.cpu = i; + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + } + perf->state = next_perf_state; + + return result; } @@ -318,21 +296,17 @@ static int acpi_cpufreq_verify ( struct cpufreq_policy *policy) { - unsigned int result = 0; - struct cpufreq_acpi_io *data = acpi_io_data[policy->cpu]; + struct acpi_cpufreq_data *data = drv_data[policy->cpu]; dprintk("acpi_cpufreq_verify\n"); - result = cpufreq_frequency_table_verify(policy, - data->freq_table); - - return (result); + return cpufreq_frequency_table_verify(policy, data->freq_table); } static unsigned long acpi_cpufreq_guess_freq ( - struct cpufreq_acpi_io *data, + struct acpi_cpufreq_data *data, unsigned int cpu) { struct acpi_processor_performance *perf = data->acpi_data; @@ -369,9 +343,10 @@ acpi_cpufreq_guess_freq ( * do _PDC and _PSD and find out the processor dependency for the * actual init that will happen later... */ -static int acpi_cpufreq_early_init_acpi(void) +static int acpi_cpufreq_early_init(void) { struct acpi_processor_performance *data; + cpumask_t covered; unsigned int i, j; dprintk("acpi_cpufreq_early_init\n"); @@ -380,17 +355,19 @@ static int acpi_cpufreq_early_init_acpi(void) data = kzalloc(sizeof(struct acpi_processor_performance), GFP_KERNEL); if (!data) { - for_each_possible_cpu(j) { + for_each_cpu_mask(j, covered) { kfree(acpi_perf_data[j]); acpi_perf_data[j] = NULL; } return (-ENOMEM); } acpi_perf_data[i] = data; + cpu_set(i, covered); } /* Do initialization in ACPI core */ - return acpi_processor_preregister_performance(acpi_perf_data); + acpi_processor_preregister_performance(acpi_perf_data); + return 0; } /* @@ -424,11 +401,12 @@ static int acpi_cpufreq_cpu_init ( struct cpufreq_policy *policy) { - unsigned int i; - unsigned int cpu = policy->cpu; - struct cpufreq_acpi_io *data; - unsigned int result = 0; - struct cpuinfo_x86 *c = &cpu_data[policy->cpu]; + unsigned int i; + unsigned int valid_states = 0; + unsigned int cpu = policy->cpu; + struct acpi_cpufreq_data *data; + unsigned int result = 0; + struct cpuinfo_x86 *c = &cpu_data[policy->cpu]; struct acpi_processor_performance *perf; dprintk("acpi_cpufreq_cpu_init\n"); @@ -436,15 +414,18 @@ acpi_cpufreq_cpu_init ( if (!acpi_perf_data[cpu]) return (-ENODEV); - data = kzalloc(sizeof(struct cpufreq_acpi_io), GFP_KERNEL); + data = kzalloc(sizeof(struct acpi_cpufreq_data), GFP_KERNEL); if (!data) return (-ENOMEM); data->acpi_data = acpi_perf_data[cpu]; - acpi_io_data[cpu] = data; + drv_data[cpu] = data; - result = acpi_processor_register_performance(data->acpi_data, cpu); + if (cpu_has(c, X86_FEATURE_CONSTANT_TSC)) { + acpi_cpufreq_driver.flags |= CPUFREQ_CONST_LOOPS; + } + result = acpi_processor_register_performance(data->acpi_data, cpu); if (result) goto err_free; @@ -467,10 +448,6 @@ acpi_cpufreq_cpu_init ( } #endif - if (cpu_has(c, X86_FEATURE_CONSTANT_TSC)) { - acpi_cpufreq_driver.flags |= CPUFREQ_CONST_LOOPS; - } - /* capability check */ if (perf->state_count <= 1) { dprintk("No P-States\n"); @@ -478,16 +455,22 @@ acpi_cpufreq_cpu_init ( goto err_unreg; } - if ((perf->control_register.space_id != ACPI_ADR_SPACE_SYSTEM_IO) || - (perf->status_register.space_id != ACPI_ADR_SPACE_SYSTEM_IO)) { - dprintk("Unsupported address space [%d, %d]\n", - (u32) (perf->control_register.space_id), - (u32) (perf->status_register.space_id)); + if (perf->control_register.space_id != perf->status_register.space_id) { + result = -ENODEV; + goto err_unreg; + } + + switch (perf->control_register.space_id) { + case ACPI_ADR_SPACE_SYSTEM_IO: + dprintk("SYSTEM IO addr space\n"); + break; + default: + dprintk("Unknown addr space %d\n", + (u32) (perf->control_register.space_id)); result = -ENODEV; goto err_unreg; } - /* alloc freq_table */ data->freq_table = kmalloc(sizeof(struct cpufreq_frequency_table) * (perf->state_count + 1), GFP_KERNEL); if (!data->freq_table) { result = -ENOMEM; @@ -506,14 +489,18 @@ acpi_cpufreq_cpu_init ( policy->cur = acpi_cpufreq_guess_freq(data, policy->cpu); /* table init */ - for (i=0; i<=perf->state_count; i++) + for (i=0; istate_count; i++) { - data->freq_table[i].index = i; - if (istate_count) - data->freq_table[i].frequency = perf->states[i].core_frequency * 1000; - else - data->freq_table[i].frequency = CPUFREQ_TABLE_END; + if ( i > 0 && perf->states[i].core_frequency == + perf->states[i - 1].core_frequency) + continue; + + data->freq_table[valid_states].index = i; + data->freq_table[valid_states].frequency = + perf->states[i].core_frequency * 1000; + valid_states++; } + data->freq_table[perf->state_count].frequency = CPUFREQ_TABLE_END; result = cpufreq_frequency_table_cpuinfo(policy, data->freq_table); if (result) { @@ -523,8 +510,7 @@ acpi_cpufreq_cpu_init ( /* notify BIOS that we exist */ acpi_processor_notify_smm(THIS_MODULE); - printk(KERN_INFO "acpi-cpufreq: CPU%u - ACPI performance management activated.\n", - cpu); + dprintk("CPU%u - ACPI performance management activated.\n", cpu); for (i = 0; i < perf->state_count; i++) dprintk(" %cP%d: %d MHz, %d mW, %d uS\n", (i == perf->state?'*':' '), i, @@ -540,7 +526,7 @@ acpi_cpufreq_cpu_init ( */ data->resume = 1; - return (result); + return result; err_freqfree: kfree(data->freq_table); @@ -548,7 +534,7 @@ acpi_cpufreq_cpu_init ( acpi_processor_unregister_performance(perf, cpu); err_free: kfree(data); - acpi_io_data[cpu] = NULL; + drv_data[cpu] = NULL; return (result); } @@ -558,14 +544,14 @@ static int acpi_cpufreq_cpu_exit ( struct cpufreq_policy *policy) { - struct cpufreq_acpi_io *data = acpi_io_data[policy->cpu]; + struct acpi_cpufreq_data *data = drv_data[policy->cpu]; dprintk("acpi_cpufreq_cpu_exit\n"); if (data) { cpufreq_frequency_table_put_attr(policy->cpu); - acpi_io_data[policy->cpu] = NULL; + drv_data[policy->cpu] = NULL; acpi_processor_unregister_performance(data->acpi_data, policy->cpu); kfree(data); } @@ -577,7 +563,7 @@ static int acpi_cpufreq_resume ( struct cpufreq_policy *policy) { - struct cpufreq_acpi_io *data = acpi_io_data[policy->cpu]; + struct acpi_cpufreq_data *data = drv_data[policy->cpu]; dprintk("acpi_cpufreq_resume\n"); @@ -596,6 +582,7 @@ static struct freq_attr* acpi_cpufreq_attr[] = { static struct cpufreq_driver acpi_cpufreq_driver = { .verify = acpi_cpufreq_verify, .target = acpi_cpufreq_target, + .get = get_cur_freq_on_cpu, .init = acpi_cpufreq_cpu_init, .exit = acpi_cpufreq_cpu_exit, .resume = acpi_cpufreq_resume, @@ -610,7 +597,7 @@ acpi_cpufreq_init (void) { dprintk("acpi_cpufreq_init\n"); - acpi_cpufreq_early_init_acpi(); + acpi_cpufreq_early_init(); return cpufreq_register_driver(&acpi_cpufreq_driver); } -- cgit v0.10.2 From dde9f7ba60adac0cade262ab9b17654e93c626e2 Mon Sep 17 00:00:00 2001 From: Venkatesh Pallipadi Date: Tue, 3 Oct 2006 12:33:14 -0700 Subject: [CPUFREQ][3/8] acpi-cpufreq: Pull in MSR based transition support Add in the support for Intel Enhanced Speedstep - MSR based transitions. With this change, the ACPI based support in speedstep-centrino can be deprecated and duplicate code in that driver can be marked for removal. Much easier to maintain and support this way. This also reduces the user misconfigurations and questions on which driver is to be used under which CPUs to support Enhanced Speedstep. Signed-off-by: Denis Sadykov Signed-off-by: Venkatesh Pallipadi Signed-off-by: Alexey Starikovskiy Signed-off-by: Dave Jones diff --git a/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c b/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c index ebc9fe2..07fd97b 100644 --- a/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c +++ b/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c @@ -39,6 +39,7 @@ #include #include +#include #include #include #include @@ -51,10 +52,19 @@ MODULE_DESCRIPTION("ACPI Processor P-States Driver"); MODULE_LICENSE("GPL"); +enum { + UNDEFINED_CAPABLE = 0, + SYSTEM_INTEL_MSR_CAPABLE, + SYSTEM_IO_CAPABLE, +}; + +#define INTEL_MSR_RANGE (0xffff) + struct acpi_cpufreq_data { struct acpi_processor_performance *acpi_data; struct cpufreq_frequency_table *freq_table; unsigned int resume; + unsigned int cpu_feature; }; static struct acpi_cpufreq_data *drv_data[NR_CPUS]; @@ -64,7 +74,20 @@ static struct cpufreq_driver acpi_cpufreq_driver; static unsigned int acpi_pstate_strict; -static unsigned extract_freq(u32 value, struct acpi_cpufreq_data *data) + +static int check_est_cpu(unsigned int cpuid) +{ + struct cpuinfo_x86 *cpu = &cpu_data[cpuid]; + + if (cpu->x86_vendor != X86_VENDOR_INTEL || + !cpu_has(cpu, X86_FEATURE_EST)) + return 0; + + return 1; +} + + +static unsigned extract_io(u32 value, struct acpi_cpufreq_data *data) { struct acpi_processor_performance *perf; int i; @@ -79,6 +102,31 @@ static unsigned extract_freq(u32 value, struct acpi_cpufreq_data *data) } +static unsigned extract_msr(u32 msr, struct acpi_cpufreq_data *data) +{ + int i; + + msr &= INTEL_MSR_RANGE; + for (i = 0; data->freq_table[i].frequency != CPUFREQ_TABLE_END; i++) { + if (msr == data->freq_table[i].index) + return data->freq_table[i].frequency; + } + return data->freq_table[0].frequency; +} + + +static unsigned extract_freq(u32 val, struct acpi_cpufreq_data *data) +{ + switch (data->cpu_feature) { + case SYSTEM_INTEL_MSR_CAPABLE: + return extract_msr(val, data); + case SYSTEM_IO_CAPABLE: + return extract_io(val, data); + default: + return 0; + } +} + static void wrport(u16 port, u8 bit_width, u32 value) { if (bit_width <= 8) { @@ -102,27 +150,57 @@ static void rdport(u16 port, u8 bit_width, u32 *ret) } } +struct msr_addr { + u32 reg; +}; + struct io_addr { u16 port; u8 bit_width; }; +typedef union { + struct msr_addr msr; + struct io_addr io; +} drv_addr_union; + struct drv_cmd { + unsigned int type; cpumask_t mask; - struct io_addr addr; + drv_addr_union addr; u32 val; }; static void do_drv_read(struct drv_cmd *cmd) { - rdport(cmd->addr.port, cmd->addr.bit_width, &cmd->val); - return; + u32 h; + + switch (cmd->type) { + case SYSTEM_INTEL_MSR_CAPABLE: + rdmsr(cmd->addr.msr.reg, cmd->val, h); + break; + case SYSTEM_IO_CAPABLE: + rdport(cmd->addr.io.port, cmd->addr.io.bit_width, &cmd->val); + break; + default: + break; + } } static void do_drv_write(struct drv_cmd *cmd) { - wrport(cmd->addr.port, cmd->addr.bit_width, cmd->val); - return; + u32 h = 0; + + switch (cmd->type) { + case SYSTEM_INTEL_MSR_CAPABLE: + wrmsr(cmd->addr.msr.reg, cmd->val, h); + break; + case SYSTEM_IO_CAPABLE: + wrport(cmd->addr.io.port, cmd->addr.io.bit_width, cmd->val); + break; + default: + break; + } } static inline void drv_read(struct drv_cmd *cmd) @@ -158,9 +236,21 @@ static u32 get_cur_val(cpumask_t mask) if (unlikely(cpus_empty(mask))) return 0; - perf = drv_data[first_cpu(mask)]->acpi_data; - cmd.addr.port = perf->control_register.address; - cmd.addr.bit_width = perf->control_register.bit_width; + switch (drv_data[first_cpu(mask)]->cpu_feature) { + case SYSTEM_INTEL_MSR_CAPABLE: + cmd.type = SYSTEM_INTEL_MSR_CAPABLE; + cmd.addr.msr.reg = MSR_IA32_PERF_STATUS; + break; + case SYSTEM_IO_CAPABLE: + cmd.type = SYSTEM_IO_CAPABLE; + perf = drv_data[first_cpu(mask)]->acpi_data; + cmd.addr.io.port = perf->control_register.address; + cmd.addr.io.bit_width = perf->control_register.bit_width; + break; + default: + return 0; + } + cmd.mask = mask; drv_read(&cmd); @@ -213,6 +303,7 @@ static int acpi_cpufreq_target(struct cpufreq_policy *policy, struct cpufreq_freqs freqs; cpumask_t online_policy_cpus; struct drv_cmd cmd; + unsigned int msr; unsigned int next_state = 0; unsigned int next_perf_state = 0; unsigned int i; @@ -256,9 +347,22 @@ static int acpi_cpufreq_target(struct cpufreq_policy *policy, } } - cmd.addr.port = perf->control_register.address; - cmd.addr.bit_width = perf->control_register.bit_width; - cmd.val = (u32) perf->states[next_perf_state].control; + switch (data->cpu_feature) { + case SYSTEM_INTEL_MSR_CAPABLE: + cmd.type = SYSTEM_INTEL_MSR_CAPABLE; + cmd.addr.msr.reg = MSR_IA32_PERF_CTL; + msr = (u32) perf->states[next_perf_state].control & INTEL_MSR_RANGE; + cmd.val = (cmd.val & ~INTEL_MSR_RANGE) | msr; + break; + case SYSTEM_IO_CAPABLE: + cmd.type = SYSTEM_IO_CAPABLE; + cmd.addr.io.port = perf->control_register.address; + cmd.addr.io.bit_width = perf->control_register.bit_width; + cmd.val = (u32) perf->states[next_perf_state].control; + break; + default: + return -ENODEV; + } cpus_clear(cmd.mask); @@ -405,6 +509,7 @@ acpi_cpufreq_cpu_init ( unsigned int valid_states = 0; unsigned int cpu = policy->cpu; struct acpi_cpufreq_data *data; + unsigned int l, h; unsigned int result = 0; struct cpuinfo_x86 *c = &cpu_data[policy->cpu]; struct acpi_processor_performance *perf; @@ -463,6 +568,15 @@ acpi_cpufreq_cpu_init ( switch (perf->control_register.space_id) { case ACPI_ADR_SPACE_SYSTEM_IO: dprintk("SYSTEM IO addr space\n"); + data->cpu_feature = SYSTEM_IO_CAPABLE; + break; + case ACPI_ADR_SPACE_FIXED_HARDWARE: + dprintk("HARDWARE addr space\n"); + if (!check_est_cpu(cpu)) { + result = -ENODEV; + goto err_unreg; + } + data->cpu_feature = SYSTEM_INTEL_MSR_CAPABLE; break; default: dprintk("Unknown addr space %d\n", @@ -485,9 +599,6 @@ acpi_cpufreq_cpu_init ( } policy->governor = CPUFREQ_DEFAULT_GOVERNOR; - /* The current speed is unknown and not detectable by ACPI... */ - policy->cur = acpi_cpufreq_guess_freq(data, policy->cpu); - /* table init */ for (i=0; istate_count; i++) { @@ -507,6 +618,18 @@ acpi_cpufreq_cpu_init ( goto err_freqfree; } + switch (data->cpu_feature) { + case ACPI_ADR_SPACE_SYSTEM_IO: + /* Current speed is unknown and not detectable by IO port */ + policy->cur = acpi_cpufreq_guess_freq(data, policy->cpu); + break; + case ACPI_ADR_SPACE_FIXED_HARDWARE: + get_cur_freq_on_cpu(cpu); + break; + default: + break; + } + /* notify BIOS that we exist */ acpi_processor_notify_smm(THIS_MODULE); @@ -599,7 +722,7 @@ acpi_cpufreq_init (void) acpi_cpufreq_early_init(); - return cpufreq_register_driver(&acpi_cpufreq_driver); + return cpufreq_register_driver(&acpi_cpufreq_driver); } -- cgit v0.10.2 From 83d0515bbb10c7a3e52eee697d1032e447291eda Mon Sep 17 00:00:00 2001 From: Venkatesh Pallipadi Date: Tue, 3 Oct 2006 12:34:28 -0700 Subject: [CPUFREQ][4/8] acpi-cpufreq: Mark speedstep-centrino ACPI as deprecated Mark ACPI hooks in speedstep-centrino as deprecated. Change the order in which speedstep-centrino and acpi-cpufreq (when both are in kernel) will be added. First driver to be tried is now acpi-cpufreq, followed by speedstep-centrino. Add a note in feature-removal-schedule to mark this deprecation. Signed-off-by: Denis Sadykov Signed-off-by: Venkatesh Pallipadi Signed-off-by: Alexey Starikovskiy Signed-off-by: Dave Jones diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 24f3c63..7ebca07 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -280,3 +280,25 @@ Why: Orphaned for ages. SMP bugs long unfixed. Few users left Who: Jeff Garzik --------------------------- + +What: ACPI hooks (X86_SPEEDSTEP_CENTRINO_ACPI) in speedstep-centrino driver +When: December 2006 +Why: Speedstep-centrino driver with ACPI hooks and acpi-cpufreq driver are + functionally very much similar. They talk to ACPI in same way. Only + difference between them is the way they do frequency transitions. + One uses MSRs and the other one uses IO ports. Functionaliy of + speedstep_centrino with ACPI hooks is now merged into acpi-cpufreq. + That means one common driver will support all Intel Enhanced Speedstep + capable CPUs. That means less confusion over name of + speedstep-centrino driver (with that driver supposed to be used on + non-centrino platforms). That means less duplication of code and + less maintenance effort and no possibility of these two drivers + going out of sync. + Current users of speedstep_centrino with ACPI hooks are requested to + switch over to acpi-cpufreq driver. speedstep-centrino will continue + to work using older non-ACPI static table based scheme even after this + date. + +Who: Venkatesh Pallipadi + +--------------------------- diff --git a/arch/i386/kernel/cpu/cpufreq/Kconfig b/arch/i386/kernel/cpu/cpufreq/Kconfig index ccc1edf..5299c5b 100644 --- a/arch/i386/kernel/cpu/cpufreq/Kconfig +++ b/arch/i386/kernel/cpu/cpufreq/Kconfig @@ -17,6 +17,7 @@ config X86_ACPI_CPUFREQ help This driver adds a CPUFreq driver which utilizes the ACPI Processor Performance States. + This driver also supports Intel Enhanced Speedstep. For details, take a look at . @@ -121,11 +122,14 @@ config X86_SPEEDSTEP_CENTRINO If in doubt, say N. config X86_SPEEDSTEP_CENTRINO_ACPI - bool "Use ACPI tables to decode valid frequency/voltage pairs" + bool "Use ACPI tables to decode valid frequency/voltage (deprecated)" depends on X86_SPEEDSTEP_CENTRINO && ACPI_PROCESSOR depends on !(X86_SPEEDSTEP_CENTRINO = y && ACPI_PROCESSOR = m) default y help + This is deprecated and this functionality is now merged into + acpi_cpufreq (X86_ACPI_CPUFREQ). Use that driver instead of + speedstep_centrino. Use primarily the information provided in the BIOS ACPI tables to determine valid CPU frequency and voltage pairings. It is required for the driver to work on non-Banias CPUs. diff --git a/arch/i386/kernel/cpu/cpufreq/Makefile b/arch/i386/kernel/cpu/cpufreq/Makefile index 2e894f1..8de3abe 100644 --- a/arch/i386/kernel/cpu/cpufreq/Makefile +++ b/arch/i386/kernel/cpu/cpufreq/Makefile @@ -7,9 +7,9 @@ obj-$(CONFIG_SC520_CPUFREQ) += sc520_freq.o obj-$(CONFIG_X86_LONGRUN) += longrun.o obj-$(CONFIG_X86_GX_SUSPMOD) += gx-suspmod.o obj-$(CONFIG_X86_SPEEDSTEP_ICH) += speedstep-ich.o -obj-$(CONFIG_X86_SPEEDSTEP_CENTRINO) += speedstep-centrino.o obj-$(CONFIG_X86_SPEEDSTEP_LIB) += speedstep-lib.o obj-$(CONFIG_X86_SPEEDSTEP_SMI) += speedstep-smi.o obj-$(CONFIG_X86_ACPI_CPUFREQ) += acpi-cpufreq.o +obj-$(CONFIG_X86_SPEEDSTEP_CENTRINO) += speedstep-centrino.o obj-$(CONFIG_X86_P4_CLOCKMOD) += p4-clockmod.o obj-$(CONFIG_X86_CPUFREQ_NFORCE2) += cpufreq-nforce2.o diff --git a/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c b/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c index e8993ba..70178bf 100644 --- a/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c +++ b/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c @@ -531,6 +531,9 @@ static int centrino_cpu_init_acpi(struct cpufreq_policy *policy) /* notify BIOS that we exist */ acpi_processor_notify_smm(THIS_MODULE); + printk("speedstep-centrino with X86_SPEEDSTEP_CENTRINO_ACPI" + "config is deprecated.\n " + "Use X86_ACPI_CPUFREQ (acpi-cpufreq instead.\n" ); return 0; diff --git a/arch/x86_64/kernel/cpufreq/Kconfig b/arch/x86_64/kernel/cpufreq/Kconfig index 81f1562..3abcfa3 100644 --- a/arch/x86_64/kernel/cpufreq/Kconfig +++ b/arch/x86_64/kernel/cpufreq/Kconfig @@ -27,10 +27,13 @@ config X86_POWERNOW_K8_ACPI default y config X86_SPEEDSTEP_CENTRINO - tristate "Intel Enhanced SpeedStep" + tristate "Intel Enhanced SpeedStep (deprecated)" select CPU_FREQ_TABLE depends on ACPI_PROCESSOR help + This is deprecated and this functionality is now merged into + acpi_cpufreq (X86_ACPI_CPUFREQ). Use that driver instead of + speedstep_centrino. This adds the CPUFreq driver for Enhanced SpeedStep enabled mobile CPUs. This means Intel Pentium M (Centrino) CPUs or 64bit enabled Intel Xeons. @@ -50,6 +53,7 @@ config X86_ACPI_CPUFREQ help This driver adds a CPUFreq driver which utilizes the ACPI Processor Performance States. + This driver also supports Intel Enhanced Speedstep. For details, take a look at . diff --git a/arch/x86_64/kernel/cpufreq/Makefile b/arch/x86_64/kernel/cpufreq/Makefile index d8b5938..753ce1d 100644 --- a/arch/x86_64/kernel/cpufreq/Makefile +++ b/arch/x86_64/kernel/cpufreq/Makefile @@ -5,8 +5,8 @@ SRCDIR := ../../../i386/kernel/cpu/cpufreq obj-$(CONFIG_X86_POWERNOW_K8) += powernow-k8.o -obj-$(CONFIG_X86_SPEEDSTEP_CENTRINO) += speedstep-centrino.o obj-$(CONFIG_X86_ACPI_CPUFREQ) += acpi-cpufreq.o +obj-$(CONFIG_X86_SPEEDSTEP_CENTRINO) += speedstep-centrino.o obj-$(CONFIG_X86_P4_CLOCKMOD) += p4-clockmod.o obj-$(CONFIG_X86_SPEEDSTEP_LIB) += speedstep-lib.o -- cgit v0.10.2 From 64be7eedb2fd0d41614739b265b22708aa81734c Mon Sep 17 00:00:00 2001 From: Venkatesh Pallipadi Date: Tue, 3 Oct 2006 12:35:23 -0700 Subject: [CPUFREQ][5/8] acpi-cpufreq: lindent acpi-cpufreq.c Lindent acpi-cpufreq. Additional changes replacing "return (..)" by "return ..". No functionality changes in this patch. Signed-off-by: Denis Sadykov Signed-off-by: Venkatesh Pallipadi Signed-off-by: Alexey Starikovskiy Signed-off-by: Dave Jones diff --git a/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c b/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c index 07fd97b..385f17a 100644 --- a/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c +++ b/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c @@ -51,7 +51,6 @@ MODULE_AUTHOR("Paul Diefenbaugh, Dominik Brodowski"); MODULE_DESCRIPTION("ACPI Processor P-States Driver"); MODULE_LICENSE("GPL"); - enum { UNDEFINED_CAPABLE = 0, SYSTEM_INTEL_MSR_CAPABLE, @@ -61,36 +60,34 @@ enum { #define INTEL_MSR_RANGE (0xffff) struct acpi_cpufreq_data { - struct acpi_processor_performance *acpi_data; - struct cpufreq_frequency_table *freq_table; - unsigned int resume; - unsigned int cpu_feature; + struct acpi_processor_performance *acpi_data; + struct cpufreq_frequency_table *freq_table; + unsigned int resume; + unsigned int cpu_feature; }; -static struct acpi_cpufreq_data *drv_data[NR_CPUS]; -static struct acpi_processor_performance *acpi_perf_data[NR_CPUS]; +static struct acpi_cpufreq_data *drv_data[NR_CPUS]; +static struct acpi_processor_performance *acpi_perf_data[NR_CPUS]; static struct cpufreq_driver acpi_cpufreq_driver; static unsigned int acpi_pstate_strict; - static int check_est_cpu(unsigned int cpuid) { struct cpuinfo_x86 *cpu = &cpu_data[cpuid]; if (cpu->x86_vendor != X86_VENDOR_INTEL || - !cpu_has(cpu, X86_FEATURE_EST)) + !cpu_has(cpu, X86_FEATURE_EST)) return 0; return 1; } - static unsigned extract_io(u32 value, struct acpi_cpufreq_data *data) { - struct acpi_processor_performance *perf; - int i; + struct acpi_processor_performance *perf; + int i; perf = data->acpi_data; @@ -101,7 +98,6 @@ static unsigned extract_io(u32 value, struct acpi_cpufreq_data *data) return 0; } - static unsigned extract_msr(u32 msr, struct acpi_cpufreq_data *data) { int i; @@ -114,15 +110,14 @@ static unsigned extract_msr(u32 msr, struct acpi_cpufreq_data *data) return data->freq_table[0].frequency; } - static unsigned extract_freq(u32 val, struct acpi_cpufreq_data *data) { switch (data->cpu_feature) { - case SYSTEM_INTEL_MSR_CAPABLE: + case SYSTEM_INTEL_MSR_CAPABLE: return extract_msr(val, data); - case SYSTEM_IO_CAPABLE: + case SYSTEM_IO_CAPABLE: return extract_io(val, data); - default: + default: return 0; } } @@ -138,7 +133,7 @@ static void wrport(u16 port, u8 bit_width, u32 value) } } -static void rdport(u16 port, u8 bit_width, u32 *ret) +static void rdport(u16 port, u8 bit_width, u32 * ret) { *ret = 0; if (bit_width <= 8) { @@ -176,13 +171,13 @@ static void do_drv_read(struct drv_cmd *cmd) u32 h; switch (cmd->type) { - case SYSTEM_INTEL_MSR_CAPABLE: + case SYSTEM_INTEL_MSR_CAPABLE: rdmsr(cmd->addr.msr.reg, cmd->val, h); break; - case SYSTEM_IO_CAPABLE: + case SYSTEM_IO_CAPABLE: rdport(cmd->addr.io.port, cmd->addr.io.bit_width, &cmd->val); break; - default: + default: break; } } @@ -192,20 +187,20 @@ static void do_drv_write(struct drv_cmd *cmd) u32 h = 0; switch (cmd->type) { - case SYSTEM_INTEL_MSR_CAPABLE: + case SYSTEM_INTEL_MSR_CAPABLE: wrmsr(cmd->addr.msr.reg, cmd->val, h); break; - case SYSTEM_IO_CAPABLE: + case SYSTEM_IO_CAPABLE: wrport(cmd->addr.io.port, cmd->addr.io.bit_width, cmd->val); break; - default: + default: break; } } static inline void drv_read(struct drv_cmd *cmd) { - cpumask_t saved_mask = current->cpus_allowed; + cpumask_t saved_mask = current->cpus_allowed; cmd->val = 0; set_cpus_allowed(current, cmd->mask); @@ -216,8 +211,8 @@ static inline void drv_read(struct drv_cmd *cmd) static void drv_write(struct drv_cmd *cmd) { - cpumask_t saved_mask = current->cpus_allowed; - unsigned int i; + cpumask_t saved_mask = current->cpus_allowed; + unsigned int i; for_each_cpu_mask(i, cmd->mask) { set_cpus_allowed(current, cpumask_of_cpu(i)); @@ -230,8 +225,8 @@ static void drv_write(struct drv_cmd *cmd) static u32 get_cur_val(cpumask_t mask) { - struct acpi_processor_performance *perf; - struct drv_cmd cmd; + struct acpi_processor_performance *perf; + struct drv_cmd cmd; if (unlikely(cpus_empty(mask))) return 0; @@ -262,14 +257,13 @@ static u32 get_cur_val(cpumask_t mask) static unsigned int get_cur_freq_on_cpu(unsigned int cpu) { - struct acpi_cpufreq_data *data = drv_data[cpu]; - unsigned int freq; + struct acpi_cpufreq_data *data = drv_data[cpu]; + unsigned int freq; dprintk("get_cur_freq_on_cpu (%d)\n", cpu); if (unlikely(data == NULL || - data->acpi_data == NULL || - data->freq_table == NULL)) { + data->acpi_data == NULL || data->freq_table == NULL)) { return 0; } @@ -280,10 +274,10 @@ static unsigned int get_cur_freq_on_cpu(unsigned int cpu) } static unsigned int check_freqs(cpumask_t mask, unsigned int freq, - struct acpi_cpufreq_data *data) + struct acpi_cpufreq_data *data) { - unsigned int cur_freq; - unsigned int i; + unsigned int cur_freq; + unsigned int i; for (i = 0; i < 100; i++) { cur_freq = extract_freq(get_cur_val(mask), data); @@ -295,34 +289,31 @@ static unsigned int check_freqs(cpumask_t mask, unsigned int freq, } static int acpi_cpufreq_target(struct cpufreq_policy *policy, - unsigned int target_freq, - unsigned int relation) + unsigned int target_freq, unsigned int relation) { - struct acpi_cpufreq_data *data = drv_data[policy->cpu]; - struct acpi_processor_performance *perf; - struct cpufreq_freqs freqs; - cpumask_t online_policy_cpus; - struct drv_cmd cmd; - unsigned int msr; - unsigned int next_state = 0; - unsigned int next_perf_state = 0; - unsigned int i; - int result = 0; + struct acpi_cpufreq_data *data = drv_data[policy->cpu]; + struct acpi_processor_performance *perf; + struct cpufreq_freqs freqs; + cpumask_t online_policy_cpus; + struct drv_cmd cmd; + unsigned int msr; + unsigned int next_state = 0; + unsigned int next_perf_state = 0; + unsigned int i; + int result = 0; dprintk("acpi_cpufreq_target %d (%d)\n", target_freq, policy->cpu); if (unlikely(data == NULL || - data->acpi_data == NULL || - data->freq_table == NULL)) { + data->acpi_data == NULL || data->freq_table == NULL)) { return -ENODEV; } perf = data->acpi_data; result = cpufreq_frequency_table_target(policy, - data->freq_table, - target_freq, - relation, - &next_state); + data->freq_table, + target_freq, + relation, &next_state); if (unlikely(result)) return -ENODEV; @@ -339,30 +330,34 @@ static int acpi_cpufreq_target(struct cpufreq_policy *policy, next_perf_state = data->freq_table[next_state].index; if (freqs.new == freqs.old) { if (unlikely(data->resume)) { - dprintk("Called after resume, resetting to P%d\n", next_perf_state); + dprintk("Called after resume, resetting to P%d\n", + next_perf_state); data->resume = 0; } else { - dprintk("Already at target state (P%d)\n", next_perf_state); + dprintk("Already at target state (P%d)\n", + next_perf_state); return 0; } } - switch (data->cpu_feature) { - case SYSTEM_INTEL_MSR_CAPABLE: - cmd.type = SYSTEM_INTEL_MSR_CAPABLE; - cmd.addr.msr.reg = MSR_IA32_PERF_CTL; - msr = (u32) perf->states[next_perf_state].control & INTEL_MSR_RANGE; - cmd.val = (cmd.val & ~INTEL_MSR_RANGE) | msr; - break; - case SYSTEM_IO_CAPABLE: - cmd.type = SYSTEM_IO_CAPABLE; - cmd.addr.io.port = perf->control_register.address; - cmd.addr.io.bit_width = perf->control_register.bit_width; - cmd.val = (u32) perf->states[next_perf_state].control; - break; - default: - return -ENODEV; - } + switch (data->cpu_feature) { + case SYSTEM_INTEL_MSR_CAPABLE: + cmd.type = SYSTEM_INTEL_MSR_CAPABLE; + cmd.addr.msr.reg = MSR_IA32_PERF_CTL; + msr = + (u32) perf->states[next_perf_state]. + control & INTEL_MSR_RANGE; + cmd.val = (cmd.val & ~INTEL_MSR_RANGE) | msr; + break; + case SYSTEM_IO_CAPABLE: + cmd.type = SYSTEM_IO_CAPABLE; + cmd.addr.io.port = perf->control_register.address; + cmd.addr.io.bit_width = perf->control_register.bit_width; + cmd.val = (u32) perf->states[next_perf_state].control; + break; + default: + return -ENODEV; + } cpus_clear(cmd.mask); @@ -381,7 +376,7 @@ static int acpi_cpufreq_target(struct cpufreq_policy *policy, if (acpi_pstate_strict) { if (!check_freqs(cmd.mask, freqs.new, data)) { dprintk("acpi_cpufreq_target failed (%d)\n", - policy->cpu); + policy->cpu); return -EAGAIN; } } @@ -395,10 +390,7 @@ static int acpi_cpufreq_target(struct cpufreq_policy *policy, return result; } - -static int -acpi_cpufreq_verify ( - struct cpufreq_policy *policy) +static int acpi_cpufreq_verify(struct cpufreq_policy *policy) { struct acpi_cpufreq_data *data = drv_data[policy->cpu]; @@ -407,13 +399,10 @@ acpi_cpufreq_verify ( return cpufreq_frequency_table_verify(policy, data->freq_table); } - static unsigned long -acpi_cpufreq_guess_freq ( - struct acpi_cpufreq_data *data, - unsigned int cpu) +acpi_cpufreq_guess_freq(struct acpi_cpufreq_data *data, unsigned int cpu) { - struct acpi_processor_performance *perf = data->acpi_data; + struct acpi_processor_performance *perf = data->acpi_data; if (cpu_khz) { /* search the closest match to cpu_khz */ @@ -423,14 +412,14 @@ acpi_cpufreq_guess_freq ( for (i = 0; i < (perf->state_count - 1); i++) { freq = freqn; - freqn = perf->states[i+1].core_frequency * 1000; + freqn = perf->states[i + 1].core_frequency * 1000; if ((2 * cpu_khz) > (freqn + freq)) { perf->state = i; - return (freq); + return freq; } } perf->state = perf->state_count - 1; - return (freqn); + return freqn; } else { /* assume CPU is at P0... */ perf->state = 0; @@ -438,7 +427,6 @@ acpi_cpufreq_guess_freq ( } } - /* * acpi_cpufreq_early_init - initialize ACPI P-States library * @@ -449,21 +437,21 @@ acpi_cpufreq_guess_freq ( */ static int acpi_cpufreq_early_init(void) { - struct acpi_processor_performance *data; - cpumask_t covered; - unsigned int i, j; + struct acpi_processor_performance *data; + cpumask_t covered; + unsigned int i, j; dprintk("acpi_cpufreq_early_init\n"); for_each_possible_cpu(i) { - data = kzalloc(sizeof(struct acpi_processor_performance), - GFP_KERNEL); + data = kzalloc(sizeof(struct acpi_processor_performance), + GFP_KERNEL); if (!data) { for_each_cpu_mask(j, covered) { kfree(acpi_perf_data[j]); acpi_perf_data[j] = NULL; } - return (-ENOMEM); + return -ENOMEM; } acpi_perf_data[i] = data; cpu_set(i, covered); @@ -501,27 +489,25 @@ static struct dmi_system_id sw_any_bug_dmi_table[] = { { } }; -static int -acpi_cpufreq_cpu_init ( - struct cpufreq_policy *policy) +static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) { - unsigned int i; - unsigned int valid_states = 0; - unsigned int cpu = policy->cpu; - struct acpi_cpufreq_data *data; - unsigned int l, h; - unsigned int result = 0; - struct cpuinfo_x86 *c = &cpu_data[policy->cpu]; - struct acpi_processor_performance *perf; + unsigned int i; + unsigned int valid_states = 0; + unsigned int cpu = policy->cpu; + struct acpi_cpufreq_data *data; + unsigned int l, h; + unsigned int result = 0; + struct cpuinfo_x86 *c = &cpu_data[policy->cpu]; + struct acpi_processor_performance *perf; dprintk("acpi_cpufreq_cpu_init\n"); if (!acpi_perf_data[cpu]) - return (-ENODEV); + return -ENODEV; data = kzalloc(sizeof(struct acpi_cpufreq_data), GFP_KERNEL); if (!data) - return (-ENOMEM); + return -ENOMEM; data->acpi_data = acpi_perf_data[cpu]; drv_data[cpu] = data; @@ -566,11 +552,11 @@ acpi_cpufreq_cpu_init ( } switch (perf->control_register.space_id) { - case ACPI_ADR_SPACE_SYSTEM_IO: + case ACPI_ADR_SPACE_SYSTEM_IO: dprintk("SYSTEM IO addr space\n"); data->cpu_feature = SYSTEM_IO_CAPABLE; break; - case ACPI_ADR_SPACE_FIXED_HARDWARE: + case ACPI_ADR_SPACE_FIXED_HARDWARE: dprintk("HARDWARE addr space\n"); if (!check_est_cpu(cpu)) { result = -ENODEV; @@ -578,14 +564,16 @@ acpi_cpufreq_cpu_init ( } data->cpu_feature = SYSTEM_INTEL_MSR_CAPABLE; break; - default: + default: dprintk("Unknown addr space %d\n", - (u32) (perf->control_register.space_id)); + (u32) (perf->control_register.space_id)); result = -ENODEV; goto err_unreg; } - data->freq_table = kmalloc(sizeof(struct cpufreq_frequency_table) * (perf->state_count + 1), GFP_KERNEL); + data->freq_table = + kmalloc(sizeof(struct cpufreq_frequency_table) * + (perf->state_count + 1), GFP_KERNEL); if (!data->freq_table) { result = -ENOMEM; goto err_unreg; @@ -593,22 +581,23 @@ acpi_cpufreq_cpu_init ( /* detect transition latency */ policy->cpuinfo.transition_latency = 0; - for (i=0; istate_count; i++) { - if ((perf->states[i].transition_latency * 1000) > policy->cpuinfo.transition_latency) - policy->cpuinfo.transition_latency = perf->states[i].transition_latency * 1000; + for (i = 0; i < perf->state_count; i++) { + if ((perf->states[i].transition_latency * 1000) > + policy->cpuinfo.transition_latency) + policy->cpuinfo.transition_latency = + perf->states[i].transition_latency * 1000; } policy->governor = CPUFREQ_DEFAULT_GOVERNOR; /* table init */ - for (i=0; istate_count; i++) - { - if ( i > 0 && perf->states[i].core_frequency == - perf->states[i - 1].core_frequency) + for (i = 0; i < perf->state_count; i++) { + if (i > 0 && perf->states[i].core_frequency == + perf->states[i - 1].core_frequency) continue; data->freq_table[valid_states].index = i; data->freq_table[valid_states].frequency = - perf->states[i].core_frequency * 1000; + perf->states[i].core_frequency * 1000; valid_states++; } data->freq_table[perf->state_count].frequency = CPUFREQ_TABLE_END; @@ -619,14 +608,14 @@ acpi_cpufreq_cpu_init ( } switch (data->cpu_feature) { - case ACPI_ADR_SPACE_SYSTEM_IO: + case ACPI_ADR_SPACE_SYSTEM_IO: /* Current speed is unknown and not detectable by IO port */ policy->cur = acpi_cpufreq_guess_freq(data, policy->cpu); break; - case ACPI_ADR_SPACE_FIXED_HARDWARE: + case ACPI_ADR_SPACE_FIXED_HARDWARE: get_cur_freq_on_cpu(cpu); break; - default: + default: break; } @@ -636,100 +625,89 @@ acpi_cpufreq_cpu_init ( dprintk("CPU%u - ACPI performance management activated.\n", cpu); for (i = 0; i < perf->state_count; i++) dprintk(" %cP%d: %d MHz, %d mW, %d uS\n", - (i == perf->state?'*':' '), i, + (i == perf->state ? '*' : ' '), i, (u32) perf->states[i].core_frequency, (u32) perf->states[i].power, (u32) perf->states[i].transition_latency); cpufreq_frequency_table_get_attr(data->freq_table, policy->cpu); - + /* * the first call to ->target() should result in us actually * writing something to the appropriate registers. */ data->resume = 1; - + return result; - err_freqfree: + err_freqfree: kfree(data->freq_table); - err_unreg: + err_unreg: acpi_processor_unregister_performance(perf, cpu); - err_free: + err_free: kfree(data); drv_data[cpu] = NULL; - return (result); + return result; } - -static int -acpi_cpufreq_cpu_exit ( - struct cpufreq_policy *policy) +static int acpi_cpufreq_cpu_exit(struct cpufreq_policy *policy) { struct acpi_cpufreq_data *data = drv_data[policy->cpu]; - dprintk("acpi_cpufreq_cpu_exit\n"); if (data) { cpufreq_frequency_table_put_attr(policy->cpu); drv_data[policy->cpu] = NULL; - acpi_processor_unregister_performance(data->acpi_data, policy->cpu); + acpi_processor_unregister_performance(data->acpi_data, + policy->cpu); kfree(data); } - return (0); + return 0; } -static int -acpi_cpufreq_resume ( - struct cpufreq_policy *policy) +static int acpi_cpufreq_resume(struct cpufreq_policy *policy) { struct acpi_cpufreq_data *data = drv_data[policy->cpu]; - dprintk("acpi_cpufreq_resume\n"); data->resume = 1; - return (0); + return 0; } - -static struct freq_attr* acpi_cpufreq_attr[] = { +static struct freq_attr *acpi_cpufreq_attr[] = { &cpufreq_freq_attr_scaling_available_freqs, NULL, }; static struct cpufreq_driver acpi_cpufreq_driver = { - .verify = acpi_cpufreq_verify, - .target = acpi_cpufreq_target, - .get = get_cur_freq_on_cpu, - .init = acpi_cpufreq_cpu_init, - .exit = acpi_cpufreq_cpu_exit, - .resume = acpi_cpufreq_resume, - .name = "acpi-cpufreq", - .owner = THIS_MODULE, - .attr = acpi_cpufreq_attr, + .verify = acpi_cpufreq_verify, + .target = acpi_cpufreq_target, + .get = get_cur_freq_on_cpu, + .init = acpi_cpufreq_cpu_init, + .exit = acpi_cpufreq_cpu_exit, + .resume = acpi_cpufreq_resume, + .name = "acpi-cpufreq", + .owner = THIS_MODULE, + .attr = acpi_cpufreq_attr, }; - -static int __init -acpi_cpufreq_init (void) +static int __init acpi_cpufreq_init(void) { dprintk("acpi_cpufreq_init\n"); acpi_cpufreq_early_init(); - return cpufreq_register_driver(&acpi_cpufreq_driver); + return cpufreq_register_driver(&acpi_cpufreq_driver); } - -static void __exit -acpi_cpufreq_exit (void) +static void __exit acpi_cpufreq_exit(void) { - unsigned int i; + unsigned int i; dprintk("acpi_cpufreq_exit\n"); cpufreq_unregister_driver(&acpi_cpufreq_driver); @@ -742,7 +720,8 @@ acpi_cpufreq_exit (void) } module_param(acpi_pstate_strict, uint, 0644); -MODULE_PARM_DESC(acpi_pstate_strict, "value 0 or non-zero. non-zero -> strict ACPI checks are performed during frequency changes."); +MODULE_PARM_DESC(acpi_pstate_strict, + "value 0 or non-zero. non-zero -> strict ACPI checks are performed during frequency changes."); late_initcall(acpi_cpufreq_init); module_exit(acpi_cpufreq_exit); -- cgit v0.10.2 From 7650b281b091f39f5e97f13b45ab3813b1526b65 Mon Sep 17 00:00:00 2001 From: Venkatesh Pallipadi Date: Tue, 3 Oct 2006 12:36:30 -0700 Subject: [CPUFREQ][6/8] acpi-cpufreq: Eliminate get of current freq on notification Only change the frequency if the state previously set is different from what we are trying to set. We don't really have to get the current frequency at this point. Signed-off-by: Denis Sadykov Signed-off-by: Venkatesh Pallipadi Signed-off-by: Alexey Starikovskiy Signed-off-by: Dave Jones diff --git a/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c b/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c index 385f17a..e6513e9 100644 --- a/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c +++ b/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c @@ -324,11 +324,8 @@ static int acpi_cpufreq_target(struct cpufreq_policy *policy, online_policy_cpus = policy->cpus; #endif - cmd.val = get_cur_val(online_policy_cpus); - freqs.old = extract_freq(cmd.val, data); - freqs.new = data->freq_table[next_state].frequency; next_perf_state = data->freq_table[next_state].index; - if (freqs.new == freqs.old) { + if (perf->state == next_perf_state) { if (unlikely(data->resume)) { dprintk("Called after resume, resetting to P%d\n", next_perf_state); @@ -366,6 +363,8 @@ static int acpi_cpufreq_target(struct cpufreq_policy *policy, else cpu_set(policy->cpu, cmd.mask); + freqs.old = data->freq_table[perf->state].frequency; + freqs.new = data->freq_table[next_perf_state].frequency; for_each_cpu_mask(i, cmd.mask) { freqs.cpu = i; cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); @@ -613,6 +612,7 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) policy->cur = acpi_cpufreq_guess_freq(data, policy->cpu); break; case ACPI_ADR_SPACE_FIXED_HARDWARE: + acpi_cpufreq_driver.get = get_cur_freq_on_cpu; get_cur_freq_on_cpu(cpu); break; default: @@ -687,7 +687,6 @@ static struct freq_attr *acpi_cpufreq_attr[] = { static struct cpufreq_driver acpi_cpufreq_driver = { .verify = acpi_cpufreq_verify, .target = acpi_cpufreq_target, - .get = get_cur_freq_on_cpu, .init = acpi_cpufreq_cpu_init, .exit = acpi_cpufreq_cpu_exit, .resume = acpi_cpufreq_resume, -- cgit v0.10.2 From a6f6e6e6ab464c9d1dff66570b78be2f66d8ba3d Mon Sep 17 00:00:00 2001 From: Venkatesh Pallipadi Date: Tue, 3 Oct 2006 12:37:42 -0700 Subject: [CPUFREQ][7/8] acpi-cpufreq: Fix get of current frequency breakage Recent speedstep-centrino unification onto acpi-cpufreq patchset broke cpuinfo_cur_freq interface in /sys/../cpuinfo/, when MSR was used for transitions. Attached patch fixes that breakage. Signed-off-by: Venkatesh Pallipadi Signed-off-by: Dave Jones diff --git a/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c b/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c index e6513e9..8b0c7db 100644 --- a/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c +++ b/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c @@ -101,10 +101,13 @@ static unsigned extract_io(u32 value, struct acpi_cpufreq_data *data) static unsigned extract_msr(u32 msr, struct acpi_cpufreq_data *data) { int i; + struct acpi_processor_performance *perf; msr &= INTEL_MSR_RANGE; + perf = data->acpi_data; + for (i = 0; data->freq_table[i].frequency != CPUFREQ_TABLE_END; i++) { - if (msr == data->freq_table[i].index) + if (msr == perf->states[data->freq_table[i].index].status) return data->freq_table[i].frequency; } return data->freq_table[0].frequency; -- cgit v0.10.2 From dfde5d62ed9b28b0bda676c16e8cb635df244ef2 Mon Sep 17 00:00:00 2001 From: Venkatesh Pallipadi Date: Tue, 3 Oct 2006 12:38:45 -0700 Subject: [CPUFREQ][8/8] acpi-cpufreq: Add support for freq feedback from hardware MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enable ondemand governor and acpi-cpufreq to use IA32_APERF and IA32_MPERF MSR to get active frequency feedback for the last sampling interval. This will make ondemand take right frequency decisions when hardware coordination of frequency is going on. Without APERF/MPERF, ondemand can take wrong decision at times due to underlying hardware coordination or TM2. Example: * CPU 0 and CPU 1 are hardware cooridnated. * CPU 1 running at highest frequency. * CPU 0 was running at highest freq. Now ondemand reduces it to some intermediate frequency based on utilization. * Due to underlying hardware coordination with other CPU 1, CPU 0 continues to run at highest frequency (as long as other CPU is at highest). * When ondemand samples CPU 0 again next time, without actual frequency feedback from APERF/MPERF, it will think that previous frequency change was successful and can go to wrong target frequency. This is because it thinks that utilization it has got this sampling interval is when running at intermediate frequency, rather than actual highest frequency. More information about IA32_APERF IA32_MPERF MSR: Refer to IA-32 Intel® Architecture Software Developer's Manual at http://developer.intel.com Signed-off-by: Venkatesh Pallipadi Signed-off-by: Dave Jones diff --git a/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c b/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c index 8b0c7db..f8a8e46 100644 --- a/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c +++ b/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c @@ -58,10 +58,12 @@ enum { }; #define INTEL_MSR_RANGE (0xffff) +#define CPUID_6_ECX_APERFMPERF_CAPABILITY (0x1) struct acpi_cpufreq_data { struct acpi_processor_performance *acpi_data; struct cpufreq_frequency_table *freq_table; + unsigned int max_freq; unsigned int resume; unsigned int cpu_feature; }; @@ -258,6 +260,100 @@ static u32 get_cur_val(cpumask_t mask) return cmd.val; } +/* + * Return the measured active (C0) frequency on this CPU since last call + * to this function. + * Input: cpu number + * Return: Average CPU frequency in terms of max frequency (zero on error) + * + * We use IA32_MPERF and IA32_APERF MSRs to get the measured performance + * over a period of time, while CPU is in C0 state. + * IA32_MPERF counts at the rate of max advertised frequency + * IA32_APERF counts at the rate of actual CPU frequency + * Only IA32_APERF/IA32_MPERF ratio is architecturally defined and + * no meaning should be associated with absolute values of these MSRs. + */ +static unsigned int get_measured_perf(unsigned int cpu) +{ + union { + struct { + u32 lo; + u32 hi; + } split; + u64 whole; + } aperf_cur, mperf_cur; + + cpumask_t saved_mask; + unsigned int perf_percent; + unsigned int retval; + + saved_mask = current->cpus_allowed; + set_cpus_allowed(current, cpumask_of_cpu(cpu)); + if (get_cpu() != cpu) { + /* We were not able to run on requested processor */ + put_cpu(); + return 0; + } + + rdmsr(MSR_IA32_APERF, aperf_cur.split.lo, aperf_cur.split.hi); + rdmsr(MSR_IA32_MPERF, mperf_cur.split.lo, mperf_cur.split.hi); + + wrmsr(MSR_IA32_APERF, 0,0); + wrmsr(MSR_IA32_MPERF, 0,0); + +#ifdef __i386__ + /* + * We dont want to do 64 bit divide with 32 bit kernel + * Get an approximate value. Return failure in case we cannot get + * an approximate value. + */ + if (unlikely(aperf_cur.split.hi || mperf_cur.split.hi)) { + int shift_count; + u32 h; + + h = max_t(u32, aperf_cur.split.hi, mperf_cur.split.hi); + shift_count = fls(h); + + aperf_cur.whole >>= shift_count; + mperf_cur.whole >>= shift_count; + } + + if (((unsigned long)(-1) / 100) < aperf_cur.split.lo) { + int shift_count = 7; + aperf_cur.split.lo >>= shift_count; + mperf_cur.split.lo >>= shift_count; + } + + if (aperf_cur.split.lo && mperf_cur.split.lo) { + perf_percent = (aperf_cur.split.lo * 100) / mperf_cur.split.lo; + } else { + perf_percent = 0; + } + +#else + if (unlikely(((unsigned long)(-1) / 100) < aperf_cur.whole)) { + int shift_count = 7; + aperf_cur.whole >>= shift_count; + mperf_cur.whole >>= shift_count; + } + + if (aperf_cur.whole && mperf_cur.whole) { + perf_percent = (aperf_cur.whole * 100) / mperf_cur.whole; + } else { + perf_percent = 0; + } + +#endif + + retval = drv_data[cpu]->max_freq * perf_percent / 100; + + put_cpu(); + set_cpus_allowed(current, saved_mask); + + dprintk("cpu %d: performance percent %d\n", cpu, perf_percent); + return retval; +} + static unsigned int get_cur_freq_on_cpu(unsigned int cpu) { struct acpi_cpufreq_data *data = drv_data[cpu]; @@ -497,7 +593,6 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) unsigned int valid_states = 0; unsigned int cpu = policy->cpu; struct acpi_cpufreq_data *data; - unsigned int l, h; unsigned int result = 0; struct cpuinfo_x86 *c = &cpu_data[policy->cpu]; struct acpi_processor_performance *perf; @@ -591,6 +686,7 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) } policy->governor = CPUFREQ_DEFAULT_GOVERNOR; + data->max_freq = perf->states[0].core_frequency * 1000; /* table init */ for (i = 0; i < perf->state_count; i++) { if (i > 0 && perf->states[i].core_frequency == @@ -625,6 +721,15 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) /* notify BIOS that we exist */ acpi_processor_notify_smm(THIS_MODULE); + /* Check for APERF/MPERF support in hardware */ + if (c->x86_vendor == X86_VENDOR_INTEL && c->cpuid_level >= 6) { + unsigned int ecx; + ecx = cpuid_ecx(6); + if (ecx & CPUID_6_ECX_APERFMPERF_CAPABILITY) { + acpi_cpufreq_driver.getavg = get_measured_perf; + } + } + dprintk("CPU%u - ACPI performance management activated.\n", cpu); for (i = 0; i < perf->state_count; i++) dprintk(" %cP%d: %d MHz, %d mW, %d uS\n", diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 86e69b7..56c433e 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1274,6 +1274,26 @@ int cpufreq_driver_target(struct cpufreq_policy *policy, } EXPORT_SYMBOL_GPL(cpufreq_driver_target); +int cpufreq_driver_getavg(struct cpufreq_policy *policy) +{ + int ret = 0; + + policy = cpufreq_cpu_get(policy->cpu); + if (!policy) + return -EINVAL; + + mutex_lock(&policy->lock); + + if (cpu_online(policy->cpu) && cpufreq_driver->getavg) + ret = cpufreq_driver->getavg(policy->cpu); + + mutex_unlock(&policy->lock); + + cpufreq_cpu_put(policy); + return ret; +} +EXPORT_SYMBOL_GPL(cpufreq_driver_getavg); + /* * Locking: Must be called with the lock_cpu_hotplug() lock held * when "event" is CPUFREQ_GOV_LIMITS diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index bf8aa45..291cfe9 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -393,8 +393,15 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) * policy. To be safe, we focus 10 points under the threshold. */ if (load < (dbs_tuners_ins.up_threshold - 10)) { - unsigned int freq_next = (policy->cur * load) / + unsigned int freq_next, freq_cur; + + freq_cur = cpufreq_driver_getavg(policy); + if (!freq_cur) + freq_cur = policy->cur; + + freq_next = (freq_cur * load) / (dbs_tuners_ins.up_threshold - 10); + if (!dbs_tuners_ins.powersave_bias) { __cpufreq_driver_target(policy, freq_next, CPUFREQ_RELATION_L); diff --git a/include/asm-i386/msr.h b/include/asm-i386/msr.h index 62b76cd..0aa15fc 100644 --- a/include/asm-i386/msr.h +++ b/include/asm-i386/msr.h @@ -125,6 +125,9 @@ static inline void wrmsrl (unsigned long msr, unsigned long long val) #define MSR_IA32_PERF_STATUS 0x198 #define MSR_IA32_PERF_CTL 0x199 +#define MSR_IA32_MPERF 0xE7 +#define MSR_IA32_APERF 0xE8 + #define MSR_IA32_THERM_CONTROL 0x19a #define MSR_IA32_THERM_INTERRUPT 0x19b #define MSR_IA32_THERM_STATUS 0x19c diff --git a/include/asm-x86_64/msr.h b/include/asm-x86_64/msr.h index 37e1941..e615822 100644 --- a/include/asm-x86_64/msr.h +++ b/include/asm-x86_64/msr.h @@ -307,6 +307,9 @@ static inline unsigned int cpuid_edx(unsigned int op) #define MSR_IA32_PERF_STATUS 0x198 #define MSR_IA32_PERF_CTL 0x199 +#define MSR_IA32_MPERF 0xE7 +#define MSR_IA32_APERF 0xE8 + #define MSR_IA32_THERM_CONTROL 0x19a #define MSR_IA32_THERM_INTERRUPT 0x19b #define MSR_IA32_THERM_STATUS 0x19c diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 4ea39fe..7f008f6 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -172,6 +172,8 @@ extern int __cpufreq_driver_target(struct cpufreq_policy *policy, unsigned int relation); +extern int cpufreq_driver_getavg(struct cpufreq_policy *policy); + int cpufreq_register_governor(struct cpufreq_governor *governor); void cpufreq_unregister_governor(struct cpufreq_governor *governor); @@ -204,6 +206,7 @@ struct cpufreq_driver { unsigned int (*get) (unsigned int cpu); /* optional */ + unsigned int (*getavg) (unsigned int cpu); int (*exit) (struct cpufreq_policy *policy); int (*suspend) (struct cpufreq_policy *policy, pm_message_t pmsg); int (*resume) (struct cpufreq_policy *policy); -- cgit v0.10.2 From 3e74341c7b356ce142ace4e9b5ff08448c9f320e Mon Sep 17 00:00:00 2001 From: Amol Lad Date: Tue, 17 Oct 2006 10:02:55 +0530 Subject: [CPUFREQ] sc520_freq.c: ioremap balanced with iounmap ioremap must be balanced by an iounmap and failing to do so can result in a memory leak. Tested (compilation only): - using allmodconfig - making sure the files are compiling without any warning/error due to new changes Signed-off-by: Amol Lad Signed-off-by: Dave Jones diff --git a/arch/i386/kernel/cpu/cpufreq/sc520_freq.c b/arch/i386/kernel/cpu/cpufreq/sc520_freq.c index ef457d5..b8fb4b5 100644 --- a/arch/i386/kernel/cpu/cpufreq/sc520_freq.c +++ b/arch/i386/kernel/cpu/cpufreq/sc520_freq.c @@ -153,6 +153,7 @@ static struct cpufreq_driver sc520_freq_driver = { static int __init sc520_freq_init(void) { struct cpuinfo_x86 *c = cpu_data; + int err; /* Test if we have the right hardware */ if(c->x86_vendor != X86_VENDOR_AMD || @@ -166,7 +167,11 @@ static int __init sc520_freq_init(void) return -ENOMEM; } - return cpufreq_register_driver(&sc520_freq_driver); + err = cpufreq_register_driver(&sc520_freq_driver); + if (err) + iounmap(cpuctl); + + return err; } -- cgit v0.10.2 From 8c6193684928407ea097f370778e3df7e971d957 Mon Sep 17 00:00:00 2001 From: Hiroshi Miura Date: Wed, 18 Oct 2006 12:59:33 +0900 Subject: [CPUFREQ] Fix speedstep-smi CPU detection to not run on Pentium 4. If someone inserts speedstep-smi on a mobile P4, it prevents other cpufreq modules from loading until it is unloaded. Signed-off-by: Hiroshi Miura Signed-off-by: Dave Jones diff --git a/arch/i386/kernel/cpu/cpufreq/speedstep-smi.c b/arch/i386/kernel/cpu/cpufreq/speedstep-smi.c index c28333d..ff0d898 100644 --- a/arch/i386/kernel/cpu/cpufreq/speedstep-smi.c +++ b/arch/i386/kernel/cpu/cpufreq/speedstep-smi.c @@ -360,9 +360,6 @@ static int __init speedstep_init(void) case SPEEDSTEP_PROCESSOR_PIII_C: case SPEEDSTEP_PROCESSOR_PIII_C_EARLY: break; - case SPEEDSTEP_PROCESSOR_P4M: - printk(KERN_INFO "speedstep-smi: you're trying to use this cpufreq driver on a Pentium 4-based CPU. Most likely it will not work.\n"); - break; default: speedstep_processor = 0; } -- cgit v0.10.2 From 0a1230acb549593949397d331f1ecf07889dde20 Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Wed, 18 Oct 2006 00:15:49 -0400 Subject: [CPUFREQ] Remove duplicate include from acpi-cpufreq Signed-off-by: Dave Jones diff --git a/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c b/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c index f8a8e46..71b9340 100644 --- a/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c +++ b/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c @@ -32,7 +32,6 @@ #include #include #include -#include /* current */ #include #include -- cgit v0.10.2 From 95dd722700dc9bbb000d51cab07dde48720e9178 Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Wed, 18 Oct 2006 00:41:48 -0400 Subject: [CPUFREQ] acpi-cpufreq: Fix up some CodingStyle nits leftover from the lindenting. Signed-off-by: Dave Jones diff --git a/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c b/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c index 71b9340..23f8353 100644 --- a/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c +++ b/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c @@ -92,7 +92,7 @@ static unsigned extract_io(u32 value, struct acpi_cpufreq_data *data) perf = data->acpi_data; - for (i = 0; i < perf->state_count; i++) { + for (i=0; istate_count; i++) { if (value == perf->states[i].status) return data->freq_table[i].frequency; } @@ -107,7 +107,7 @@ static unsigned extract_msr(u32 msr, struct acpi_cpufreq_data *data) msr &= INTEL_MSR_RANGE; perf = data->acpi_data; - for (i = 0; data->freq_table[i].frequency != CPUFREQ_TABLE_END; i++) { + for (i=0; data->freq_table[i].frequency != CPUFREQ_TABLE_END; i++) { if (msr == perf->states[data->freq_table[i].index].status) return data->freq_table[i].frequency; } @@ -128,25 +128,23 @@ static unsigned extract_freq(u32 val, struct acpi_cpufreq_data *data) static void wrport(u16 port, u8 bit_width, u32 value) { - if (bit_width <= 8) { + if (bit_width <= 8) outb(value, port); - } else if (bit_width <= 16) { + else if (bit_width <= 16) outw(value, port); - } else if (bit_width <= 32) { + else if (bit_width <= 32) outl(value, port); - } } static void rdport(u16 port, u8 bit_width, u32 * ret) { *ret = 0; - if (bit_width <= 8) { + if (bit_width <= 8) *ret = inb(port); - } else if (bit_width <= 16) { + else if (bit_width <= 16) *ret = inw(port); - } else if (bit_width <= 32) { + else if (bit_width <= 32) *ret = inl(port); - } } struct msr_addr { @@ -202,7 +200,7 @@ static void do_drv_write(struct drv_cmd *cmd) } } -static inline void drv_read(struct drv_cmd *cmd) +static void drv_read(struct drv_cmd *cmd) { cpumask_t saved_mask = current->cpus_allowed; cmd->val = 0; @@ -210,7 +208,6 @@ static inline void drv_read(struct drv_cmd *cmd) set_cpus_allowed(current, cmd->mask); do_drv_read(cmd); set_cpus_allowed(current, saved_mask); - } static void drv_write(struct drv_cmd *cmd) @@ -323,11 +320,10 @@ static unsigned int get_measured_perf(unsigned int cpu) mperf_cur.split.lo >>= shift_count; } - if (aperf_cur.split.lo && mperf_cur.split.lo) { + if (aperf_cur.split.lo && mperf_cur.split.lo) perf_percent = (aperf_cur.split.lo * 100) / mperf_cur.split.lo; - } else { + else perf_percent = 0; - } #else if (unlikely(((unsigned long)(-1) / 100) < aperf_cur.whole)) { @@ -336,11 +332,10 @@ static unsigned int get_measured_perf(unsigned int cpu) mperf_cur.whole >>= shift_count; } - if (aperf_cur.whole && mperf_cur.whole) { + if (aperf_cur.whole && mperf_cur.whole) perf_percent = (aperf_cur.whole * 100) / mperf_cur.whole; - } else { + else perf_percent = 0; - } #endif @@ -377,7 +372,7 @@ static unsigned int check_freqs(cpumask_t mask, unsigned int freq, unsigned int cur_freq; unsigned int i; - for (i = 0; i < 100; i++) { + for (i=0; i<100; i++) { cur_freq = extract_freq(get_cur_val(mask), data); if (cur_freq == freq) return 1; @@ -403,7 +398,7 @@ static int acpi_cpufreq_target(struct cpufreq_policy *policy, dprintk("acpi_cpufreq_target %d (%d)\n", target_freq, policy->cpu); if (unlikely(data == NULL || - data->acpi_data == NULL || data->freq_table == NULL)) { + data->acpi_data == NULL || data->freq_table == NULL)) { return -ENODEV; } @@ -507,15 +502,15 @@ acpi_cpufreq_guess_freq(struct acpi_cpufreq_data *data, unsigned int cpu) unsigned long freq; unsigned long freqn = perf->states[0].core_frequency * 1000; - for (i = 0; i < (perf->state_count - 1); i++) { + for (i=0; i<(perf->state_count-1); i++) { freq = freqn; - freqn = perf->states[i + 1].core_frequency * 1000; + freqn = perf->states[i+1].core_frequency * 1000; if ((2 * cpu_khz) > (freqn + freq)) { perf->state = i; return freq; } } - perf->state = perf->state_count - 1; + perf->state = perf->state_count-1; return freqn; } else { /* assume CPU is at P0... */ @@ -608,9 +603,8 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) data->acpi_data = acpi_perf_data[cpu]; drv_data[cpu] = data; - if (cpu_has(c, X86_FEATURE_CONSTANT_TSC)) { + if (cpu_has(c, X86_FEATURE_CONSTANT_TSC)) acpi_cpufreq_driver.flags |= CPUFREQ_CONST_LOOPS; - } result = acpi_processor_register_performance(data->acpi_data, cpu); if (result) @@ -618,8 +612,9 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) perf = data->acpi_data; policy->shared_type = perf->shared_type; + /* - * Will let policy->cpus know about dependency only when software + * Will let policy->cpus know about dependency only when software * coordination is required. */ if (policy->shared_type == CPUFREQ_SHARED_TYPE_ALL || @@ -667,9 +662,8 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) goto err_unreg; } - data->freq_table = - kmalloc(sizeof(struct cpufreq_frequency_table) * - (perf->state_count + 1), GFP_KERNEL); + data->freq_table = kmalloc(sizeof(struct cpufreq_frequency_table) * + (perf->state_count+1), GFP_KERNEL); if (!data->freq_table) { result = -ENOMEM; goto err_unreg; @@ -677,7 +671,7 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) /* detect transition latency */ policy->cpuinfo.transition_latency = 0; - for (i = 0; i < perf->state_count; i++) { + for (i=0; istate_count; i++) { if ((perf->states[i].transition_latency * 1000) > policy->cpuinfo.transition_latency) policy->cpuinfo.transition_latency = @@ -687,9 +681,9 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) data->max_freq = perf->states[0].core_frequency * 1000; /* table init */ - for (i = 0; i < perf->state_count; i++) { - if (i > 0 && perf->states[i].core_frequency == - perf->states[i - 1].core_frequency) + for (i=0; istate_count; i++) { + if (i>0 && perf->states[i].core_frequency == + perf->states[i-1].core_frequency) continue; data->freq_table[valid_states].index = i; @@ -700,9 +694,8 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) data->freq_table[perf->state_count].frequency = CPUFREQ_TABLE_END; result = cpufreq_frequency_table_cpuinfo(policy, data->freq_table); - if (result) { + if (result) goto err_freqfree; - } switch (data->cpu_feature) { case ACPI_ADR_SPACE_SYSTEM_IO: @@ -724,9 +717,8 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) if (c->x86_vendor == X86_VENDOR_INTEL && c->cpuid_level >= 6) { unsigned int ecx; ecx = cpuid_ecx(6); - if (ecx & CPUID_6_ECX_APERFMPERF_CAPABILITY) { + if (ecx & CPUID_6_ECX_APERFMPERF_CAPABILITY) acpi_cpufreq_driver.getavg = get_measured_perf; - } } dprintk("CPU%u - ACPI performance management activated.\n", cpu); @@ -747,11 +739,11 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) return result; - err_freqfree: +err_freqfree: kfree(data->freq_table); - err_unreg: +err_unreg: acpi_processor_unregister_performance(perf, cpu); - err_free: +err_free: kfree(data); drv_data[cpu] = NULL; @@ -827,7 +819,8 @@ static void __exit acpi_cpufreq_exit(void) module_param(acpi_pstate_strict, uint, 0644); MODULE_PARM_DESC(acpi_pstate_strict, - "value 0 or non-zero. non-zero -> strict ACPI checks are performed during frequency changes."); + "value 0 or non-zero. non-zero -> strict ACPI checks are " + "performed during frequency changes."); late_initcall(acpi_cpufreq_init); module_exit(acpi_cpufreq_exit); -- cgit v0.10.2 From 8dd851de8184bb39c4ea86de20a0ed2496e6eb0d Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 20 Oct 2006 02:11:40 +0100 Subject: =?UTF-8?q?[MTD=20NAND]=20OLPC=20CAF=C3=89=20driver=20update?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix OOB handling, bad block table marker placement - Some cleanups, enable runtime-optional debugging - Allow BBT stuff to be skipped Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index 8d7a795..60cb019 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c @@ -5,7 +5,7 @@ * Copyright © 2006 David Woodhouse */ -//#define DEBUG +#define DEBUG #include #undef DEBUG @@ -53,15 +53,24 @@ struct cafe_priv { static int usedma = 1; module_param(usedma, int, 0644); +static int skipbbt = 0; +module_param(skipbbt, int, 0644); + +static int debug = 0; +module_param(debug, int, 0644); + +#define cafe_dev_dbg(dev, args...) do { if (debug) dev_dbg(dev, ##args); } while(0) + + static int cafe_device_ready(struct mtd_info *mtd) { struct cafe_priv *cafe = mtd->priv; int result = !!(readl(cafe->mmio + CAFE_NAND_STATUS) | 0x40000000); - uint32_t irqs = readl(cafe->mmio + 0x10); - writel(irqs, cafe->mmio+0x10); - dev_dbg(&cafe->pdev->dev, "NAND device is%s ready, IRQ %x (%x) (%x,%x)\n", - result?"":" not", irqs, readl(cafe->mmio + 0x10), + uint32_t irqs = readl(cafe->mmio + CAFE_NAND_IRQ); + writel(irqs, cafe->mmio+CAFE_NAND_IRQ); + cafe_dev_dbg(&cafe->pdev->dev, "NAND device is%s ready, IRQ %x (%x) (%x,%x)\n", + result?"":" not", irqs, readl(cafe->mmio + CAFE_NAND_IRQ), readl(cafe->mmio + 0x3008), readl(cafe->mmio + 0x300c)); return result; } @@ -77,7 +86,7 @@ static void cafe_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) memcpy_toio(cafe->mmio + CAFE_NAND_WRITE_DATA + cafe->datalen, buf, len); cafe->datalen += len; - dev_dbg(&cafe->pdev->dev, "Copy 0x%x bytes to write buffer. datalen 0x%x\n", + cafe_dev_dbg(&cafe->pdev->dev, "Copy 0x%x bytes to write buffer. datalen 0x%x\n", len, cafe->datalen); } @@ -90,7 +99,7 @@ static void cafe_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) else memcpy_fromio(buf, cafe->mmio + CAFE_NAND_READ_DATA + cafe->datalen, len); - dev_dbg(&cafe->pdev->dev, "Copy 0x%x bytes from position 0x%x in read buffer.\n", + cafe_dev_dbg(&cafe->pdev->dev, "Copy 0x%x bytes from position 0x%x in read buffer.\n", len, cafe->datalen); cafe->datalen += len; } @@ -101,7 +110,7 @@ static uint8_t cafe_read_byte(struct mtd_info *mtd) uint8_t d; cafe_read_buf(mtd, &d, 1); - dev_dbg(&cafe->pdev->dev, "Read %02x\n", d); + cafe_dev_dbg(&cafe->pdev->dev, "Read %02x\n", d); return d; } @@ -115,14 +124,14 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, uint32_t doneint = 0x80000000; int i; - dev_dbg(&cafe->pdev->dev, "cmdfunc %02x, 0x%x, 0x%x\n", + cafe_dev_dbg(&cafe->pdev->dev, "cmdfunc %02x, 0x%x, 0x%x\n", command, column, page_addr); if (command == NAND_CMD_ERASE2 || command == NAND_CMD_PAGEPROG) { /* Second half of a command we already calculated */ writel(cafe->ctl2 | 0x100 | command, cafe->mmio + 0x04); ctl1 = cafe->ctl1; - dev_dbg(&cafe->pdev->dev, "Continue command, ctl1 %08x, #data %d\n", + cafe_dev_dbg(&cafe->pdev->dev, "Continue command, ctl1 %08x, #data %d\n", cafe->ctl1, cafe->nr_data); goto do_command; } @@ -163,7 +172,7 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, if (command == NAND_CMD_READID || command == NAND_CMD_STATUS) { ctl1 |= (1<<26); /* rd */ /* Always 5 bytes, for now */ - cafe->datalen = 5; + cafe->datalen = 4; /* And one address cycle -- even for STATUS, since the controller doesn't work without */ adrbytes = 1; } else if (command == NAND_CMD_READ0 || command == NAND_CMD_READ1 || @@ -183,7 +192,7 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, deals with them both at once, later */ cafe->ctl1 = ctl1; cafe->ctl2 = 0; - dev_dbg(&cafe->pdev->dev, "Setup for delayed command, ctl1 %08x, dlen %x\n", + cafe_dev_dbg(&cafe->pdev->dev, "Setup for delayed command, ctl1 %08x, dlen %x\n", cafe->ctl1, cafe->datalen); return; } @@ -194,13 +203,14 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, writel(cafe->ctl2 | 0x100 | NAND_CMD_READSTART, cafe->mmio + CAFE_NAND_CTRL2); do_command: - if (cafe->datalen == 2112) - cafe->datalen = 2062; - dev_dbg(&cafe->pdev->dev, "dlen %x, ctl1 %x, ctl2 %x\n", + // ECC on read only works if we ... + // if (cafe->datalen == 2112) + // cafe->datalen = 2062; + cafe_dev_dbg(&cafe->pdev->dev, "dlen %x, ctl1 %x, ctl2 %x\n", cafe->datalen, ctl1, readl(cafe->mmio+CAFE_NAND_CTRL2)); /* NB: The datasheet lies -- we really should be subtracting 1 here */ writel(cafe->datalen, cafe->mmio + CAFE_NAND_DATA_LEN); - writel(0x90000000, cafe->mmio + 0x10); + writel(0x90000000, cafe->mmio + CAFE_NAND_IRQ); if (usedma && (ctl1 & (3<<25))) { uint32_t dmactl = 0xc0000000 + cafe->datalen; /* If WR or RD bits set, set up DMA */ @@ -230,20 +240,20 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, ndelay(100); if (1) { - int c = 50000; + int c = 500000; uint32_t irqs; while (c--) { - irqs = readl(cafe->mmio + 0x10); + irqs = readl(cafe->mmio + CAFE_NAND_IRQ); if (irqs & doneint) break; udelay(1); - if (!(c & 1000)) - dev_dbg(&cafe->pdev->dev, "Wait for ready, IRQ %x\n", irqs); + if (!(c % 100000)) + cafe_dev_dbg(&cafe->pdev->dev, "Wait for ready, IRQ %x\n", irqs); cpu_relax(); } - writel(doneint, cafe->mmio + 0x10); - dev_dbg(&cafe->pdev->dev, "Command %x completed after %d usec, irqs %x (%x)\n", command, 50000-c, irqs, readl(cafe->mmio + 0x10)); + writel(doneint, cafe->mmio + CAFE_NAND_IRQ); + cafe_dev_dbg(&cafe->pdev->dev, "Command %x completed after %d usec, irqs %x (%x)\n", command, 50000-c, irqs, readl(cafe->mmio + CAFE_NAND_IRQ)); } @@ -276,18 +286,18 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, static void cafe_select_chip(struct mtd_info *mtd, int chipnr) { //struct cafe_priv *cafe = mtd->priv; - // dev_dbg(&cafe->pdev->dev, "select_chip %d\n", chipnr); + // cafe_dev_dbg(&cafe->pdev->dev, "select_chip %d\n", chipnr); } static int cafe_nand_interrupt(int irq, void *id, struct pt_regs *regs) { struct mtd_info *mtd = id; struct cafe_priv *cafe = mtd->priv; - uint32_t irqs = readl(cafe->mmio + 0x10); - writel(irqs & ~0x90000000, cafe->mmio + 0x10); + uint32_t irqs = readl(cafe->mmio + CAFE_NAND_IRQ); + writel(irqs & ~0x90000000, cafe->mmio + CAFE_NAND_IRQ); if (!irqs) return IRQ_NONE; - dev_dbg(&cafe->pdev->dev, "irq, bits %x (%x)\n", irqs, readl(cafe->mmio + 0x10)); + cafe_dev_dbg(&cafe->pdev->dev, "irq, bits %x (%x)\n", irqs, readl(cafe->mmio + CAFE_NAND_IRQ)); return IRQ_HANDLED; } @@ -335,7 +345,7 @@ static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, WARN_ON(chip->oob_poi != chip->buffers->oobrbuf); - dev_dbg(&cafe->pdev->dev, "ECC result %08x SYN1,2 %08x\n", readl(cafe->mmio + 0x3c), readl(cafe->mmio + 0x50)); + cafe_dev_dbg(&cafe->pdev->dev, "ECC result %08x SYN1,2 %08x\n", readl(cafe->mmio + 0x3c), readl(cafe->mmio + 0x50)); chip->read_buf(mtd, buf, mtd->writesize); chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); @@ -343,7 +353,44 @@ static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, return 0; } -static char foo[14]; +static struct nand_ecclayout cafe_oobinfo_2048 = { + .eccbytes = 14, + .eccpos = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, + .oobfree = {{14, 50}} +}; + +/* Ick. The BBT code really ought to be able to work this bit out + for itself from the above */ +static uint8_t cafe_bbt_pattern[] = {'B', 'b', 't', '0' }; +static uint8_t cafe_mirror_pattern[] = {'1', 't', 'b', 'B' }; + +static struct nand_bbt_descr cafe_bbt_main_descr_2048 = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 14, + .len = 4, + .veroffs = 18, + .maxblocks = 4, + .pattern = cafe_bbt_pattern +}; + +static struct nand_bbt_descr cafe_bbt_mirror_descr_2048 = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 14, + .len = 4, + .veroffs = 18, + .maxblocks = 4, + .pattern = cafe_mirror_pattern +}; + +static struct nand_ecclayout cafe_oobinfo_512 = { + .eccbytes = 14, + .eccpos = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, + .oobfree = {{14, 2}} +}; + + static void cafe_nand_write_page_lowlevel(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf) { @@ -352,8 +399,7 @@ static void cafe_nand_write_page_lowlevel(struct mtd_info *mtd, WARN_ON(chip->oob_poi != chip->buffers->oobwbuf); chip->write_buf(mtd, buf, mtd->writesize); - chip->write_buf(mtd, foo, 14); - // chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); /* Set up ECC autogeneration */ cafe->ctl2 |= (1<<27) | (1<<30); @@ -410,6 +456,10 @@ static int cafe_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, return 0; } +static int cafe_nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) +{ + return 0; +} static int __devinit cafe_nand_probe(struct pci_dev *pdev, const struct pci_device_id *ent) @@ -461,6 +511,11 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, /* Enable the following for a flash based bad block table */ cafe->nand.options = NAND_USE_FLASH_BBT | NAND_NO_AUTOINCR | NAND_OWN_BUFFERS; + + if (skipbbt) { + cafe->nand.options |= NAND_SKIP_BBTSCAN; + cafe->nand.block_bad = cafe_nand_block_bad; + } /* Timings from Marvell's test code (not verified or calculated by us) */ writel(0xffffffff, cafe->mmio + CAFE_NAND_IRQ_MASK); @@ -473,7 +528,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, writel(0xffffffff, cafe->mmio + CAFE_NAND_TIMING2); writel(0xffffffff, cafe->mmio + CAFE_NAND_TIMING3); #endif - writel(0xdfffffff, cafe->mmio + 0x14); + writel(0xffffffff, cafe->mmio + CAFE_NAND_IRQ_MASK); err = request_irq(pdev->irq, &cafe_nand_interrupt, SA_SHIRQ, "CAFE NAND", mtd); if (err) { dev_warn(&pdev->dev, "Could not register IRQ %d\n", pdev->irq); @@ -498,18 +553,18 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, writel((cafe->dmaaddr >> 16) >> 16, cafe->mmio + 0x48); else writel(0, cafe->mmio + 0x48); - dev_dbg(&cafe->pdev->dev, "Set DMA address to %x (virt %p)\n", + cafe_dev_dbg(&cafe->pdev->dev, "Set DMA address to %x (virt %p)\n", readl(cafe->mmio+0x44), cafe->dmabuf); /* Enable NAND IRQ in global IRQ mask register */ writel(0x80000007, cafe->mmio + 0x300c); - dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n", + cafe_dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n", readl(cafe->mmio + 0x3004), readl(cafe->mmio + 0x300c)); #endif #if 1 mtd->writesize=2048; mtd->oobsize = 0x40; - memset(cafe->dmabuf, 0xa5, 2112); + memset(cafe->dmabuf, 0x5a, 2112); cafe->nand.cmdfunc(mtd, NAND_CMD_READID, 0, -1); cafe->nand.read_byte(mtd); cafe->nand.read_byte(mtd); @@ -528,7 +583,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, #if 0 writel(0x84600070, cafe->mmio); udelay(10); - dev_dbg(&cafe->pdev->dev, "Status %x\n", readl(cafe->mmio + 0x30)); + cafe_dev_dbg(&cafe->pdev->dev, "Status %x\n", readl(cafe->mmio + 0x30)); #endif /* Scan to find existance of the device */ if (nand_scan_ident(mtd, 1)) { @@ -545,6 +600,9 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, cafe->nand.ecc.mode = NAND_ECC_HW_SYNDROME; cafe->nand.ecc.size = mtd->writesize; cafe->nand.ecc.bytes = 14; + cafe->nand.ecc.layout = &cafe_oobinfo_2048; + cafe->nand.bbt_td = &cafe_bbt_main_descr_2048; + cafe->nand.bbt_md = &cafe_bbt_mirror_descr_2048; cafe->nand.ecc.hwctl = (void *)cafe_nand_bug; cafe->nand.ecc.calculate = (void *)cafe_nand_bug; cafe->nand.ecc.correct = (void *)cafe_nand_bug; @@ -564,7 +622,6 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, if (err) goto out_irq; - pci_set_drvdata(pdev, mtd); add_mtd_device(mtd); goto out; @@ -633,3 +690,9 @@ MODULE_DESCRIPTION("NAND flash driver for OLPC CAFE chip"); /* Correct ECC for 2048 bytes of 0xff: 41 a0 71 65 54 27 f3 93 ec a9 be ed 0b a1 */ + +/* dwmw2's B-test board, in case of completely screwing it: +Bad eraseblock 2394 at 0x12b40000 +Bad eraseblock 2627 at 0x14860000 +Bad eraseblock 3349 at 0x1a2a0000 +*/ -- cgit v0.10.2 From 965a3d447276491b7ed053b25679c062beb04194 Mon Sep 17 00:00:00 2001 From: Martin Bligh Date: Fri, 20 Oct 2006 14:30:26 -0700 Subject: ACPI: avoid gcc warnings in ACPI mutex debug code 32bit vs 64 bit issues. sizeof(sizeof) and sizeof(pointer) is variable, but we're trying to print it as unsigned int or u32. Casts to unsigned long are used because type acpi_thread_id can be any one of typedef u64 acpi_native_uint; typedef u32 acpi_native_uint; typedef u16 acpi_native_uint; #define acpi_thread_id struct task_struct * Signed-off-by: Martin J. Bligh Acked-by: Jeff Garzik Signed-off-by: Andrew Morton Signed-off-by: Len Brown diff --git a/drivers/acpi/executer/exmutex.c b/drivers/acpi/executer/exmutex.c index 3a39c2e..bf90f04 100644 --- a/drivers/acpi/executer/exmutex.c +++ b/drivers/acpi/executer/exmutex.c @@ -266,10 +266,10 @@ acpi_ex_release_mutex(union acpi_operand_object *obj_desc, walk_state->thread->thread_id) && (obj_desc->mutex.os_mutex != ACPI_GLOBAL_LOCK)) { ACPI_ERROR((AE_INFO, - "Thread %X cannot release Mutex [%4.4s] acquired by thread %X", - (u32) walk_state->thread->thread_id, + "Thread %lX cannot release Mutex [%4.4s] acquired by thread %lX", + (unsigned long)walk_state->thread->thread_id, acpi_ut_get_node_name(obj_desc->mutex.node), - (u32) obj_desc->mutex.owner_thread->thread_id)); + (unsigned long)obj_desc->mutex.owner_thread->thread_id)); return_ACPI_STATUS(AE_AML_NOT_OWNER); } diff --git a/drivers/acpi/utilities/utdebug.c b/drivers/acpi/utilities/utdebug.c index bb1eaf9..9e9054e 100644 --- a/drivers/acpi/utilities/utdebug.c +++ b/drivers/acpi/utilities/utdebug.c @@ -180,8 +180,9 @@ acpi_ut_debug_print(u32 requested_debug_level, if (thread_id != acpi_gbl_prev_thread_id) { if (ACPI_LV_THREADS & acpi_dbg_level) { acpi_os_printf - ("\n**** Context Switch from TID %X to TID %X ****\n\n", - (u32) acpi_gbl_prev_thread_id, (u32) thread_id); + ("\n**** Context Switch from TID %lX to TID %lX ****\n\n", + (unsigned long) acpi_gbl_prev_thread_id, + (unsigned long) thread_id); } acpi_gbl_prev_thread_id = thread_id; diff --git a/drivers/acpi/utilities/utmutex.c b/drivers/acpi/utilities/utmutex.c index c39062a..180e73c 100644 --- a/drivers/acpi/utilities/utmutex.c +++ b/drivers/acpi/utilities/utmutex.c @@ -243,23 +243,24 @@ acpi_status acpi_ut_acquire_mutex(acpi_mutex_handle mutex_id) #endif ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, - "Thread %X attempting to acquire Mutex [%s]\n", - (u32) this_thread_id, acpi_ut_get_mutex_name(mutex_id))); + "Thread %lX attempting to acquire Mutex [%s]\n", + (unsigned long) this_thread_id, + acpi_ut_get_mutex_name(mutex_id))); status = acpi_os_acquire_mutex(acpi_gbl_mutex_info[mutex_id].mutex, ACPI_WAIT_FOREVER); if (ACPI_SUCCESS(status)) { ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, - "Thread %X acquired Mutex [%s]\n", - (u32) this_thread_id, + "Thread %lX acquired Mutex [%s]\n", + (unsigned long) this_thread_id, acpi_ut_get_mutex_name(mutex_id))); acpi_gbl_mutex_info[mutex_id].use_count++; acpi_gbl_mutex_info[mutex_id].thread_id = this_thread_id; } else { ACPI_EXCEPTION((AE_INFO, status, - "Thread %X could not acquire Mutex [%X]", - (u32) this_thread_id, mutex_id)); + "Thread %lX could not acquire Mutex [%X]", + (unsigned long) this_thread_id, mutex_id)); } return (status); @@ -285,7 +286,8 @@ acpi_status acpi_ut_release_mutex(acpi_mutex_handle mutex_id) this_thread_id = acpi_os_get_thread_id(); ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, - "Thread %X releasing Mutex [%s]\n", (u32) this_thread_id, + "Thread %lX releasing Mutex [%s]\n", + (unsigned long) this_thread_id, acpi_ut_get_mutex_name(mutex_id))); if (mutex_id > ACPI_MAX_MUTEX) { -- cgit v0.10.2 From d0a9081b1e75ba62bb4450c5b8e8299a41d25278 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 20 Oct 2006 14:30:27 -0700 Subject: ACPI: uninline ACPI global locking functions - Fixes a build problem with CONFIG_M386=y (include file dependencies get messy). - Share the implementation between x86 and x86_64 - These are too big to inline anyway. Signed-off-by: Andrew Morton Signed-off-by: Len Brown diff --git a/arch/i386/kernel/acpi/boot.c b/arch/i386/kernel/acpi/boot.c index ab974ff..bf7099c 100644 --- a/arch/i386/kernel/acpi/boot.c +++ b/arch/i386/kernel/acpi/boot.c @@ -1319,3 +1319,25 @@ static int __init setup_acpi_sci(char *s) return 0; } early_param("acpi_sci", setup_acpi_sci); + +int __acpi_acquire_global_lock(unsigned int *lock) +{ + unsigned int old, new, val; + do { + old = *lock; + new = (((old & ~0x3) + 2) + ((old >> 1) & 0x1)); + val = cmpxchg(lock, old, new); + } while (unlikely (val != old)); + return (new < 3) ? -1 : 0; +} + +int __acpi_release_global_lock(unsigned int *lock) +{ + unsigned int old, new, val; + do { + old = *lock; + new = old & ~0x3; + val = cmpxchg(lock, old, new); + } while (unlikely (val != old)); + return old & 0x1; +} diff --git a/include/asm-i386/acpi.h b/include/asm-i386/acpi.h index 6016632..29bee1d 100644 --- a/include/asm-i386/acpi.h +++ b/include/asm-i386/acpi.h @@ -56,30 +56,8 @@ #define ACPI_ENABLE_IRQS() local_irq_enable() #define ACPI_FLUSH_CPU_CACHE() wbinvd() - -static inline int -__acpi_acquire_global_lock (unsigned int *lock) -{ - unsigned int old, new, val; - do { - old = *lock; - new = (((old & ~0x3) + 2) + ((old >> 1) & 0x1)); - val = cmpxchg(lock, old, new); - } while (unlikely (val != old)); - return (new < 3) ? -1 : 0; -} - -static inline int -__acpi_release_global_lock (unsigned int *lock) -{ - unsigned int old, new, val; - do { - old = *lock; - new = old & ~0x3; - val = cmpxchg(lock, old, new); - } while (unlikely (val != old)); - return old & 0x1; -} +int __acpi_acquire_global_lock(unsigned int *lock); +int __acpi_release_global_lock(unsigned int *lock); #define ACPI_ACQUIRE_GLOBAL_LOCK(GLptr, Acq) \ ((Acq) = __acpi_acquire_global_lock((unsigned int *) GLptr)) diff --git a/include/asm-x86_64/acpi.h b/include/asm-x86_64/acpi.h index ed59aa4..1371e88 100644 --- a/include/asm-x86_64/acpi.h +++ b/include/asm-x86_64/acpi.h @@ -54,30 +54,8 @@ #define ACPI_ENABLE_IRQS() local_irq_enable() #define ACPI_FLUSH_CPU_CACHE() wbinvd() - -static inline int -__acpi_acquire_global_lock (unsigned int *lock) -{ - unsigned int old, new, val; - do { - old = *lock; - new = (((old & ~0x3) + 2) + ((old >> 1) & 0x1)); - val = cmpxchg(lock, old, new); - } while (unlikely (val != old)); - return (new < 3) ? -1 : 0; -} - -static inline int -__acpi_release_global_lock (unsigned int *lock) -{ - unsigned int old, new, val; - do { - old = *lock; - new = old & ~0x3; - val = cmpxchg(lock, old, new); - } while (unlikely (val != old)); - return old & 0x1; -} +int __acpi_acquire_global_lock(unsigned int *lock); +int __acpi_release_global_lock(unsigned int *lock); #define ACPI_ACQUIRE_GLOBAL_LOCK(GLptr, Acq) \ ((Acq) = __acpi_acquire_global_lock((unsigned int *) GLptr)) -- cgit v0.10.2 From 5c5e81aaa821822309fd2663c22c94ca0802e407 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 20 Oct 2006 14:30:30 -0700 Subject: ACPI: acpi-cpufreq: remove unused data when !CONFIG_SMP acpi-cpufreq.c, speedstep-centrino.c: warning: 'sw_any_bug_dmi_table' defined but not used Signed-off-by: Andrew Morton Signed-off-by: Len Brown diff --git a/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c b/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c index 57c880b..41e1289 100644 --- a/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c +++ b/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c @@ -402,6 +402,7 @@ static int sw_any_bug_found(struct dmi_system_id *d) return 0; } +#ifdef CONFIG_SMP static struct dmi_system_id sw_any_bug_dmi_table[] = { { .callback = sw_any_bug_found, @@ -414,6 +415,7 @@ static struct dmi_system_id sw_any_bug_dmi_table[] = { }, { } }; +#endif static int acpi_cpufreq_cpu_init ( -- cgit v0.10.2 From 914f7c31b0bea0ccf3bf474d0b99d803f7985097 Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Fri, 20 Oct 2006 14:31:00 -0700 Subject: [CPUFREQ] handle sysfs errors Signed-off-by: Jeff Garzik Signed-off-by: Andrew Morton Signed-off-by: Dave Jones diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c index c4c578d..8fe13ec 100644 --- a/drivers/cpufreq/cpufreq_conservative.c +++ b/drivers/cpufreq/cpufreq_conservative.c @@ -453,6 +453,7 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, unsigned int cpu = policy->cpu; struct cpu_dbs_info_s *this_dbs_info; unsigned int j; + int rc; this_dbs_info = &per_cpu(cpu_dbs_info, cpu); @@ -469,6 +470,13 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, break; mutex_lock(&dbs_mutex); + + rc = sysfs_create_group(&policy->kobj, &dbs_attr_group); + if (rc) { + mutex_unlock(&dbs_mutex); + return rc; + } + for_each_cpu_mask(j, policy->cpus) { struct cpu_dbs_info_s *j_dbs_info; j_dbs_info = &per_cpu(cpu_dbs_info, j); @@ -481,7 +489,7 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, this_dbs_info->enable = 1; this_dbs_info->down_skip = 0; this_dbs_info->requested_freq = policy->cur; - sysfs_create_group(&policy->kobj, &dbs_attr_group); + dbs_enable++; /* * Start the timerschedule work, when this governor diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index 291cfe9..cbde076 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -473,6 +473,7 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, unsigned int cpu = policy->cpu; struct cpu_dbs_info_s *this_dbs_info; unsigned int j; + int rc; this_dbs_info = &per_cpu(cpu_dbs_info, cpu); @@ -501,6 +502,16 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, return -ENOSPC; } } + + rc = sysfs_create_group(&policy->kobj, &dbs_attr_group); + if (rc) { + if (dbs_enable == 1) + destroy_workqueue(kondemand_wq); + dbs_enable--; + mutex_unlock(&dbs_mutex); + return rc; + } + for_each_cpu_mask(j, policy->cpus) { struct cpu_dbs_info_s *j_dbs_info; j_dbs_info = &per_cpu(cpu_dbs_info, j); @@ -510,7 +521,6 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, j_dbs_info->prev_cpu_wall = get_jiffies_64(); } this_dbs_info->enable = 1; - sysfs_create_group(&policy->kobj, &dbs_attr_group); /* * Start the timerschedule work, when this governor * is used for first time diff --git a/drivers/cpufreq/cpufreq_userspace.c b/drivers/cpufreq/cpufreq_userspace.c index a06c204..2a4eb0b 100644 --- a/drivers/cpufreq/cpufreq_userspace.c +++ b/drivers/cpufreq/cpufreq_userspace.c @@ -131,19 +131,26 @@ static int cpufreq_governor_userspace(struct cpufreq_policy *policy, unsigned int event) { unsigned int cpu = policy->cpu; + int rc = 0; + switch (event) { case CPUFREQ_GOV_START: if (!cpu_online(cpu)) return -EINVAL; BUG_ON(!policy->cur); mutex_lock(&userspace_mutex); + rc = sysfs_create_file (&policy->kobj, + &freq_attr_scaling_setspeed.attr); + if (rc) + goto start_out; + cpu_is_managed[cpu] = 1; cpu_min_freq[cpu] = policy->min; cpu_max_freq[cpu] = policy->max; cpu_cur_freq[cpu] = policy->cur; cpu_set_freq[cpu] = policy->cur; - sysfs_create_file (&policy->kobj, &freq_attr_scaling_setspeed.attr); dprintk("managing cpu %u started (%u - %u kHz, currently %u kHz)\n", cpu, cpu_min_freq[cpu], cpu_max_freq[cpu], cpu_cur_freq[cpu]); +start_out: mutex_unlock(&userspace_mutex); break; case CPUFREQ_GOV_STOP: @@ -180,7 +187,7 @@ static int cpufreq_governor_userspace(struct cpufreq_policy *policy, mutex_unlock(&userspace_mutex); break; } - return 0; + return rc; } -- cgit v0.10.2 From fe0f96020d5158b6579548666c842706ce3af371 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 20 Oct 2006 14:31:01 -0700 Subject: [CPUFREQ] speedstep-centrino: remove dead code arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c:396: warning: 'sw_any_bug_dmi_table' defined but not used Signed-off-by: Andrew Morton Signed-off-by: Dave Jones diff --git a/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c b/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c index 70178bf..f6afa23 100644 --- a/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c +++ b/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c @@ -392,7 +392,7 @@ static int sw_any_bug_found(struct dmi_system_id *d) return 0; } - +#ifdef CONFIG_SMP static struct dmi_system_id sw_any_bug_dmi_table[] = { { .callback = sw_any_bug_found, @@ -405,7 +405,7 @@ static struct dmi_system_id sw_any_bug_dmi_table[] = { }, { } }; - +#endif /* * centrino_cpu_init_acpi - register with ACPI P-States library -- cgit v0.10.2 From 8acb025085aa88c41063bfa0f2c3b4d0a3f2ef11 Mon Sep 17 00:00:00 2001 From: Holger Macht Date: Fri, 20 Oct 2006 14:30:28 -0700 Subject: ACPI: ibm_acpi: Add support for the generic backlight device Add support for the generic backlight interface below /sys/class/backlight. The patch keeps the procfs brightness handling for backward compatibility. Add two generic functions brightness_get and brightness_set to be used both by the procfs related and the sysfs related methods. [apw@shadowen.org: backlight users need to select BACKLIGHT_CLASS_DEVICE] Signed-off-by: Holger Macht Signed-off-by: Andy Whitcroft Signed-off-by: Andrew Morton Signed-off-by: Len Brown diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 0f9d4be..bc58a3b 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -200,6 +200,7 @@ config ACPI_ASUS config ACPI_IBM tristate "IBM ThinkPad Laptop Extras" depends on X86 + select BACKLIGHT_CLASS_DEVICE ---help--- This is a Linux ACPI driver for the IBM ThinkPad laptops. It adds support for Fn-Fx key combinations, Bluetooth control, video diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index 003a987..cd8722e 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -78,6 +78,7 @@ #include #include #include +#include #include #include @@ -243,6 +244,8 @@ struct ibm_struct { static struct proc_dir_entry *proc_dir = NULL; +static struct backlight_device *ibm_backlight_device; + #define onoff(status,bit) ((status) & (1 << (bit)) ? "on" : "off") #define enabled(status,bit) ((status) & (1 << (bit)) ? "enabled" : "disabled") #define strlencmp(a,b) (strncmp((a), (b), strlen(b))) @@ -1381,12 +1384,22 @@ static int ecdump_write(char *buf) static int brightness_offset = 0x31; +static int brightness_get(struct backlight_device *bd) +{ + u8 level; + if (!acpi_ec_read(brightness_offset, &level)) + return -EIO; + + level &= 0x7; + return level; +} + static int brightness_read(char *p) { int len = 0; - u8 level; + int level; - if (!acpi_ec_read(brightness_offset, &level)) { + if ((level = brightness_get(NULL)) < 0) { len += sprintf(p + len, "level:\t\tunreadable\n"); } else { len += sprintf(p + len, "level:\t\t%d\n", level & 0x7); @@ -1401,16 +1414,34 @@ static int brightness_read(char *p) #define BRIGHTNESS_UP 4 #define BRIGHTNESS_DOWN 5 -static int brightness_write(char *buf) +static int brightness_set(int value) { int cmos_cmd, inc, i; - u8 level; + int current_value = brightness_get(NULL); + + value &= 7; + + cmos_cmd = value > current_value ? BRIGHTNESS_UP : BRIGHTNESS_DOWN; + inc = value > current_value ? 1 : -1; + for (i = current_value; i != value; i += inc) { + if (!cmos_eval(cmos_cmd)) + return -EIO; + if (!acpi_ec_write(brightness_offset, i + inc)) + return -EIO; + } + + return 0; +} + +static int brightness_write(char *buf) +{ + int level; int new_level; char *cmd; while ((cmd = next_cmd(&buf))) { - if (!acpi_ec_read(brightness_offset, &level)) - return -EIO; + if ((level = brightness_get(NULL)) < 0) + return level; level &= 7; if (strlencmp(cmd, "up") == 0) { @@ -1423,19 +1454,17 @@ static int brightness_write(char *buf) } else return -EINVAL; - cmos_cmd = new_level > level ? BRIGHTNESS_UP : BRIGHTNESS_DOWN; - inc = new_level > level ? 1 : -1; - for (i = level; i != new_level; i += inc) { - if (!cmos_eval(cmos_cmd)) - return -EIO; - if (!acpi_ec_write(brightness_offset, i + inc)) - return -EIO; - } + brightness_set(new_level); } return 0; } +static int brightness_update_status(struct backlight_device *bd) +{ + return brightness_set(bd->props->brightness); +} + static int volume_offset = 0x30; static int volume_read(char *p) @@ -1963,10 +1992,20 @@ IBM_PARAM(brightness); IBM_PARAM(volume); IBM_PARAM(fan); +static struct backlight_properties ibm_backlight_data = { + .owner = THIS_MODULE, + .get_brightness = brightness_get, + .update_status = brightness_update_status, + .max_brightness = 7, +}; + static void acpi_ibm_exit(void) { int i; + if (ibm_backlight_device) + backlight_device_unregister(ibm_backlight_device); + for (i = ARRAY_SIZE(ibms) - 1; i >= 0; i--) ibm_exit(&ibms[i]); @@ -2034,6 +2073,14 @@ static int __init acpi_ibm_init(void) } } + ibm_backlight_device = backlight_device_register("ibm", NULL, + &ibm_backlight_data); + if (IS_ERR(ibm_backlight_device)) { + printk(IBM_ERR "Could not register ibm backlight device\n"); + ibm_backlight_device = NULL; + acpi_ibm_exit(); + } + return 0; } -- cgit v0.10.2 From 2039a6eb72d4b5d0dd71de5c4dff5db129848c44 Mon Sep 17 00:00:00 2001 From: Holger Macht Date: Fri, 20 Oct 2006 14:30:29 -0700 Subject: ACPI: asus_acpi: Add support for the generic backlight device Add support for the generic backlight interface below /sys/class/backlight. Keep the procfs brightness handling for backward compatibility. [apw@shadowen.org: backlight users need to select BACKLIGHT_CLASS_DEVICE] Signed-off-by: Holger Macht Signed-off-by: Andy Whitcroft Signed-off-by: Andrew Morton Signed-off-by: Len Brown diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index bc58a3b..9b64c4e 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -172,6 +172,7 @@ config ACPI_NUMA config ACPI_ASUS tristate "ASUS/Medion Laptop Extras" depends on X86 + select BACKLIGHT_CLASS_DEVICE ---help--- This driver provides support for extra features of ACPI-compatible ASUS laptops. As some of Medion laptops are made by ASUS, it may also diff --git a/drivers/acpi/asus_acpi.c b/drivers/acpi/asus_acpi.c index c7ac929..bf7bc25 100644 --- a/drivers/acpi/asus_acpi.c +++ b/drivers/acpi/asus_acpi.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -402,6 +403,8 @@ static struct model_data model_conf[END_MODEL] = { /* procdir we use */ static struct proc_dir_entry *asus_proc_dir; +static struct backlight_device *asus_backlight_device; + /* * This header is made available to allow proper configuration given model, * revision number , ... this info cannot go in struct asus_hotk because it is @@ -779,7 +782,7 @@ proc_write_lcd(struct file *file, const char __user * buffer, return rv; } -static int read_brightness(void) +static int read_brightness(struct backlight_device *bd) { int value; @@ -801,9 +804,10 @@ static int read_brightness(void) /* * Change the brightness level */ -static void set_brightness(int value) +static int set_brightness(int value) { acpi_status status = 0; + int ret = 0; /* SPLV laptop */ if (hotk->methods->brightness_set) { @@ -811,11 +815,12 @@ static void set_brightness(int value) value, NULL)) printk(KERN_WARNING "Asus ACPI: Error changing brightness\n"); - return; + ret = -EIO; + goto out; } /* No SPLV method if we are here, act as appropriate */ - value -= read_brightness(); + value -= read_brightness(NULL); while (value != 0) { status = acpi_evaluate_object(NULL, (value > 0) ? hotk->methods->brightness_up : @@ -825,15 +830,22 @@ static void set_brightness(int value) if (ACPI_FAILURE(status)) printk(KERN_WARNING "Asus ACPI: Error changing brightness\n"); + ret = -EIO; } - return; +out: + return ret; +} + +static int set_brightness_status(struct backlight_device *bd) +{ + return set_brightness(bd->props->brightness); } static int proc_read_brn(char *page, char **start, off_t off, int count, int *eof, void *data) { - return sprintf(page, "%d\n", read_brightness()); + return sprintf(page, "%d\n", read_brightness(NULL)); } static int @@ -1333,6 +1345,26 @@ static int asus_hotk_remove(struct acpi_device *device, int type) return 0; } +static struct backlight_properties asus_backlight_data = { + .owner = THIS_MODULE, + .get_brightness = read_brightness, + .update_status = set_brightness_status, + .max_brightness = 15, +}; + +static void __exit asus_acpi_exit(void) +{ + if (asus_backlight_device) + backlight_device_unregister(asus_backlight_device); + + acpi_bus_unregister_driver(&asus_hotk_driver); + remove_proc_entry(PROC_ASUS, acpi_root_dir); + + kfree(asus_info); + + return; +} + static int __init asus_acpi_init(void) { int result; @@ -1370,17 +1402,15 @@ static int __init asus_acpi_init(void) return result; } - return 0; -} - -static void __exit asus_acpi_exit(void) -{ - acpi_bus_unregister_driver(&asus_hotk_driver); - remove_proc_entry(PROC_ASUS, acpi_root_dir); - - kfree(asus_info); + asus_backlight_device = backlight_device_register("asus", NULL, + &asus_backlight_data); + if (IS_ERR(asus_backlight_device)) { + printk(KERN_ERR "Could not register asus backlight device\n"); + asus_backlight_device = NULL; + asus_acpi_exit(); + } - return; + return 0; } module_init(asus_acpi_init); -- cgit v0.10.2 From c92635572489b810d03acdf03f61bf6dd1af5433 Mon Sep 17 00:00:00 2001 From: Holger Macht Date: Fri, 20 Oct 2006 14:30:29 -0700 Subject: ACPI: toshiba_acpi: Add support for the generic backlight device Add support for the generic backlight interface below /sys/class/backlight. Keep the procfs brightness handling for backward compatibility. To achive this, add two generic functions get_lcd and set_lcd to be used both by the procfs related and the sysfs related methods. [apw@shadowen.org: backlight users need to select BACKLIGHT_CLASS_DEVICE] Signed-off-by: Holger Macht Signed-off-by: Andy Whitcroft Signed-off-by: Andrew Morton Signed-off-by: Len Brown diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 9b64c4e..59f9def 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -227,6 +227,7 @@ config ACPI_IBM_DOCK config ACPI_TOSHIBA tristate "Toshiba Laptop Extras" depends on X86 + select BACKLIGHT_CLASS_DEVICE ---help--- This driver adds support for access to certain system settings on "legacy free" Toshiba laptops. These laptops can be recognized by diff --git a/drivers/acpi/toshiba_acpi.c b/drivers/acpi/toshiba_acpi.c index 7fe0b7a..2f35f89 100644 --- a/drivers/acpi/toshiba_acpi.c +++ b/drivers/acpi/toshiba_acpi.c @@ -41,6 +41,8 @@ #include #include #include +#include + #include #include @@ -210,6 +212,7 @@ static acpi_status hci_read1(u32 reg, u32 * out1, u32 * result) } static struct proc_dir_entry *toshiba_proc_dir /*= 0*/ ; +static struct backlight_device *toshiba_backlight_device; static int force_fan; static int last_key_event; static int key_event_valid; @@ -271,14 +274,23 @@ dispatch_write(struct file *file, const char __user * buffer, return result; } -static char *read_lcd(char *p) +static int get_lcd(struct backlight_device *bd) { u32 hci_result; u32 value; hci_read1(HCI_LCD_BRIGHTNESS, &value, &hci_result); if (hci_result == HCI_SUCCESS) { - value = value >> HCI_LCD_BRIGHTNESS_SHIFT; + return (value >> HCI_LCD_BRIGHTNESS_SHIFT); + } else + return -EFAULT; +} + +static char *read_lcd(char *p) +{ + int value = get_lcd(NULL); + + if (value >= 0) { p += sprintf(p, "brightness: %d\n", value); p += sprintf(p, "brightness_levels: %d\n", HCI_LCD_BRIGHTNESS_LEVELS); @@ -289,22 +301,34 @@ static char *read_lcd(char *p) return p; } +static int set_lcd(int value) +{ + u32 hci_result; + + value = value << HCI_LCD_BRIGHTNESS_SHIFT; + hci_write1(HCI_LCD_BRIGHTNESS, value, &hci_result); + if (hci_result != HCI_SUCCESS) + return -EFAULT; + + return 0; +} + +static int set_lcd_status(struct backlight_device *bd) +{ + return set_lcd(bd->props->brightness); +} + static unsigned long write_lcd(const char *buffer, unsigned long count) { int value; - u32 hci_result; + int ret = count; if (sscanf(buffer, " brightness : %i", &value) == 1 && - value >= 0 && value < HCI_LCD_BRIGHTNESS_LEVELS) { - value = value << HCI_LCD_BRIGHTNESS_SHIFT; - hci_write1(HCI_LCD_BRIGHTNESS, value, &hci_result); - if (hci_result != HCI_SUCCESS) - return -EFAULT; - } else { - return -EINVAL; - } - - return count; + value >= 0 && value < HCI_LCD_BRIGHTNESS_LEVELS) + ret = set_lcd(value); + else + ret = -EINVAL; + return ret; } static char *read_video(char *p) @@ -506,6 +530,26 @@ static acpi_status __exit remove_device(void) return AE_OK; } +static struct backlight_properties toshiba_backlight_data = { + .owner = THIS_MODULE, + .get_brightness = get_lcd, + .update_status = set_lcd_status, + .max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1, +}; + +static void __exit toshiba_acpi_exit(void) +{ + if (toshiba_backlight_device) + backlight_device_unregister(toshiba_backlight_device); + + remove_device(); + + if (toshiba_proc_dir) + remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); + + return; +} + static int __init toshiba_acpi_init(void) { acpi_status status = AE_OK; @@ -546,17 +590,15 @@ static int __init toshiba_acpi_init(void) remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); } - return (ACPI_SUCCESS(status)) ? 0 : -ENODEV; -} - -static void __exit toshiba_acpi_exit(void) -{ - remove_device(); - - if (toshiba_proc_dir) - remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); + toshiba_backlight_device = backlight_device_register("toshiba", NULL, + &toshiba_backlight_data); + if (IS_ERR(toshiba_backlight_device)) { + printk(KERN_ERR "Could not register toshiba backlight device\n"); + toshiba_backlight_device = NULL; + toshiba_acpi_exit(); + } - return; + return (ACPI_SUCCESS(status)) ? 0 : -ENODEV; } module_init(toshiba_acpi_init); -- cgit v0.10.2 From 95625b8f19e1e030c7fe3c010407d90fa248c68f Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Sat, 21 Oct 2006 01:37:39 -0400 Subject: [CPUFREQ] ifdef more unused on !SMP code. acpi-cpufreq needs the same patch as the previous speedstep-centrino change. Additionally, the centrino driver can have its ifdef moved out a little further to eliminate some more code/variables. Signed-off-by: Dave Jones diff --git a/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c b/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c index 23f8353..60d20cf 100644 --- a/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c +++ b/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c @@ -554,6 +554,7 @@ static int acpi_cpufreq_early_init(void) return 0; } +#ifdef CONFIG_SMP /* * Some BIOSes do SW_ANY coordination internally, either set it up in hw * or do it in BIOS firmware and won't inform about it to OS. If not @@ -580,6 +581,7 @@ static struct dmi_system_id sw_any_bug_dmi_table[] = { }, { } }; +#endif static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) { diff --git a/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c b/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c index f6afa23..d2d9caf 100644 --- a/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c +++ b/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c @@ -379,6 +379,7 @@ static int centrino_cpu_early_init_acpi(void) } +#ifdef CONFIG_SMP /* * Some BIOSes do SW_ANY coordination internally, either set it up in hw * or do it in BIOS firmware and won't inform about it to OS. If not @@ -392,7 +393,6 @@ static int sw_any_bug_found(struct dmi_system_id *d) return 0; } -#ifdef CONFIG_SMP static struct dmi_system_id sw_any_bug_dmi_table[] = { { .callback = sw_any_bug_found, -- cgit v0.10.2 From c9073ce02adfa273a3d6d53eac8c4c035510ad9c Mon Sep 17 00:00:00 2001 From: Ryan Jackson Date: Fri, 20 Oct 2006 14:41:01 -0700 Subject: [MTD] MAPS: Add parameter to amd76xrom to override rom window size The 2 bits controlling the window size are often set to allow reading the BIOS, but too small to allow writing, since the lock registers are 4MiB lower in the address space than the data. This is intended to prevent flashing the bios, perhaps accidentally. The bits are 6 and 7. If both bits are set, it is a 5MiB window. If only the 7 Bit is set, it is a 4MiB window. Otherwise, it is a 64KiB window. This parameter allows the driver to override the BIOS settings. Signed-off-by: Ryan Jackson Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse diff --git a/drivers/mtd/maps/amd76xrom.c b/drivers/mtd/maps/amd76xrom.c index 797caff..78b6711 100644 --- a/drivers/mtd/maps/amd76xrom.c +++ b/drivers/mtd/maps/amd76xrom.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -44,6 +45,23 @@ struct amd76xrom_map_info { char map_name[sizeof(MOD_NAME) + 2 + ADDRESS_NAME_LEN]; }; +/* The 2 bits controlling the window size are often set to allow reading + * the BIOS, but too small to allow writing, since the lock registers are + * 4MiB lower in the address space than the data. + * + * This is intended to prevent flashing the bios, perhaps accidentally. + * + * This parameter allows the normal driver to over-ride the BIOS settings. + * + * The bits are 6 and 7. If both bits are set, it is a 5MiB window. + * If only the 7 Bit is set, it is a 4MiB window. Otherwise, a + * 64KiB window. + * + */ +static uint win_size_bits; +module_param(win_size_bits, uint, 0); +MODULE_PARM_DESC(win_size_bits, "ROM window size bits override for 0x43 byte, normally set by BIOS."); + static struct amd76xrom_window amd76xrom_window = { .maps = LIST_HEAD_INIT(amd76xrom_window.maps), }; @@ -95,6 +113,16 @@ static int __devinit amd76xrom_init_one (struct pci_dev *pdev, /* Remember the pci dev I find the window in - already have a ref */ window->pdev = pdev; + /* Enable the selected rom window. This is often incorrectly + * set up by the BIOS, and the 4MiB offset for the lock registers + * requires the full 5MiB of window space. + * + * This 'write, then read' approach leaves the bits for + * other uses of the hardware info. + */ + pci_read_config_byte(pdev, 0x43, &byte); + pci_write_config_byte(pdev, 0x43, byte | win_size_bits ); + /* Assume the rom window is properly setup, and find it's size */ pci_read_config_byte(pdev, 0x43, &byte); if ((byte & ((1<<7)|(1<<6))) == ((1<<7)|(1<<6))) { @@ -129,12 +157,6 @@ static int __devinit amd76xrom_init_one (struct pci_dev *pdev, (unsigned long long)window->rsrc.end); } -#if 0 - - /* Enable the selected rom window */ - pci_read_config_byte(pdev, 0x43, &byte); - pci_write_config_byte(pdev, 0x43, byte | rwindow->segen_bits); -#endif /* Enable writes through the rom window */ pci_read_config_byte(pdev, 0x40, &byte); -- cgit v0.10.2 From 89072ef99367cd6fab37b85d6a59a575084c2d2c Mon Sep 17 00:00:00 2001 From: Ryan Jackson Date: Fri, 20 Oct 2006 14:41:03 -0700 Subject: [MTD] CHIPS: Support for SST 49LF040B flash chip Add chip driver and JEDEC probe support for the SST 49LF040B flash chip. Signed-off-by: Ryan Jackson Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 702ae4c..ca0882b 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -48,6 +48,7 @@ #define MANUFACTURER_ATMEL 0x001F #define MANUFACTURER_SST 0x00BF #define SST49LF004B 0x0060 +#define SST49LF040B 0x0050 #define SST49LF008A 0x005a #define AT49BV6416 0x00d6 @@ -233,6 +234,7 @@ static struct cfi_fixup cfi_fixup_table[] = { }; static struct cfi_fixup jedec_fixup_table[] = { { MANUFACTURER_SST, SST49LF004B, fixup_use_fwh_lock, NULL, }, + { MANUFACTURER_SST, SST49LF040B, fixup_use_fwh_lock, NULL, }, { MANUFACTURER_SST, SST49LF008A, fixup_use_fwh_lock, NULL, }, { 0, 0, NULL, NULL } }; @@ -519,10 +521,12 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr if (mode == FL_WRITING) /* FIXME: Erase-suspend-program appears broken. */ goto sleep; - if (!(mode == FL_READY || mode == FL_POINT + if (!( mode == FL_READY + || mode == FL_POINT || !cfip || (mode == FL_WRITING && (cfip->EraseSuspend & 0x2)) - || (mode == FL_WRITING && (cfip->EraseSuspend & 0x1)))) + || (mode == FL_WRITING && (cfip->EraseSuspend & 0x1) + ))) goto sleep; /* We could check to see if we're trying to access the sector diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index 1154dac..63d1287 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c @@ -154,6 +154,7 @@ #define SST39SF010A 0x00B5 #define SST39SF020A 0x00B6 #define SST49LF004B 0x0060 +#define SST49LF040B 0x0050 #define SST49LF008A 0x005a #define SST49LF030A 0x001C #define SST49LF040A 0x0051 @@ -1401,6 +1402,20 @@ static const struct amd_flash_info jedec_table[] = { } }, { .mfr_id = MANUFACTURER_SST, + .dev_id = SST49LF040B, + .name = "SST 49LF040B", + .uaddr = { + [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ + }, + .DevSize = SIZE_512KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x01000,128), + } + }, { + + .mfr_id = MANUFACTURER_SST, .dev_id = SST49LF004B, .name = "SST 49LF004B", .uaddr = { -- cgit v0.10.2 From 29175778b07aa60e7f8030bd95d69f70070cc1f7 Mon Sep 17 00:00:00 2001 From: Lew Glendenning Date: Fri, 20 Oct 2006 14:41:04 -0700 Subject: [MTD] MAPS: Support for BIOS flash chips on Intel ESB2 southbridge Add MTD map driver for BIOS flash chips connected to the Intel ESB2 southbridge. [akpm@osdl.org: coding-style fixes, build fix] Signed-off-by: Ryan Jackson Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 24747bd..7514a9b 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -184,6 +184,15 @@ config MTD_ICHXROM BE VERY CAREFUL. +config MTD_ESB2ROM + tristate "BIOS flash chip on Intel ESB Controller Hub 2" + depends on X86 && MTD_JEDECPROBE + help + Support for treating the BIOS flash chip on ESB2 motherboards + as an MTD device - with this you can reprogram your BIOS. + + BE VERY CAREFUL. + config MTD_SCB2_FLASH tristate "BIOS flash chip on Intel SCB2 boards" depends on X86 && MTD_JEDECPROBE diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 191c192..9061432 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_MTD_DC21285) += dc21285.o obj-$(CONFIG_MTD_DILNETPC) += dilnetpc.o obj-$(CONFIG_MTD_L440GX) += l440gx.o obj-$(CONFIG_MTD_AMD76XROM) += amd76xrom.o +obj-$(CONFIG_MTD_ESB2ROM) += esb2rom.o obj-$(CONFIG_MTD_ICHXROM) += ichxrom.o obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o obj-$(CONFIG_MTD_LUBBOCK) += lubbock-flash.o diff --git a/drivers/mtd/maps/esb2rom.c b/drivers/mtd/maps/esb2rom.c new file mode 100644 index 0000000..e1c7814 --- /dev/null +++ b/drivers/mtd/maps/esb2rom.c @@ -0,0 +1,449 @@ +/* + * esb2rom.c + * + * Normal mappings of flash chips in physical memory + * through the Intel ESB2 Southbridge. + * + * This was derived from ichxrom.c in May 2006 by + * Lew Glendenning + * + * Eric Biederman, of course, was a major help in this effort. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MOD_NAME KBUILD_BASENAME + +#define ADDRESS_NAME_LEN 18 + +#define ROM_PROBE_STEP_SIZE (64*1024) /* 64KiB */ + +#define BIOS_CNTL 0xDC +#define BIOS_LOCK_ENABLE 0x02 +#define BIOS_WRITE_ENABLE 0x01 + +/* This became a 16-bit register, and EN2 has disappeared */ +#define FWH_DEC_EN1 0xD8 +#define FWH_F8_EN 0x8000 +#define FWH_F0_EN 0x4000 +#define FWH_E8_EN 0x2000 +#define FWH_E0_EN 0x1000 +#define FWH_D8_EN 0x0800 +#define FWH_D0_EN 0x0400 +#define FWH_C8_EN 0x0200 +#define FWH_C0_EN 0x0100 +#define FWH_LEGACY_F_EN 0x0080 +#define FWH_LEGACY_E_EN 0x0040 +/* reserved 0x0020 and 0x0010 */ +#define FWH_70_EN 0x0008 +#define FWH_60_EN 0x0004 +#define FWH_50_EN 0x0002 +#define FWH_40_EN 0x0001 + +/* these are 32-bit values */ +#define FWH_SEL1 0xD0 +#define FWH_SEL2 0xD4 + +#define FWH_8MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \ + FWH_D8_EN | FWH_D0_EN | FWH_C8_EN | FWH_C0_EN | \ + FWH_70_EN | FWH_60_EN | FWH_50_EN | FWH_40_EN) + +#define FWH_7MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \ + FWH_D8_EN | FWH_D0_EN | FWH_C8_EN | FWH_C0_EN | \ + FWH_70_EN | FWH_60_EN | FWH_50_EN) + +#define FWH_6MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \ + FWH_D8_EN | FWH_D0_EN | FWH_C8_EN | FWH_C0_EN | \ + FWH_70_EN | FWH_60_EN) + +#define FWH_5MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \ + FWH_D8_EN | FWH_D0_EN | FWH_C8_EN | FWH_C0_EN | \ + FWH_70_EN) + +#define FWH_4MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \ + FWH_D8_EN | FWH_D0_EN | FWH_C8_EN | FWH_C0_EN) + +#define FWH_3_5MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \ + FWH_D8_EN | FWH_D0_EN | FWH_C8_EN) + +#define FWH_3MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \ + FWH_D8_EN | FWH_D0_EN) + +#define FWH_2_5MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \ + FWH_D8_EN) + +#define FWH_2MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN) + +#define FWH_1_5MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN) + +#define FWH_1MiB (FWH_F8_EN | FWH_F0_EN) + +#define FWH_0_5MiB (FWH_F8_EN) + + +struct esb2rom_window { + void __iomem* virt; + unsigned long phys; + unsigned long size; + struct list_head maps; + struct resource rsrc; + struct pci_dev *pdev; +}; + +struct esb2rom_map_info { + struct list_head list; + struct map_info map; + struct mtd_info *mtd; + struct resource rsrc; + char map_name[sizeof(MOD_NAME) + 2 + ADDRESS_NAME_LEN]; +}; + +static struct esb2rom_window esb2rom_window = { + .maps = LIST_HEAD_INIT(esb2rom_window.maps), +}; + +static void esb2rom_cleanup(struct esb2rom_window *window) +{ + struct esb2rom_map_info *map, *scratch; + u8 byte; + + /* Disable writes through the rom window */ + pci_read_config_byte(window->pdev, BIOS_CNTL, &byte); + pci_write_config_byte(window->pdev, BIOS_CNTL, + byte & ~BIOS_WRITE_ENABLE); + + /* Free all of the mtd devices */ + list_for_each_entry_safe(map, scratch, &window->maps, list) { + if (map->rsrc.parent) + release_resource(&map->rsrc); + del_mtd_device(map->mtd); + map_destroy(map->mtd); + list_del(&map->list); + kfree(map); + } + if (window->rsrc.parent) + release_resource(&window->rsrc); + if (window->virt) { + iounmap(window->virt); + window->virt = NULL; + window->phys = 0; + window->size = 0; + window->pdev = NULL; + } +} + +static int __devinit esb2rom_init_one(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL }; + struct esb2rom_window *window = &esb2rom_window; + struct esb2rom_map_info *map = NULL; + unsigned long map_top; + u8 byte; + u16 word; + + /* For now I just handle the ecb2 and I assume there + * are not a lot of resources up at the top of the address + * space. It is possible to handle other devices in the + * top 16MiB but it is very painful. Also since + * you can only really attach a FWH to an ICHX there + * a number of simplifications you can make. + * + * Also you can page firmware hubs if an 8MiB window isn't enough + * but don't currently handle that case either. + */ + window->pdev = pdev; + + /* RLG: experiment 2. Force the window registers to the widest values */ + +/* + pci_read_config_word(pdev, FWH_DEC_EN1, &word); + printk(KERN_DEBUG "Original FWH_DEC_EN1 : %x\n", word); + pci_write_config_byte(pdev, FWH_DEC_EN1, 0xff); + pci_read_config_byte(pdev, FWH_DEC_EN1, &byte); + printk(KERN_DEBUG "New FWH_DEC_EN1 : %x\n", byte); + + pci_read_config_byte(pdev, FWH_DEC_EN2, &byte); + printk(KERN_DEBUG "Original FWH_DEC_EN2 : %x\n", byte); + pci_write_config_byte(pdev, FWH_DEC_EN2, 0x0f); + pci_read_config_byte(pdev, FWH_DEC_EN2, &byte); + printk(KERN_DEBUG "New FWH_DEC_EN2 : %x\n", byte); +*/ + + /* Find a region continuous to the end of the ROM window */ + window->phys = 0; + pci_read_config_word(pdev, FWH_DEC_EN1, &word); + printk(KERN_DEBUG "pci_read_config_byte : %x\n", word); + + if ((word & FWH_8MiB) == FWH_8MiB) + window->phys = 0xff400000; + else if ((word & FWH_7MiB) == FWH_7MiB) + window->phys = 0xff500000; + else if ((word & FWH_6MiB) == FWH_6MiB) + window->phys = 0xff600000; + else if ((word & FWH_5MiB) == FWH_5MiB) + window->phys = 0xFF700000; + else if ((word & FWH_4MiB) == FWH_4MiB) + window->phys = 0xffc00000; + else if ((word & FWH_3_5MiB) == FWH_3_5MiB) + window->phys = 0xffc80000; + else if ((word & FWH_3MiB) == FWH_3MiB) + window->phys = 0xffd00000; + else if ((word & FWH_2_5MiB) == FWH_2_5MiB) + window->phys = 0xffd80000; + else if ((word & FWH_2MiB) == FWH_2MiB) + window->phys = 0xffe00000; + else if ((word & FWH_1_5MiB) == FWH_1_5MiB) + window->phys = 0xffe80000; + else if ((word & FWH_1MiB) == FWH_1MiB) + window->phys = 0xfff00000; + else if ((word & FWH_0_5MiB) == FWH_0_5MiB) + window->phys = 0xfff80000; + + /* reserved 0x0020 and 0x0010 */ + window->phys -= 0x400000UL; + window->size = (0xffffffffUL - window->phys) + 1UL; + + /* Enable writes through the rom window */ + pci_read_config_byte(pdev, BIOS_CNTL, &byte); + if (!(byte & BIOS_WRITE_ENABLE) && (byte & (BIOS_LOCK_ENABLE))) { + /* The BIOS will generate an error if I enable + * this device, so don't even try. + */ + printk(KERN_ERR MOD_NAME ": firmware access control, I can't enable writes\n"); + goto out; + } + pci_write_config_byte(pdev, BIOS_CNTL, byte | BIOS_WRITE_ENABLE); + + /* + * Try to reserve the window mem region. If this fails then + * it is likely due to the window being "reseved" by the BIOS. + */ + window->rsrc.name = MOD_NAME; + window->rsrc.start = window->phys; + window->rsrc.end = window->phys + window->size - 1; + window->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; + if (request_resource(&iomem_resource, &window->rsrc)) { + window->rsrc.parent = NULL; + printk(KERN_DEBUG MOD_NAME + ": %s(): Unable to register resource" + " 0x%.08llx-0x%.08llx - kernel bug?\n", + __func__, + (unsigned long long)window->rsrc.start, + (unsigned long long)window->rsrc.end); + } + + /* Map the firmware hub into my address space. */ + window->virt = ioremap_nocache(window->phys, window->size); + if (!window->virt) { + printk(KERN_ERR MOD_NAME ": ioremap(%08lx, %08lx) failed\n", + window->phys, window->size); + goto out; + } + + /* Get the first address to look for an rom chip at */ + map_top = window->phys; + if ((window->phys & 0x3fffff) != 0) { + /* if not aligned on 4MiB, look 4MiB lower in address space */ + map_top = window->phys + 0x400000; + } +#if 1 + /* The probe sequence run over the firmware hub lock + * registers sets them to 0x7 (no access). + * (Insane hardware design, but most copied Intel's.) + * ==> Probe at most the last 4M of the address space. + */ + if (map_top < 0xffc00000) + map_top = 0xffc00000; +#endif + /* Loop through and look for rom chips */ + while ((map_top - 1) < 0xffffffffUL) { + struct cfi_private *cfi; + unsigned long offset; + int i; + + if (!map) + map = kmalloc(sizeof(*map), GFP_KERNEL); + if (!map) { + printk(KERN_ERR MOD_NAME ": kmalloc failed"); + goto out; + } + memset(map, 0, sizeof(*map)); + INIT_LIST_HEAD(&map->list); + map->map.name = map->map_name; + map->map.phys = map_top; + offset = map_top - window->phys; + map->map.virt = (void __iomem *) + (((unsigned long)(window->virt)) + offset); + map->map.size = 0xffffffffUL - map_top + 1UL; + /* Set the name of the map to the address I am trying */ + sprintf(map->map_name, "%s @%08lx", + MOD_NAME, map->map.phys); + + /* Firmware hubs only use vpp when being programmed + * in a factory setting. So in-place programming + * needs to use a different method. + */ + for(map->map.bankwidth = 32; map->map.bankwidth; + map->map.bankwidth >>= 1) { + char **probe_type; + /* Skip bankwidths that are not supported */ + if (!map_bankwidth_supported(map->map.bankwidth)) + continue; + + /* Setup the map methods */ + simple_map_init(&map->map); + + /* Try all of the probe methods */ + probe_type = rom_probe_types; + for(; *probe_type; probe_type++) { + map->mtd = do_map_probe(*probe_type, &map->map); + if (map->mtd) + goto found; + } + } + map_top += ROM_PROBE_STEP_SIZE; + continue; + found: + /* Trim the size if we are larger than the map */ + if (map->mtd->size > map->map.size) { + printk(KERN_WARNING MOD_NAME + " rom(%u) larger than window(%lu). fixing...\n", + map->mtd->size, map->map.size); + map->mtd->size = map->map.size; + } + if (window->rsrc.parent) { + /* + * Registering the MTD device in iomem may not be possible + * if there is a BIOS "reserved" and BUSY range. If this + * fails then continue anyway. + */ + map->rsrc.name = map->map_name; + map->rsrc.start = map->map.phys; + map->rsrc.end = map->map.phys + map->mtd->size - 1; + map->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; + if (request_resource(&window->rsrc, &map->rsrc)) { + printk(KERN_ERR MOD_NAME + ": cannot reserve MTD resource\n"); + map->rsrc.parent = NULL; + } + } + + /* Make the whole region visible in the map */ + map->map.virt = window->virt; + map->map.phys = window->phys; + cfi = map->map.fldrv_priv; + for(i = 0; i < cfi->numchips; i++) + cfi->chips[i].start += offset; + + /* Now that the mtd devices is complete claim and export it */ + map->mtd->owner = THIS_MODULE; + if (add_mtd_device(map->mtd)) { + map_destroy(map->mtd); + map->mtd = NULL; + goto out; + } + + /* Calculate the new value of map_top */ + map_top += map->mtd->size; + + /* File away the map structure */ + list_add(&map->list, &window->maps); + map = NULL; + } + + out: + /* Free any left over map structures */ + kfree(map); + + /* See if I have any map structures */ + if (list_empty(&window->maps)) { + esb2rom_cleanup(window); + return -ENODEV; + } + return 0; +} + +static void __devexit esb2rom_remove_one (struct pci_dev *pdev) +{ + struct esb2rom_window *window = &esb2rom_window; + esb2rom_cleanup(window); +} + +static struct pci_device_id esb2rom_pci_tbl[] __devinitdata = { + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, + PCI_ANY_ID, PCI_ANY_ID, }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0, + PCI_ANY_ID, PCI_ANY_ID, }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0, + PCI_ANY_ID, PCI_ANY_ID, }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0, + PCI_ANY_ID, PCI_ANY_ID, }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_1, + PCI_ANY_ID, PCI_ANY_ID, }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB2_0, + PCI_ANY_ID, PCI_ANY_ID, }, + { 0, }, +}; + +#if 0 +MODULE_DEVICE_TABLE(pci, esb2rom_pci_tbl); + +static struct pci_driver esb2rom_driver = { + .name = MOD_NAME, + .id_table = esb2rom_pci_tbl, + .probe = esb2rom_init_one, + .remove = esb2rom_remove_one, +}; +#endif + +static int __init init_esb2rom(void) +{ + struct pci_dev *pdev; + struct pci_device_id *id; + int retVal; + + pdev = NULL; + for (id = esb2rom_pci_tbl; id->vendor; id++) { + printk(KERN_DEBUG "device id = %x\n", id->device); + pdev = pci_find_device(id->vendor, id->device, NULL); + if (pdev) { + printk(KERN_DEBUG "matched device = %x\n", id->device); + break; + } + } + if (pdev) { + printk(KERN_DEBUG "matched device id %x\n", id->device); + retVal = esb2rom_init_one(pdev, &esb2rom_pci_tbl[0]); + printk(KERN_DEBUG "retVal = %d\n", retVal); + return retVal; + } + return -ENXIO; +#if 0 + return pci_register_driver(&esb2rom_driver); +#endif +} + +static void __exit cleanup_esb2rom(void) +{ + esb2rom_remove_one(esb2rom_window.pdev); +} + +module_init(init_esb2rom); +module_exit(cleanup_esb2rom); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Lew Glendenning "); +MODULE_DESCRIPTION("MTD map driver for BIOS chips on the ESB2 southbridge"); -- cgit v0.10.2 From ea7415cca922389b4f3c0cf75e0af9fbf827880e Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Fri, 20 Oct 2006 14:41:05 -0700 Subject: [JFFS2] Use rb_first() and rb_last() cleanup Use rb_first() and rb_last() to implement frag_first() and frag_last(). Signed-off-by: Akinbou Mita Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse diff --git a/fs/jffs2/nodelist.h b/fs/jffs2/nodelist.h index 0ddfd70..4178b4b5 100644 --- a/fs/jffs2/nodelist.h +++ b/fs/jffs2/nodelist.h @@ -294,23 +294,21 @@ static inline int jffs2_encode_dev(union jffs2_device_node *jdev, dev_t rdev) static inline struct jffs2_node_frag *frag_first(struct rb_root *root) { - struct rb_node *node = root->rb_node; + struct rb_node *node = rb_first(root); if (!node) return NULL; - while(node->rb_left) - node = node->rb_left; + return rb_entry(node, struct jffs2_node_frag, rb); } static inline struct jffs2_node_frag *frag_last(struct rb_root *root) { - struct rb_node *node = root->rb_node; + struct rb_node *node = rb_last(root); if (!node) return NULL; - while(node->rb_right) - node = node->rb_right; + return rb_entry(node, struct jffs2_node_frag, rb); } -- cgit v0.10.2 From f33686b5a79674bec0e1aa553d420485e3a12899 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Fri, 20 Oct 2006 14:41:05 -0700 Subject: [MTD] JEDEC probe: fix comment typo (devic) Signed-off-by: Alexey Dobriyan Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index 63d1287..58e561e 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c @@ -1889,7 +1889,7 @@ static int cfi_jedec_setup(struct cfi_private *p_cfi, int index) /* - * There is a BIG problem properly ID'ing the JEDEC devic and guaranteeing + * There is a BIG problem properly ID'ing the JEDEC device and guaranteeing * the mapped address, unlock addresses, and proper chip ID. This function * attempts to minimize errors. It is doubtfull that this probe will ever * be perfect - consequently there should be some module parameters that -- cgit v0.10.2 From c7438d02b384e82261e28fc280167f4e7a65e822 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 20 Oct 2006 14:41:06 -0700 Subject: [MTD] MAPS: esb2rom: use hotplug safe interfaces Fairly self explanatory. Keep a reference initially, drop it when we free up the driver resources. Signed-off-by: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse diff --git a/drivers/mtd/maps/esb2rom.c b/drivers/mtd/maps/esb2rom.c index e1c7814..a9d808a 100644 --- a/drivers/mtd/maps/esb2rom.c +++ b/drivers/mtd/maps/esb2rom.c @@ -140,8 +140,8 @@ static void esb2rom_cleanup(struct esb2rom_window *window) window->virt = NULL; window->phys = 0; window->size = 0; - window->pdev = NULL; } + pci_dev_put(window->pdev); } static int __devinit esb2rom_init_one(struct pci_dev *pdev, @@ -164,7 +164,7 @@ static int __devinit esb2rom_init_one(struct pci_dev *pdev, * Also you can page firmware hubs if an 8MiB window isn't enough * but don't currently handle that case either. */ - window->pdev = pdev; + window->pdev = pci_dev_get(pdev); /* RLG: experiment 2. Force the window registers to the widest values */ @@ -418,7 +418,7 @@ static int __init init_esb2rom(void) pdev = NULL; for (id = esb2rom_pci_tbl; id->vendor; id++) { printk(KERN_DEBUG "device id = %x\n", id->device); - pdev = pci_find_device(id->vendor, id->device, NULL); + pdev = pci_get_device(id->vendor, id->device, NULL); if (pdev) { printk(KERN_DEBUG "matched device = %x\n", id->device); break; @@ -427,6 +427,7 @@ static int __init init_esb2rom(void) if (pdev) { printk(KERN_DEBUG "matched device id %x\n", id->device); retVal = esb2rom_init_one(pdev, &esb2rom_pci_tbl[0]); + pci_dev_put(pdev); printk(KERN_DEBUG "retVal = %d\n", retVal); return retVal; } -- cgit v0.10.2 From c7cf0c68ea4e2020db7204eb0ed4412b2cbb167d Mon Sep 17 00:00:00 2001 From: Qi Yong Date: Mon, 16 Oct 2006 19:42:23 +0800 Subject: [JFFS2] Fix jffs2_follow_link() typo typo fix: noticed this typo while reading the patch "jffs2: fix symlink error handling" Signed-off-by: David Woodhouse diff --git a/fs/jffs2/symlink.c b/fs/jffs2/symlink.c index fc211b6..b90d5aa 100644 --- a/fs/jffs2/symlink.c +++ b/fs/jffs2/symlink.c @@ -51,7 +51,7 @@ static void *jffs2_follow_link(struct dentry *dentry, struct nameidata *nd) */ if (!p) { - printk(KERN_ERR "jffs2_follow_link(): can't find symlink taerget\n"); + printk(KERN_ERR "jffs2_follow_link(): can't find symlink target\n"); p = ERR_PTR(-EIO); } D1(printk(KERN_DEBUG "jffs2_follow_link(): target path is '%s'\n", (char *) f->target)); -- cgit v0.10.2 From 42cb1403af8a755b3dfebeb9d2a5f73bc48832a1 Mon Sep 17 00:00:00 2001 From: Andrew Victor Date: Thu, 19 Oct 2006 18:24:35 +0200 Subject: [MTD] NAND: AT91 NAND driver This version only differs from version posted by Savin Zlobec (20 Jun 2006) in that the AT91RM9200-specific chip-select / bus setup code has been moved from the at91_nand.c driver into the processor-specific file. From: Savin Zlobec Signed-off-by: Andrew Victor Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 1831340..b4b1656 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -232,6 +232,13 @@ config MTD_NAND_CS553X If you say "m", the module will be called "cs553x_nand.ko". +config MTD_NAND_AT91 + bool "Support for NAND Flash / SmartMedia on AT91" + depends on MTD_NAND && ARCH_AT91 + help + Enables support for NAND Flash / Smart Media Card interface + on Atmel AT91 processors. + config MTD_NAND_NANDSIM tristate "Support for NAND Flash Simulator" depends on MTD_NAND && MTD_PARTITIONS diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index f747593..27c9f0a 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -22,5 +22,6 @@ obj-$(CONFIG_MTD_NAND_TS7250) += ts7250.o obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o +obj-$(CONFIG_MTD_NAND_AT91) += at91_nand.o nand-objs = nand_base.o nand_bbt.o diff --git a/drivers/mtd/nand/at91_nand.c b/drivers/mtd/nand/at91_nand.c new file mode 100644 index 0000000..a58ed37 --- /dev/null +++ b/drivers/mtd/nand/at91_nand.c @@ -0,0 +1,220 @@ +/* + * drivers/mtd/nand/at91_nand.c + * + * Copyright (C) 2003 Rick Bronson + * + * Derived from drivers/mtd/nand/autcpu12.c + * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) + * + * Derived from drivers/mtd/spia.c + * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +struct at91_nand_host { + struct nand_chip nand_chip; + struct mtd_info mtd; + void __iomem *io_base; + struct at91_nand_data *board; +}; + +/* + * Hardware specific access to control-lines + */ +static void at91_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + struct nand_chip *nand_chip = mtd->priv; + struct at91_nand_host *host = nand_chip->priv; + + if (cmd == NAND_CMD_NONE) + return; + + if (ctrl & NAND_CLE) + writeb(cmd, host->io_base + (1 << host->board->cle)); + else + writeb(cmd, host->io_base + (1 << host->board->ale)); +} + +/* + * Read the Device Ready pin. + */ +static int at91_nand_device_ready(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd->priv; + struct at91_nand_host *host = nand_chip->priv; + + return at91_get_gpio_value(host->board->rdy_pin); +} + +/* + * Enable NAND. + */ +static void at91_nand_enable(struct at91_nand_host *host) +{ + if (host->board->enable_pin) + at91_set_gpio_value(host->board->enable_pin, 0); +} + +/* + * Disable NAND. + */ +static void at91_nand_disable(struct at91_nand_host *host) +{ + if (host->board->enable_pin) + at91_set_gpio_value(host->board->enable_pin, 1); +} + +/* + * Probe for the NAND device. + */ +static int __init at91_nand_probe(struct platform_device *pdev) +{ + struct at91_nand_host *host; + struct mtd_info *mtd; + struct nand_chip *nand_chip; + int res; + +#ifdef CONFIG_MTD_PARTITIONS + struct mtd_partition *partitions = NULL; + int num_partitions = 0; +#endif + + /* Allocate memory for the device structure (and zero it) */ + host = kzalloc(sizeof(struct at91_nand_host), GFP_KERNEL); + if (!host) { + printk(KERN_ERR "at91_nand: failed to allocate device structure.\n"); + return -ENOMEM; + } + + host->io_base = ioremap(pdev->resource[0].start, + pdev->resource[0].end - pdev->resource[0].start + 1); + if (host->io_base == NULL) { + printk(KERN_ERR "at91_nand: ioremap failed\n"); + kfree(host); + return -EIO; + } + + mtd = &host->mtd; + nand_chip = &host->nand_chip; + host->board = pdev->dev.platform_data; + + nand_chip->priv = host; /* link the private data structures */ + mtd->priv = nand_chip; + mtd->owner = THIS_MODULE; + + /* Set address of NAND IO lines */ + nand_chip->IO_ADDR_R = host->io_base; + nand_chip->IO_ADDR_W = host->io_base; + nand_chip->cmd_ctrl = at91_nand_cmd_ctrl; + nand_chip->dev_ready = at91_nand_device_ready; + nand_chip->ecc.mode = NAND_ECC_SOFT; /* enable ECC */ + nand_chip->chip_delay = 20; /* 20us command delay time */ + + platform_set_drvdata(pdev, host); + at91_nand_enable(host); + + if (host->board->det_pin) { + if (at91_get_gpio_value(host->board->det_pin)) { + printk ("No SmartMedia card inserted.\n"); + res = ENXIO; + goto out; + } + } + + /* Scan to find existance of the device */ + if (nand_scan(mtd, 1)) { + res = -ENXIO; + goto out; + } + +#ifdef CONFIG_MTD_PARTITIONS + if (host->board->partition_info) + partitions = host->board->partition_info(mtd->size, &num_partitions); + + if ((!partitions) || (num_partitions == 0)) { + printk(KERN_ERR "at91_nand: No parititions defined, or unsupported device.\n"); + res = ENXIO; + goto release; + } + + res = add_mtd_partitions(mtd, partitions, num_partitions); +#else + res = add_mtd_device(mtd); +#endif + + if (!res) + return res; + +release: + nand_release(mtd); +out: + at91_nand_disable(host); + platform_set_drvdata(pdev, NULL); + iounmap(host->io_base); + kfree(host); + return res; +} + +/* + * Remove a NAND device. + */ +static int __devexit at91_nand_remove(struct platform_device *pdev) +{ + struct at91_nand_host *host = platform_get_drvdata(pdev); + struct mtd_info *mtd = &host->mtd; + + nand_release(mtd); + + at91_nand_disable(host); + + iounmap(host->io_base); + kfree(host); + + return 0; +} + +static struct platform_driver at91_nand_driver = { + .probe = at91_nand_probe, + .remove = at91_nand_remove, + .driver = { + .name = "at91_nand", + .owner = THIS_MODULE, + }, +}; + +static int __init at91_nand_init(void) +{ + return platform_driver_register(&at91_nand_driver); +} + + +static void __exit at91_nand_exit(void) +{ + platform_driver_unregister(&at91_nand_driver); +} + + +module_init(at91_nand_init); +module_exit(at91_nand_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Rick Bronson"); +MODULE_DESCRIPTION("NAND/SmartMedia driver for AT91RM9200"); -- cgit v0.10.2 From d25ade71ef80e6312b3e0b53583db518ebb11798 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricard=20Wanderl=C3=B6f?= Date: Tue, 17 Oct 2006 17:27:11 +0200 Subject: [MTD] mtdchar: Fix MEMGETOOBSEL and ECCGETLAYOUT ioctls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. The ECCGETLAYOUT ioctl copy_to_user() call has a superfluous '&' causing the resulting information to be garbage rather than the intended mtd->ecclayout. 2. The MEMGETOOBSEL misses copying mtd->ecclayout->eccbytes so the resulting field of the returned structure contains garbage. Signed-off-by: Ricard Wanderlöf Signed-off-by: David Woodhouse diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 5b6acfc..866c8e0 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -616,6 +616,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, memcpy(&oi.eccpos, mtd->ecclayout->eccpos, sizeof(oi.eccpos)); memcpy(&oi.oobfree, mtd->ecclayout->oobfree, sizeof(oi.oobfree)); + oi.eccbytes = mtd->ecclayout->eccbytes; if (copy_to_user(argp, &oi, sizeof(struct nand_oobinfo))) return -EFAULT; @@ -715,7 +716,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, if (!mtd->ecclayout) return -EOPNOTSUPP; - if (copy_to_user(argp, &mtd->ecclayout, + if (copy_to_user(argp, mtd->ecclayout, sizeof(struct nand_ecclayout))) return -EFAULT; break; -- cgit v0.10.2 From 6652018c829c26d6ab0524c5c74f70daa5ed478d Mon Sep 17 00:00:00 2001 From: Yoichi Yuasa Date: Thu, 12 Oct 2006 17:38:15 +0900 Subject: [MTD] MAPS: Remove ITE 8172G and Globespan IVR MTD support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch has removed ITE 8172G and Globespan IVR MTD support. These boards support have already been removed. Signed-off-by: Yoichi Yuasa Acked-by: Ralf Bächle Signed-off-by: David Woodhouse diff --git a/drivers/mtd/maps/cstm_mips_ixx.c b/drivers/mtd/maps/cstm_mips_ixx.c index df2c38e..d57eba2 100644 --- a/drivers/mtd/maps/cstm_mips_ixx.c +++ b/drivers/mtd/maps/cstm_mips_ixx.c @@ -40,62 +40,6 @@ #include #include -#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) -#define CC_GCR 0xB4013818 -#define CC_GPBCR 0xB401380A -#define CC_GPBDR 0xB4013808 -#define CC_M68K_DEVICE 1 -#define CC_M68K_FUNCTION 6 -#define CC_CONFADDR 0xB8004000 -#define CC_CONFDATA 0xB8004004 -#define CC_FC_FCR 0xB8002004 -#define CC_FC_DCR 0xB8002008 -#define CC_GPACR 0xB4013802 -#define CC_GPAICR 0xB4013804 -#endif /* defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) */ - -#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) -void cstm_mips_ixx_set_vpp(struct map_info *map,int vpp) -{ - static DEFINE_SPINLOCK(vpp_lock); - static int vpp_count = 0; - unsigned long flags; - - spin_lock_irqsave(&vpp_lock, flags); - - if (vpp) { - if (!vpp_count++) { - __u16 data; - __u8 data1; - static u8 first = 1; - - // Set GPIO port B pin3 to high - data = *(__u16 *)(CC_GPBCR); - data = (data & 0xff0f) | 0x0040; - *(__u16 *)CC_GPBCR = data; - *(__u8 *)CC_GPBDR = (*(__u8*)CC_GPBDR) | 0x08; - if (first) { - first = 0; - /* need to have this delay for first - enabling vpp after powerup */ - udelay(40); - } - } - } else { - if (!--vpp_count) { - __u16 data; - - // Set GPIO port B pin3 to high - data = *(__u16 *)(CC_GPBCR); - data = (data & 0xff3f) | 0x0040; - *(__u16 *)CC_GPBCR = data; - *(__u8 *)CC_GPBDR = (*(__u8*)CC_GPBDR) & 0xf7; - } - } - spin_unlock_irqrestore(&vpp_lock, flags); -} -#endif - /* board and partition description */ #define MAX_PHYSMAP_PARTITIONS 8 @@ -107,29 +51,6 @@ struct cstm_mips_ixx_info { int num_partitions; }; -#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) -#define PHYSMAP_NUMBER 1 // number of board desc structs needed, one per contiguous flash type -const struct cstm_mips_ixx_info cstm_mips_ixx_board_desc[PHYSMAP_NUMBER] = -{ - { // 28F128J3A in 2x16 configuration - "big flash", // name - 0x08000000, // window_addr - 0x02000000, // window_size - 4, // bankwidth - 1, // num_partitions - } - -}; -static struct mtd_partition cstm_mips_ixx_partitions[PHYSMAP_NUMBER][MAX_PHYSMAP_PARTITIONS] = { -{ // 28F128J3A in 2x16 configuration - { - .name = "main partition ", - .size = 0x02000000, // 128 x 2 x 128k byte sectors - .offset = 0, - }, -}, -}; -#else /* defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) */ #define PHYSMAP_NUMBER 1 // number of board desc structs needed, one per contiguous flash type const struct cstm_mips_ixx_info cstm_mips_ixx_board_desc[PHYSMAP_NUMBER] = { @@ -151,7 +72,6 @@ static struct mtd_partition cstm_mips_ixx_partitions[PHYSMAP_NUMBER][MAX_PHYSMAP }, }, }; -#endif /* defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) */ struct map_info cstm_mips_ixx_map[PHYSMAP_NUMBER]; @@ -184,17 +104,10 @@ int __init init_cstm_mips_ixx(void) cstm_mips_ixx_map[i].name = cstm_mips_ixx_board_desc[i].name; cstm_mips_ixx_map[i].size = cstm_mips_ixx_board_desc[i].window_size; cstm_mips_ixx_map[i].bankwidth = cstm_mips_ixx_board_desc[i].bankwidth; -#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) - cstm_mips_ixx_map[i].set_vpp = cstm_mips_ixx_set_vpp; -#endif simple_map_init(&cstm_mips_ixx_map[i]); //printk(KERN_NOTICE "cstm_mips_ixx: ioremap is %x\n",(unsigned int)(cstm_mips_ixx_map[i].virt)); } -#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) - setup_ITE_IVR_flash(); -#endif /* defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) */ - for (i=0;i> 8 >>1; // Bug: we must shift one more bit - - /* need to set ITE flash to 32 bits instead of default 8 */ -#ifdef CONFIG_MIPS_IVR - *(__u32 *)CC_FC_FCR = 0x55; - *(__u32 *)CC_GPACR = 0xfffc; -#else - *(__u32 *)CC_FC_FCR = 0x77; -#endif - /* turn bursting off */ - *(__u32 *)CC_FC_DCR = 0x0; - - /* setup for one chip 4 byte PCI access */ - PCISetULongByOffset(CC_M68K_DEVICE, CC_M68K_FUNCTION, 0x60, size | base); - PCISetULongByOffset(CC_M68K_DEVICE, CC_M68K_FUNCTION, 0x64, 0x02); -} -#endif /* defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) */ module_init(init_cstm_mips_ixx); module_exit(cleanup_cstm_mips_ixx); @@ -280,4 +161,4 @@ module_exit(cleanup_cstm_mips_ixx); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Alice Hennessy "); -MODULE_DESCRIPTION("MTD map driver for ITE 8172G and Globespan IVR boards"); +MODULE_DESCRIPTION("MTD map driver for MIPS boards"); -- cgit v0.10.2 From 47e37743381823a5c2d51ef88cfc3d6cff1f8580 Mon Sep 17 00:00:00 2001 From: Vijay Kumar Date: Sun, 8 Oct 2006 22:00:37 +0530 Subject: [MTD] NAND: nandsim page-wise allocation (1/2) This patch removes code that does chip mapping. The chip mapping code is no longer used. Signed-off-by: Vijay Kumar Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index 545ff25..5dd3c4e 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -37,10 +37,6 @@ #include #include #include -#ifdef CONFIG_NS_ABS_POS -#include -#endif - /* Default simulator parameters values */ #if !defined(CONFIG_NANDSIM_FIRST_ID_BYTE) || \ @@ -440,14 +436,6 @@ init_nandsim(struct mtd_info *mtd) printk("options: %#x\n", ns->options); /* Map / allocate and initialize the flash image */ -#ifdef CONFIG_NS_ABS_POS - ns->mem.byte = ioremap(CONFIG_NS_ABS_POS, ns->geom.totszoob); - if (!ns->mem.byte) { - NS_ERR("init_nandsim: failed to map the NAND flash image at address %p\n", - (void *)CONFIG_NS_ABS_POS); - return -ENOMEM; - } -#else ns->mem.byte = vmalloc(ns->geom.totszoob); if (!ns->mem.byte) { NS_ERR("init_nandsim: unable to allocate %u bytes for flash image\n", @@ -455,7 +443,6 @@ init_nandsim(struct mtd_info *mtd) return -ENOMEM; } memset(ns->mem.byte, 0xFF, ns->geom.totszoob); -#endif /* Allocate / initialize the internal buffer */ ns->buf.byte = kmalloc(ns->geom.pgszoob, GFP_KERNEL); @@ -474,11 +461,7 @@ init_nandsim(struct mtd_info *mtd) return 0; error: -#ifdef CONFIG_NS_ABS_POS - iounmap(ns->mem.byte); -#else vfree(ns->mem.byte); -#endif return -ENOMEM; } @@ -490,12 +473,7 @@ static void free_nandsim(struct nandsim *ns) { kfree(ns->buf.byte); - -#ifdef CONFIG_NS_ABS_POS - iounmap(ns->mem.byte); -#else vfree(ns->mem.byte); -#endif return; } -- cgit v0.10.2 From d086d43640a40dda7783f3c56724048685586d17 Mon Sep 17 00:00:00 2001 From: Vijay Kumar Date: Sun, 8 Oct 2006 22:02:31 +0530 Subject: [MTD] NAND: nandsim page-wise allocation (2/2) For page wise allocation, an array of flash page pointers is allocated during initialization. The flash pages are themselves allocated when a write occurs to the page. The flash pages are deallocated when they are erased. Signed-off-by: Vijay Kumar Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index 5dd3c4e..f00e195 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -227,6 +227,14 @@ MODULE_PARM_DESC(dbg, "Output debug information if not zero"); #define NS_MAX_PREVSTATES 1 /* + * A union to represent flash memory contents and flash buffer. + */ +union ns_mem { + u_char *byte; /* for byte access */ + uint16_t *word; /* for 16-bit word access */ +}; + +/* * The structure which describes all the internal simulator data. */ struct nandsim { @@ -243,17 +251,11 @@ struct nandsim { uint16_t npstates; /* number of previous states saved */ uint16_t stateidx; /* current state index */ - /* The simulated NAND flash image */ - union flash_media { - u_char *byte; - uint16_t *word; - } mem; + /* The simulated NAND flash pages array */ + union ns_mem *pages; /* Internal buffer of page + OOB size bytes */ - union internal_buffer { - u_char *byte; /* for byte access */ - uint16_t *word; /* for 16-bit word access */ - } buf; + union ns_mem buf; /* NAND flash "geometry" */ struct nandsin_geometry { @@ -342,6 +344,46 @@ static struct mtd_info *nsmtd; static u_char ns_verify_buf[NS_LARGEST_PAGE_SIZE]; /* + * Allocate array of page pointers and initialize the array to NULL + * pointers. + * + * RETURNS: 0 if success, -ENOMEM if memory alloc fails. + */ +static int +alloc_device(struct nandsim *ns) +{ + int i; + + ns->pages = vmalloc(ns->geom.pgnum * sizeof(union ns_mem)); + if (!ns->pages) { + NS_ERR("alloc_map: unable to allocate page array\n"); + return -ENOMEM; + } + for (i = 0; i < ns->geom.pgnum; i++) { + ns->pages[i].byte = NULL; + } + + return 0; +} + +/* + * Free any allocated pages, and free the array of page pointers. + */ +static void +free_device(struct nandsim *ns) +{ + int i; + + if (ns->pages) { + for (i = 0; i < ns->geom.pgnum; i++) { + if (ns->pages[i].byte) + kfree(ns->pages[i].byte); + } + vfree(ns->pages); + } +} + +/* * Initialize the nandsim structure. * * RETURNS: 0 if success, -ERRNO if failure. @@ -435,14 +477,8 @@ init_nandsim(struct mtd_info *mtd) printk("sector address bytes: %u\n", ns->geom.secaddrbytes); printk("options: %#x\n", ns->options); - /* Map / allocate and initialize the flash image */ - ns->mem.byte = vmalloc(ns->geom.totszoob); - if (!ns->mem.byte) { - NS_ERR("init_nandsim: unable to allocate %u bytes for flash image\n", - ns->geom.totszoob); - return -ENOMEM; - } - memset(ns->mem.byte, 0xFF, ns->geom.totszoob); + if (alloc_device(ns) != 0) + goto error; /* Allocate / initialize the internal buffer */ ns->buf.byte = kmalloc(ns->geom.pgszoob, GFP_KERNEL); @@ -461,7 +497,7 @@ init_nandsim(struct mtd_info *mtd) return 0; error: - vfree(ns->mem.byte); + free_device(ns); return -ENOMEM; } @@ -473,7 +509,7 @@ static void free_nandsim(struct nandsim *ns) { kfree(ns->buf.byte); - vfree(ns->mem.byte); + free_device(ns); return; } @@ -769,6 +805,84 @@ find_operation(struct nandsim *ns, uint32_t flag) } /* + * Returns a pointer to the current page. + */ +static inline union ns_mem *NS_GET_PAGE(struct nandsim *ns) +{ + return &(ns->pages[ns->regs.row]); +} + +/* + * Retuns a pointer to the current byte, within the current page. + */ +static inline u_char *NS_PAGE_BYTE_OFF(struct nandsim *ns) +{ + return NS_GET_PAGE(ns)->byte + ns->regs.column + ns->regs.off; +} + +/* + * Fill the NAND buffer with data read from the specified page. + */ +static void read_page(struct nandsim *ns, int num) +{ + union ns_mem *mypage; + + mypage = NS_GET_PAGE(ns); + if (mypage->byte == NULL) { + NS_DBG("read_page: page %d not allocated\n", ns->regs.row); + memset(ns->buf.byte, 0xFF, num); + } else { + NS_DBG("read_page: page %d allocated, reading from %d\n", + ns->regs.row, ns->regs.column + ns->regs.off); + memcpy(ns->buf.byte, NS_PAGE_BYTE_OFF(ns), num); + } +} + +/* + * Erase all pages in the specified sector. + */ +static void erase_sector(struct nandsim *ns) +{ + union ns_mem *mypage; + int i; + + mypage = NS_GET_PAGE(ns); + for (i = 0; i < ns->geom.pgsec; i++) { + if (mypage->byte != NULL) { + NS_DBG("erase_sector: freeing page %d\n", ns->regs.row+i); + kfree(mypage->byte); + mypage->byte = NULL; + } + mypage++; + } +} + +/* + * Program the specified page with the contents from the NAND buffer. + */ +static int prog_page(struct nandsim *ns, int num) +{ + union ns_mem *mypage; + u_char *pg_off; + + mypage = NS_GET_PAGE(ns); + if (mypage->byte == NULL) { + NS_DBG("prog_page: allocating page %d\n", ns->regs.row); + mypage->byte = kmalloc(ns->geom.pgszoob, GFP_KERNEL); + if (mypage->byte == NULL) { + NS_ERR("prog_page: error allocating memory for page %d\n", ns->regs.row); + return -1; + } + memset(mypage->byte, 0xFF, ns->geom.pgszoob); + } + + pg_off = NS_PAGE_BYTE_OFF(ns); + memcpy(pg_off, ns->buf.byte, num); + + return 0; +} + +/* * If state has any action bit, perform this action. * * RETURNS: 0 if success, -1 if error. @@ -776,7 +890,7 @@ find_operation(struct nandsim *ns, uint32_t flag) static int do_state_action(struct nandsim *ns, uint32_t action) { - int i, num; + int num; int busdiv = ns->busw == 8 ? 1 : 2; action &= ACTION_MASK; @@ -800,7 +914,7 @@ do_state_action(struct nandsim *ns, uint32_t action) break; } num = ns->geom.pgszoob - ns->regs.off - ns->regs.column; - memcpy(ns->buf.byte, ns->mem.byte + NS_RAW_OFFSET(ns) + ns->regs.off, num); + read_page(ns, num); NS_DBG("do_state_action: (ACTION_CPY:) copy %d bytes to int buf, raw offset %d\n", num, NS_RAW_OFFSET(ns) + ns->regs.off); @@ -841,7 +955,7 @@ do_state_action(struct nandsim *ns, uint32_t action) ns->regs.row, NS_RAW_OFFSET(ns)); NS_LOG("erase sector %d\n", ns->regs.row >> (ns->geom.secshift - ns->geom.pgshift)); - memset(ns->mem.byte + NS_RAW_OFFSET(ns), 0xFF, ns->geom.secszoob); + erase_sector(ns); NS_MDELAY(erase_delay); @@ -864,8 +978,8 @@ do_state_action(struct nandsim *ns, uint32_t action) return -1; } - for (i = 0; i < num; i++) - ns->mem.byte[NS_RAW_OFFSET(ns) + ns->regs.off + i] &= ns->buf.byte[i]; + if (prog_page(ns, num) == -1) + return -1; NS_DBG("do_state_action: copy %d bytes from int buf to (%#x, %#x), raw off = %d\n", num, ns->regs.row, ns->regs.column, NS_RAW_OFFSET(ns) + ns->regs.off); -- cgit v0.10.2 From a5602146c5abea7dfb0c9f4fe6f5870ebdafbc9f Mon Sep 17 00:00:00 2001 From: Vijay Kumar Date: Sat, 14 Oct 2006 21:33:34 +0530 Subject: [MTD] NAND: nandsim coding style fix Removes line break after return type in function definitions, to be consistent with the Linux coding style. Signed-off-by: Vijay Kumar Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index f00e195..28ee785 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -349,8 +349,7 @@ static u_char ns_verify_buf[NS_LARGEST_PAGE_SIZE]; * * RETURNS: 0 if success, -ENOMEM if memory alloc fails. */ -static int -alloc_device(struct nandsim *ns) +static int alloc_device(struct nandsim *ns) { int i; @@ -369,8 +368,7 @@ alloc_device(struct nandsim *ns) /* * Free any allocated pages, and free the array of page pointers. */ -static void -free_device(struct nandsim *ns) +static void free_device(struct nandsim *ns) { int i; @@ -388,8 +386,7 @@ free_device(struct nandsim *ns) * * RETURNS: 0 if success, -ERRNO if failure. */ -static int -init_nandsim(struct mtd_info *mtd) +static int init_nandsim(struct mtd_info *mtd) { struct nand_chip *chip = (struct nand_chip *)mtd->priv; struct nandsim *ns = (struct nandsim *)(chip->priv); @@ -505,8 +502,7 @@ error: /* * Free the nandsim structure. */ -static void -free_nandsim(struct nandsim *ns) +static void free_nandsim(struct nandsim *ns) { kfree(ns->buf.byte); free_device(ns); @@ -517,8 +513,7 @@ free_nandsim(struct nandsim *ns) /* * Returns the string representation of 'state' state. */ -static char * -get_state_name(uint32_t state) +static char *get_state_name(uint32_t state) { switch (NS_STATE(state)) { case STATE_CMD_READ0: @@ -576,8 +571,7 @@ get_state_name(uint32_t state) * * RETURNS: 1 if wrong command, 0 if right. */ -static int -check_command(int cmd) +static int check_command(int cmd) { switch (cmd) { @@ -603,8 +597,7 @@ check_command(int cmd) /* * Returns state after command is accepted by command number. */ -static uint32_t -get_state_by_command(unsigned command) +static uint32_t get_state_by_command(unsigned command) { switch (command) { case NAND_CMD_READ0: @@ -640,8 +633,7 @@ get_state_by_command(unsigned command) /* * Move an address byte to the correspondent internal register. */ -static inline void -accept_addr_byte(struct nandsim *ns, u_char bt) +static inline void accept_addr_byte(struct nandsim *ns, u_char bt) { uint byte = (uint)bt; @@ -659,8 +651,7 @@ accept_addr_byte(struct nandsim *ns, u_char bt) /* * Switch to STATE_READY state. */ -static inline void -switch_to_ready_state(struct nandsim *ns, u_char status) +static inline void switch_to_ready_state(struct nandsim *ns, u_char status) { NS_DBG("switch_to_ready_state: switch to %s state\n", get_state_name(STATE_READY)); @@ -719,8 +710,7 @@ switch_to_ready_state(struct nandsim *ns, u_char status) * -1 - several matches. * 0 - operation is found. */ -static int -find_operation(struct nandsim *ns, uint32_t flag) +static int find_operation(struct nandsim *ns, uint32_t flag) { int opsfound = 0; int i, j, idx = 0; @@ -887,8 +877,7 @@ static int prog_page(struct nandsim *ns, int num) * * RETURNS: 0 if success, -1 if error. */ -static int -do_state_action(struct nandsim *ns, uint32_t action) +static int do_state_action(struct nandsim *ns, uint32_t action) { int num; int busdiv = ns->busw == 8 ? 1 : 2; @@ -1020,8 +1009,7 @@ do_state_action(struct nandsim *ns, uint32_t action) /* * Switch simulator's state. */ -static void -switch_state(struct nandsim *ns) +static void switch_state(struct nandsim *ns) { if (ns->op) { /* @@ -1162,8 +1150,7 @@ switch_state(struct nandsim *ns) } } -static u_char -ns_nand_read_byte(struct mtd_info *mtd) +static u_char ns_nand_read_byte(struct mtd_info *mtd) { struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv; u_char outb = 0x00; @@ -1236,8 +1223,7 @@ ns_nand_read_byte(struct mtd_info *mtd) return outb; } -static void -ns_nand_write_byte(struct mtd_info *mtd, u_char byte) +static void ns_nand_write_byte(struct mtd_info *mtd, u_char byte) { struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv; @@ -1400,15 +1386,13 @@ static void ns_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int bitmask) ns_nand_write_byte(mtd, cmd); } -static int -ns_device_ready(struct mtd_info *mtd) +static int ns_device_ready(struct mtd_info *mtd) { NS_DBG("device_ready\n"); return 1; } -static uint16_t -ns_nand_read_word(struct mtd_info *mtd) +static uint16_t ns_nand_read_word(struct mtd_info *mtd) { struct nand_chip *chip = (struct nand_chip *)mtd->priv; @@ -1417,8 +1401,7 @@ ns_nand_read_word(struct mtd_info *mtd) return chip->read_byte(mtd) | (chip->read_byte(mtd) << 8); } -static void -ns_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) +static void ns_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) { struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv; @@ -1445,8 +1428,7 @@ ns_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) } } -static void -ns_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) +static void ns_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) { struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv; @@ -1499,8 +1481,7 @@ ns_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) return; } -static int -ns_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) +static int ns_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) { ns_nand_read_buf(mtd, (u_char *)&ns_verify_buf[0], len); -- cgit v0.10.2 From d29ebdbee4c196adddf9f412e6ea1f211656744f Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Thu, 19 Oct 2006 16:04:02 +0300 Subject: [MTD] core: trivial comments fix Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index baece61..53c66d5 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -755,7 +755,7 @@ static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, } /** - * nand_read_page_swecc - {REPLACABLE] software ecc based page read function + * nand_read_page_swecc - [REPLACABLE] software ecc based page read function * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data @@ -795,7 +795,7 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, } /** - * nand_read_page_hwecc - {REPLACABLE] hardware ecc based page read function + * nand_read_page_hwecc - [REPLACABLE] hardware ecc based page read function * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data @@ -839,7 +839,7 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, } /** - * nand_read_page_syndrome - {REPLACABLE] hardware ecc syndrom based page read + * nand_read_page_syndrome - [REPLACABLE] hardware ecc syndrom based page read * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data @@ -1375,7 +1375,7 @@ static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, } /** - * nand_write_page_swecc - {REPLACABLE] software ecc based page write function + * nand_write_page_swecc - [REPLACABLE] software ecc based page write function * @mtd: mtd info structure * @chip: nand chip info structure * @buf: data buffer @@ -1401,7 +1401,7 @@ static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, } /** - * nand_write_page_hwecc - {REPLACABLE] hardware ecc based page write function + * nand_write_page_hwecc - [REPLACABLE] hardware ecc based page write function * @mtd: mtd info structure * @chip: nand chip info structure * @buf: data buffer @@ -1429,7 +1429,7 @@ static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, } /** - * nand_write_page_syndrome - {REPLACABLE] hardware ecc syndrom based page write + * nand_write_page_syndrome - [REPLACABLE] hardware ecc syndrom based page write * @mtd: mtd info structure * @chip: nand chip info structure * @buf: data buffer -- cgit v0.10.2 From a86aaa6ddf32b0401e64e74174042866e0fb3e20 Mon Sep 17 00:00:00 2001 From: David Anders Date: Thu, 19 Oct 2006 19:33:19 +0300 Subject: [MTD] NOR: leave Intel chips in read-array mode on suspend During some testing with several samsung s3c24xx based devices it was discovered that often the cfi_cmdset_0001.c would not leave the chip in read-array mode on suspend. this is an issue if the same flash chip is used for the bootloader that needs to be read on resume. Signed-off-by: David Anders Signed-off-by: Nicolas Pitre Signed-off-by: David Woodhouse diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index 7ea49a0..e249736 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -2224,6 +2224,8 @@ static int cfi_intelext_suspend(struct mtd_info *mtd) case FL_CFI_QUERY: case FL_JEDEC_QUERY: if (chip->oldstate == FL_READY) { + /* place the chip in a known state before suspend */ + map_write(map, CMD(0xFF), cfi->chips[i].start); chip->oldstate = chip->state; chip->state = FL_PM_SUSPENDED; /* No need to wake_up() on this state change - -- cgit v0.10.2 From 82810b7b6cc7a74c68881a13b0eb66c7a6370fcc Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Fri, 20 Oct 2006 11:23:56 +0300 Subject: [MTD] NAND: nandsim: support subpage write As flash cannot do 0->1 bit transitions when programming, do not do this in the simulator too. This makes nandsim able to accept subpage writes. Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index 28ee785..abebcab 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -852,6 +852,7 @@ static void erase_sector(struct nandsim *ns) */ static int prog_page(struct nandsim *ns, int num) { + int i; union ns_mem *mypage; u_char *pg_off; @@ -867,7 +868,8 @@ static int prog_page(struct nandsim *ns, int num) } pg_off = NS_PAGE_BYTE_OFF(ns); - memcpy(pg_off, ns->buf.byte, num); + for (i = 0; i < num; i++) + pg_off[i] &= ns->buf.byte[i]; return 0; } -- cgit v0.10.2 From 7dcdcbef5d2b60d1db68fd2c07351f7afd8ad376 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sat, 21 Oct 2006 17:09:53 +0100 Subject: [MTD] NAND: Combined oob buffer so it's contiguous with data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ditch the separate oobrbuf and oobwbuf fields from the chip buffers, and use only a single buffer immediately after the data. This accommodates NAND controllers such as the OLPC CAFÉ chip, which can't do scatter/gather DMA so needs the OOB buffer to be contiguous with the data, for both read and write. Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 53c66d5..29090c6 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -971,7 +971,6 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, page = realpage & chip->pagemask; col = (int)(from & (mtd->writesize - 1)); - chip->oob_poi = chip->buffers->oobrbuf; buf = ops->datbuf; oob = ops->oobbuf; @@ -1270,8 +1269,6 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, realpage = (int)(from >> chip->page_shift); page = realpage & chip->pagemask; - chip->oob_poi = chip->buffers->oobrbuf; - while(1) { sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd); buf = nand_transfer_oob(chip, buf, ops); @@ -1625,7 +1622,9 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, (chip->pagebuf << chip->page_shift) < (to + ops->len)) chip->pagebuf = -1; - chip->oob_poi = chip->buffers->oobwbuf; + /* If we're not given explicit OOB data, let it be 0xFF */ + if (likely(!oob)) + memset(chip->oob_poi, 0xff, mtd->oobsize); while(1) { int cached = writelen > bytes && page != blockmask; @@ -1654,9 +1653,6 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, } } - if (unlikely(oob)) - memset(chip->oob_poi, 0xff, mtd->oobsize); - ops->retlen = ops->len - writelen; return ret; } @@ -1744,7 +1740,6 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, if (page == chip->pagebuf) chip->pagebuf = -1; - chip->oob_poi = chip->buffers->oobwbuf; memset(chip->oob_poi, 0xff, mtd->oobsize); nand_fill_oob(chip, ops->oobbuf, ops); status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask); @@ -2348,8 +2343,8 @@ int nand_scan_tail(struct mtd_info *mtd) if (!chip->buffers) return -ENOMEM; - /* Preset the internal oob write buffer */ - memset(chip->buffers->oobwbuf, 0xff, mtd->oobsize); + /* Set the internal oob buffer location, just after the page data */ + chip->oob_poi = chip->buffers + mtd->writesize; /* * If no default placement scheme is given, select an appropriate one diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 70420bb..6fc3e07 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -286,9 +286,7 @@ struct nand_ecc_ctrl { * struct nand_buffers - buffer structure for read/write * @ecccalc: buffer for calculated ecc * @ecccode: buffer for ecc read from flash - * @oobwbuf: buffer for write oob data * @databuf: buffer for data - dynamically sized - * @oobrbuf: buffer to read oob data * * Do not change the order of buffers. databuf and oobrbuf must be in * consecutive order. @@ -296,9 +294,7 @@ struct nand_ecc_ctrl { struct nand_buffers { uint8_t ecccalc[NAND_MAX_OOBSIZE]; uint8_t ecccode[NAND_MAX_OOBSIZE]; - uint8_t oobwbuf[NAND_MAX_OOBSIZE]; - uint8_t databuf[NAND_MAX_PAGESIZE]; - uint8_t oobrbuf[NAND_MAX_OOBSIZE]; + uint8_t databuf[NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE]; }; /** -- cgit v0.10.2 From 784f4d5e66ac1d962091e08fe5a4b238ed8c793d Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sun, 22 Oct 2006 01:47:45 +0100 Subject: [MTD] NAND: Correct setting of chip->oob_poi OOB buffer Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 29090c6..f23ab2c 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2344,7 +2344,7 @@ int nand_scan_tail(struct mtd_info *mtd) return -ENOMEM; /* Set the internal oob buffer location, just after the page data */ - chip->oob_poi = chip->buffers + mtd->writesize; + chip->oob_poi = chip->buffers->databuf + mtd->writesize; /* * If no default placement scheme is given, select an appropriate one -- cgit v0.10.2 From 04459d7c6239193fa8de4a5107ee8fdb0f366e35 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sun, 22 Oct 2006 02:18:48 +0100 Subject: =?UTF-8?q?[MTD]=20NAND:=20Add=20hardware=20ECC=20correction=20sup?= =?UTF-8?q?port=20to=20CAF=C3=89=20NAND=20driver?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/cafe.c b/drivers/mtd/nand/cafe.c new file mode 100644 index 0000000..1fe1108 --- /dev/null +++ b/drivers/mtd/nand/cafe.c @@ -0,0 +1,722 @@ +/* + * cafe_nand.c + * + * Copyright © 2006 Red Hat, Inc. + * Copyright © 2006 David Woodhouse + */ + +#define DEBUG + +#include +#undef DEBUG +#include +#include +#include +#include +#include +#include + +#define CAFE_NAND_CTRL1 0x00 +#define CAFE_NAND_CTRL2 0x04 +#define CAFE_NAND_CTRL3 0x08 +#define CAFE_NAND_STATUS 0x0c +#define CAFE_NAND_IRQ 0x10 +#define CAFE_NAND_IRQ_MASK 0x14 +#define CAFE_NAND_DATA_LEN 0x18 +#define CAFE_NAND_ADDR1 0x1c +#define CAFE_NAND_ADDR2 0x20 +#define CAFE_NAND_TIMING1 0x24 +#define CAFE_NAND_TIMING2 0x28 +#define CAFE_NAND_TIMING3 0x2c +#define CAFE_NAND_NONMEM 0x30 +#define CAFE_NAND_ECC_RESULT 0x3C +#define CAFE_NAND_ECC_SYN01 0x50 +#define CAFE_NAND_ECC_SYN23 0x54 +#define CAFE_NAND_ECC_SYN45 0x58 +#define CAFE_NAND_ECC_SYN67 0x5c +#define CAFE_NAND_DMA_CTRL 0x40 +#define CAFE_NAND_DMA_ADDR0 0x44 +#define CAFE_NAND_DMA_ADDR1 0x48 +#define CAFE_NAND_READ_DATA 0x1000 +#define CAFE_NAND_WRITE_DATA 0x2000 + +int cafe_correct_ecc(unsigned char *buf, + unsigned short *chk_syndrome_list); + +struct cafe_priv { + struct nand_chip nand; + struct pci_dev *pdev; + void __iomem *mmio; + uint32_t ctl1; + uint32_t ctl2; + int datalen; + int nr_data; + int data_pos; + int page_addr; + dma_addr_t dmaaddr; + unsigned char *dmabuf; + +}; + +static int usedma = 0; +module_param(usedma, int, 0644); + +static int skipbbt = 0; +module_param(skipbbt, int, 0644); + +static int debug = 0; +module_param(debug, int, 0644); + +/* Hrm. Why isn't this already conditional on something in the struct device? */ +#define cafe_dev_dbg(dev, args...) do { if (debug) dev_dbg(dev, ##args); } while(0) + + +static int cafe_device_ready(struct mtd_info *mtd) +{ + struct cafe_priv *cafe = mtd->priv; + int result = !!(readl(cafe->mmio + CAFE_NAND_STATUS) | 0x40000000); + + uint32_t irqs = readl(cafe->mmio + CAFE_NAND_IRQ); + writel(irqs, cafe->mmio+CAFE_NAND_IRQ); + cafe_dev_dbg(&cafe->pdev->dev, "NAND device is%s ready, IRQ %x (%x) (%x,%x)\n", + result?"":" not", irqs, readl(cafe->mmio + CAFE_NAND_IRQ), + readl(cafe->mmio + 0x3008), readl(cafe->mmio + 0x300c)); + return result; +} + + +static void cafe_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + struct cafe_priv *cafe = mtd->priv; + + if (usedma) + memcpy(cafe->dmabuf + cafe->datalen, buf, len); + else + memcpy_toio(cafe->mmio + CAFE_NAND_WRITE_DATA + cafe->datalen, buf, len); + cafe->datalen += len; + + cafe_dev_dbg(&cafe->pdev->dev, "Copy 0x%x bytes to write buffer. datalen 0x%x\n", + len, cafe->datalen); +} + +static void cafe_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct cafe_priv *cafe = mtd->priv; + + if (usedma) + memcpy(buf, cafe->dmabuf + cafe->datalen, len); + else + memcpy_fromio(buf, cafe->mmio + CAFE_NAND_READ_DATA + cafe->datalen, len); + + cafe_dev_dbg(&cafe->pdev->dev, "Copy 0x%x bytes from position 0x%x in read buffer.\n", + len, cafe->datalen); + cafe->datalen += len; +} + +static uint8_t cafe_read_byte(struct mtd_info *mtd) +{ + struct cafe_priv *cafe = mtd->priv; + uint8_t d; + + cafe_read_buf(mtd, &d, 1); + cafe_dev_dbg(&cafe->pdev->dev, "Read %02x\n", d); + + return d; +} + +static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, + int column, int page_addr) +{ + struct cafe_priv *cafe = mtd->priv; + int adrbytes = 0; + uint32_t ctl1; + uint32_t doneint = 0x80000000; + int i; + + cafe_dev_dbg(&cafe->pdev->dev, "cmdfunc %02x, 0x%x, 0x%x\n", + command, column, page_addr); + + if (command == NAND_CMD_ERASE2 || command == NAND_CMD_PAGEPROG) { + /* Second half of a command we already calculated */ + writel(cafe->ctl2 | 0x100 | command, cafe->mmio + 0x04); + ctl1 = cafe->ctl1; + cafe_dev_dbg(&cafe->pdev->dev, "Continue command, ctl1 %08x, #data %d\n", + cafe->ctl1, cafe->nr_data); + goto do_command; + } + /* Reset ECC engine */ + writel(0, cafe->mmio + CAFE_NAND_CTRL2); + + /* Emulate NAND_CMD_READOOB on large-page chips */ + if (mtd->writesize > 512 && + command == NAND_CMD_READOOB) { + column += mtd->writesize; + command = NAND_CMD_READ0; + } + + /* FIXME: Do we need to send read command before sending data + for small-page chips, to position the buffer correctly? */ + + if (column != -1) { + writel(column, cafe->mmio + 0x1c); + adrbytes = 2; + if (page_addr != -1) + goto write_adr2; + } else if (page_addr != -1) { + writel(page_addr & 0xffff, cafe->mmio + 0x1c); + page_addr >>= 16; + write_adr2: + writel(page_addr, cafe->mmio+0x20); + adrbytes += 2; + if (mtd->size > mtd->writesize << 16) + adrbytes++; + } + + cafe->data_pos = cafe->datalen = 0; + + /* Set command valid bit */ + ctl1 = 0x80000000 | command; + + /* Set RD or WR bits as appropriate */ + if (command == NAND_CMD_READID || command == NAND_CMD_STATUS) { + ctl1 |= (1<<26); /* rd */ + /* Always 5 bytes, for now */ + cafe->datalen = 4; + /* And one address cycle -- even for STATUS, since the controller doesn't work without */ + adrbytes = 1; + } else if (command == NAND_CMD_READ0 || command == NAND_CMD_READ1 || + command == NAND_CMD_READOOB || command == NAND_CMD_RNDOUT) { + ctl1 |= 1<<26; /* rd */ + /* For now, assume just read to end of page */ + cafe->datalen = mtd->writesize + mtd->oobsize - column; + } else if (command == NAND_CMD_SEQIN) + ctl1 |= 1<<25; /* wr */ + + /* Set number of address bytes */ + if (adrbytes) + ctl1 |= ((adrbytes-1)|8) << 27; + + if (command == NAND_CMD_SEQIN || command == NAND_CMD_ERASE1) { + /* Ignore the first command of a pair; the hardware + deals with them both at once, later */ + cafe->ctl1 = ctl1; + cafe->ctl2 = 0; + cafe_dev_dbg(&cafe->pdev->dev, "Setup for delayed command, ctl1 %08x, dlen %x\n", + cafe->ctl1, cafe->datalen); + return; + } + /* RNDOUT and READ0 commands need a following byte */ + if (command == NAND_CMD_RNDOUT) + writel(cafe->ctl2 | 0x100 | NAND_CMD_RNDOUTSTART, cafe->mmio + CAFE_NAND_CTRL2); + else if (command == NAND_CMD_READ0 && mtd->writesize > 512) + writel(cafe->ctl2 | 0x100 | NAND_CMD_READSTART, cafe->mmio + CAFE_NAND_CTRL2); + + do_command: +#if 0 + // ECC on read only works if we ... + if (cafe->datalen == 2112) + cafe->datalen = 2062; +#endif + cafe_dev_dbg(&cafe->pdev->dev, "dlen %x, ctl1 %x, ctl2 %x\n", + cafe->datalen, ctl1, readl(cafe->mmio+CAFE_NAND_CTRL2)); + /* NB: The datasheet lies -- we really should be subtracting 1 here */ + writel(cafe->datalen, cafe->mmio + CAFE_NAND_DATA_LEN); + writel(0x90000000, cafe->mmio + CAFE_NAND_IRQ); + if (usedma && (ctl1 & (3<<25))) { + uint32_t dmactl = 0xc0000000 + cafe->datalen; + /* If WR or RD bits set, set up DMA */ + if (ctl1 & (1<<26)) { + /* It's a read */ + dmactl |= (1<<29); + /* ... so it's done when the DMA is done, not just + the command. */ + doneint = 0x10000000; + } + writel(dmactl, cafe->mmio + 0x40); + } +#if 0 + printk("DMA setup is %x, status %x, ctl1 %x\n", readl(cafe->mmio + 0x40), readl(cafe->mmio + 0x0c), readl(cafe->mmio)); + printk("DMA setup is %x, status %x, ctl1 %x\n", readl(cafe->mmio + 0x40), readl(cafe->mmio + 0x0c), readl(cafe->mmio)); +#endif + cafe->datalen = 0; + +#if 0 + printk("About to write command %08x\n", ctl1); + for (i=0; i< 0x5c; i+=4) + printk("Register %x: %08x\n", i, readl(cafe->mmio + i)); +#endif + writel(ctl1, cafe->mmio + CAFE_NAND_CTRL1); + /* Apply this short delay always to ensure that we do wait tWB in + * any case on any machine. */ + ndelay(100); + + if (1) { + int c = 500000; + uint32_t irqs; + + while (c--) { + irqs = readl(cafe->mmio + CAFE_NAND_IRQ); + if (irqs & doneint) + break; + udelay(1); + if (!(c % 100000)) + cafe_dev_dbg(&cafe->pdev->dev, "Wait for ready, IRQ %x\n", irqs); + cpu_relax(); + } + writel(doneint, cafe->mmio + CAFE_NAND_IRQ); + cafe_dev_dbg(&cafe->pdev->dev, "Command %x completed after %d usec, irqs %x (%x)\n", command, 50000-c, irqs, readl(cafe->mmio + CAFE_NAND_IRQ)); + } + + + cafe->ctl2 &= ~(1<<8); + cafe->ctl2 &= ~(1<<30); + + switch (command) { + + case NAND_CMD_CACHEDPROG: + case NAND_CMD_PAGEPROG: + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_SEQIN: + case NAND_CMD_RNDIN: + case NAND_CMD_STATUS: + case NAND_CMD_DEPLETE1: + case NAND_CMD_RNDOUT: + case NAND_CMD_STATUS_ERROR: + case NAND_CMD_STATUS_ERROR0: + case NAND_CMD_STATUS_ERROR1: + case NAND_CMD_STATUS_ERROR2: + case NAND_CMD_STATUS_ERROR3: + writel(cafe->ctl2, cafe->mmio + CAFE_NAND_CTRL2); + return; + } + nand_wait_ready(mtd); + writel(cafe->ctl2, cafe->mmio + CAFE_NAND_CTRL2); +} + +static void cafe_select_chip(struct mtd_info *mtd, int chipnr) +{ + //struct cafe_priv *cafe = mtd->priv; + // cafe_dev_dbg(&cafe->pdev->dev, "select_chip %d\n", chipnr); +} +static int cafe_nand_interrupt(int irq, void *id, struct pt_regs *regs) +{ + struct mtd_info *mtd = id; + struct cafe_priv *cafe = mtd->priv; + uint32_t irqs = readl(cafe->mmio + CAFE_NAND_IRQ); + writel(irqs & ~0x90000000, cafe->mmio + CAFE_NAND_IRQ); + if (!irqs) + return IRQ_NONE; + + cafe_dev_dbg(&cafe->pdev->dev, "irq, bits %x (%x)\n", irqs, readl(cafe->mmio + CAFE_NAND_IRQ)); + return IRQ_HANDLED; +} + +static void cafe_nand_bug(struct mtd_info *mtd) +{ + BUG(); +} + +static int cafe_nand_write_oob(struct mtd_info *mtd, + struct nand_chip *chip, int page) +{ + int status = 0; + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + + return status & NAND_STATUS_FAIL ? -EIO : 0; +} + +/* Don't use -- use nand_read_oob_std for now */ +static int cafe_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page, int sndcmd) +{ + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + return 1; +} +/** + * cafe_nand_read_page_syndrome - {REPLACABLE] hardware ecc syndrom based page read + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * + * The hw generator calculates the error syndrome automatically. Therefor + * we need a special oob layout and handling. + */ +static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf) +{ + struct cafe_priv *cafe = mtd->priv; + + dev_dbg(&cafe->pdev->dev, "ECC result %08x SYN1,2 %08x\n", + readl(cafe->mmio + CAFE_NAND_ECC_RESULT), + readl(cafe->mmio + CAFE_NAND_ECC_SYN01)); + + chip->read_buf(mtd, buf, mtd->writesize); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + + if (readl(cafe->mmio + CAFE_NAND_ECC_RESULT) & (1<<18)) { + unsigned short syn[8]; + int i; + + for (i=0; i<8; i+=2) { + uint32_t tmp = readl(cafe->mmio + CAFE_NAND_ECC_SYN01 + (i*2)); + syn[i] = tmp & 0xfff; + syn[i+1] = (tmp >> 16) & 0xfff; + } + + if ((i = cafe_correct_ecc(buf, syn)) < 0) { + dev_dbg(&cafe->pdev->dev, "Failed to correct ECC\n"); + mtd->ecc_stats.failed++; + } else { + dev_dbg(&cafe->pdev->dev, "Corrected %d symbol errors\n", i); + mtd->ecc_stats.corrected += i; + } + } + + + return 0; +} + +static struct nand_ecclayout cafe_oobinfo_2048 = { + .eccbytes = 14, + .eccpos = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, + .oobfree = {{14, 50}} +}; + +/* Ick. The BBT code really ought to be able to work this bit out + for itself from the above */ +static uint8_t cafe_bbt_pattern[] = {'B', 'b', 't', '0' }; +static uint8_t cafe_mirror_pattern[] = {'1', 't', 'b', 'B' }; + +static struct nand_bbt_descr cafe_bbt_main_descr_2048 = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 14, + .len = 4, + .veroffs = 18, + .maxblocks = 4, + .pattern = cafe_bbt_pattern +}; + +static struct nand_bbt_descr cafe_bbt_mirror_descr_2048 = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 14, + .len = 4, + .veroffs = 18, + .maxblocks = 4, + .pattern = cafe_mirror_pattern +}; + +static struct nand_ecclayout cafe_oobinfo_512 = { + .eccbytes = 14, + .eccpos = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, + .oobfree = {{14, 2}} +}; + +static void cafe_nand_write_page_lowlevel(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf) +{ + struct cafe_priv *cafe = mtd->priv; + + chip->write_buf(mtd, buf, mtd->writesize); + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + + /* Set up ECC autogeneration */ + cafe->ctl2 |= (1<<27) | (1<<30); + if (mtd->writesize == 2048) + cafe->ctl2 |= (1<<29); +} + +static int cafe_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int page, int cached, int raw) +{ + int status; + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); + + if (unlikely(raw)) + chip->ecc.write_page_raw(mtd, chip, buf); + else + chip->ecc.write_page(mtd, chip, buf); + + /* + * Cached progamming disabled for now, Not sure if its worth the + * trouble. The speed gain is not very impressive. (2.3->2.6Mib/s) + */ + cached = 0; + + if (!cached || !(chip->options & NAND_CACHEPRG)) { + + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + /* + * See if operation failed and additional status checks are + * available + */ + if ((status & NAND_STATUS_FAIL) && (chip->errstat)) + status = chip->errstat(mtd, chip, FL_WRITING, status, + page); + + if (status & NAND_STATUS_FAIL) + return -EIO; + } else { + chip->cmdfunc(mtd, NAND_CMD_CACHEDPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + } + +#ifdef CONFIG_MTD_NAND_VERIFY_WRITE + /* Send command to read back the data */ + chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + + if (chip->verify_buf(mtd, buf, mtd->writesize)) + return -EIO; +#endif + return 0; +} + +static int cafe_nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) +{ + return 0; +} + +static int __devinit cafe_nand_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct mtd_info *mtd; + struct cafe_priv *cafe; + uint32_t ctrl; + int err = 0; + + err = pci_enable_device(pdev); + if (err) + return err; + + pci_set_master(pdev); + + mtd = kzalloc(sizeof(*mtd) + sizeof(struct cafe_priv), GFP_KERNEL); + if (!mtd) { + dev_warn(&pdev->dev, "failed to alloc mtd_info\n"); + return -ENOMEM; + } + cafe = (void *)(&mtd[1]); + + mtd->priv = cafe; + mtd->owner = THIS_MODULE; + + cafe->pdev = pdev; + cafe->mmio = pci_iomap(pdev, 0, 0); + if (!cafe->mmio) { + dev_warn(&pdev->dev, "failed to iomap\n"); + err = -ENOMEM; + goto out_free_mtd; + } + cafe->dmabuf = dma_alloc_coherent(&cafe->pdev->dev, 2112 + sizeof(struct nand_buffers), + &cafe->dmaaddr, GFP_KERNEL); + if (!cafe->dmabuf) { + err = -ENOMEM; + goto out_ior; + } + cafe->nand.buffers = (void *)cafe->dmabuf + 2112; + + cafe->nand.cmdfunc = cafe_nand_cmdfunc; + cafe->nand.dev_ready = cafe_device_ready; + cafe->nand.read_byte = cafe_read_byte; + cafe->nand.read_buf = cafe_read_buf; + cafe->nand.write_buf = cafe_write_buf; + cafe->nand.select_chip = cafe_select_chip; + + cafe->nand.chip_delay = 0; + + /* Enable the following for a flash based bad block table */ + cafe->nand.options = NAND_USE_FLASH_BBT | NAND_NO_AUTOINCR | NAND_OWN_BUFFERS; + + if (skipbbt) { + cafe->nand.options |= NAND_SKIP_BBTSCAN; + cafe->nand.block_bad = cafe_nand_block_bad; + } + + /* Timings from Marvell's test code (not verified or calculated by us) */ + writel(0xffffffff, cafe->mmio + CAFE_NAND_IRQ_MASK); +#if 1 + writel(0x01010a0a, cafe->mmio + CAFE_NAND_TIMING1); + writel(0x24121212, cafe->mmio + CAFE_NAND_TIMING2); + writel(0x11000000, cafe->mmio + CAFE_NAND_TIMING3); +#else + writel(0xffffffff, cafe->mmio + CAFE_NAND_TIMING1); + writel(0xffffffff, cafe->mmio + CAFE_NAND_TIMING2); + writel(0xffffffff, cafe->mmio + CAFE_NAND_TIMING3); +#endif + writel(0xffffffff, cafe->mmio + CAFE_NAND_IRQ_MASK); + err = request_irq(pdev->irq, &cafe_nand_interrupt, SA_SHIRQ, "CAFE NAND", mtd); + if (err) { + dev_warn(&pdev->dev, "Could not register IRQ %d\n", pdev->irq); + + goto out_free_dma; + } +#if 1 + /* Disable master reset, enable NAND clock */ + ctrl = readl(cafe->mmio + 0x3004); + ctrl &= 0xffffeff0; + ctrl |= 0x00007000; + writel(ctrl | 0x05, cafe->mmio + 0x3004); + writel(ctrl | 0x0a, cafe->mmio + 0x3004); + writel(0, cafe->mmio + 0x40); + + writel(0x7006, cafe->mmio + 0x3004); + writel(0x700a, cafe->mmio + 0x3004); + + /* Set up DMA address */ + writel(cafe->dmaaddr & 0xffffffff, cafe->mmio + 0x44); + if (sizeof(cafe->dmaaddr) > 4) + writel((cafe->dmaaddr >> 16) >> 16, cafe->mmio + 0x48); + else + writel(0, cafe->mmio + 0x48); + cafe_dev_dbg(&cafe->pdev->dev, "Set DMA address to %x (virt %p)\n", + readl(cafe->mmio+0x44), cafe->dmabuf); + + /* Enable NAND IRQ in global IRQ mask register */ + writel(0x80000007, cafe->mmio + 0x300c); + cafe_dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n", + readl(cafe->mmio + 0x3004), readl(cafe->mmio + 0x300c)); +#endif +#if 1 + mtd->writesize=2048; + mtd->oobsize = 0x40; + memset(cafe->dmabuf, 0x5a, 2112); + cafe->nand.cmdfunc(mtd, NAND_CMD_READID, 0, -1); + cafe->nand.read_byte(mtd); + cafe->nand.read_byte(mtd); + cafe->nand.read_byte(mtd); + cafe->nand.read_byte(mtd); + cafe->nand.read_byte(mtd); +#endif +#if 0 + cafe->nand.cmdfunc(mtd, NAND_CMD_READ0, 0, 0); + // nand_wait_ready(mtd); + cafe->nand.read_byte(mtd); + cafe->nand.read_byte(mtd); + cafe->nand.read_byte(mtd); + cafe->nand.read_byte(mtd); +#endif +#if 0 + writel(0x84600070, cafe->mmio); + udelay(10); + cafe_dev_dbg(&cafe->pdev->dev, "Status %x\n", readl(cafe->mmio + 0x30)); +#endif + /* Scan to find existance of the device */ + if (nand_scan_ident(mtd, 1)) { + err = -ENXIO; + goto out_irq; + } + + cafe->ctl2 = 1<<27; /* Reed-Solomon ECC */ + if (mtd->writesize == 2048) + cafe->ctl2 |= 1<<29; /* 2KiB page size */ + + /* Set up ECC according to the type of chip we found */ + if (mtd->writesize == 512 || mtd->writesize == 2048) { + cafe->nand.ecc.mode = NAND_ECC_HW_SYNDROME; + cafe->nand.ecc.size = mtd->writesize; + cafe->nand.ecc.bytes = 14; + cafe->nand.ecc.layout = &cafe_oobinfo_2048; + cafe->nand.bbt_td = &cafe_bbt_main_descr_2048; + cafe->nand.bbt_md = &cafe_bbt_mirror_descr_2048; + cafe->nand.ecc.hwctl = (void *)cafe_nand_bug; + cafe->nand.ecc.calculate = (void *)cafe_nand_bug; + cafe->nand.ecc.correct = (void *)cafe_nand_bug; + cafe->nand.write_page = cafe_nand_write_page; + cafe->nand.ecc.write_page = cafe_nand_write_page_lowlevel; + cafe->nand.ecc.write_oob = cafe_nand_write_oob; + cafe->nand.ecc.read_page = cafe_nand_read_page; + cafe->nand.ecc.read_oob = cafe_nand_read_oob; + + } else { + printk(KERN_WARNING "Unexpected NAND flash writesize %d. Using software ECC\n", + mtd->writesize); + cafe->nand.ecc.mode = NAND_ECC_NONE; + } + + err = nand_scan_tail(mtd); + if (err) + goto out_irq; + + pci_set_drvdata(pdev, mtd); + add_mtd_device(mtd); + goto out; + + out_irq: + /* Disable NAND IRQ in global IRQ mask register */ + writel(~1 & readl(cafe->mmio + 0x300c), cafe->mmio + 0x300c); + free_irq(pdev->irq, mtd); + out_free_dma: + dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr); + out_ior: + pci_iounmap(pdev, cafe->mmio); + out_free_mtd: + kfree(mtd); + out: + return err; +} + +static void __devexit cafe_nand_remove(struct pci_dev *pdev) +{ + struct mtd_info *mtd = pci_get_drvdata(pdev); + struct cafe_priv *cafe = mtd->priv; + + del_mtd_device(mtd); + /* Disable NAND IRQ in global IRQ mask register */ + writel(~1 & readl(cafe->mmio + 0x300c), cafe->mmio + 0x300c); + free_irq(pdev->irq, mtd); + nand_release(mtd); + pci_iounmap(pdev, cafe->mmio); + dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr); + kfree(mtd); +} + +static struct pci_device_id cafe_nand_tbl[] = { + { 0x11ab, 0x4100, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MEMORY_FLASH << 8, 0xFFFF0 } +}; + +MODULE_DEVICE_TABLE(pci, cafe_nand_tbl); + +static struct pci_driver cafe_nand_pci_driver = { + .name = "CAFÉ NAND", + .id_table = cafe_nand_tbl, + .probe = cafe_nand_probe, + .remove = __devexit_p(cafe_nand_remove), +#ifdef CONFIG_PMx + .suspend = cafe_nand_suspend, + .resume = cafe_nand_resume, +#endif +}; + +static int cafe_nand_init(void) +{ + return pci_register_driver(&cafe_nand_pci_driver); +} + +static void cafe_nand_exit(void) +{ + pci_unregister_driver(&cafe_nand_pci_driver); +} +module_init(cafe_nand_init); +module_exit(cafe_nand_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Woodhouse "); +MODULE_DESCRIPTION("NAND flash driver for OLPC CAFE chip"); + +/* Correct ECC for 2048 bytes of 0xff: + 41 a0 71 65 54 27 f3 93 ec a9 be ed 0b a1 */ + +/* dwmw2's B-test board, in case of completely screwing it: +Bad eraseblock 2394 at 0x12b40000 +Bad eraseblock 2627 at 0x14860000 +Bad eraseblock 3349 at 0x1a2a0000 +*/ diff --git a/drivers/mtd/nand/cafe_ecc.c b/drivers/mtd/nand/cafe_ecc.c new file mode 100644 index 0000000..4df28a8 --- /dev/null +++ b/drivers/mtd/nand/cafe_ecc.c @@ -0,0 +1,1348 @@ +/* Error correction for CAFÉ NAND controller + * + * © 2006 Marvell, Inc. + * Author: Tom Chiou + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include + +static unsigned short gf4096_mul(unsigned short, unsigned short); +static unsigned short gf64_mul(unsigned short, unsigned short); +static unsigned short gf4096_inv(unsigned short); +static unsigned short err_pos(unsigned short); +static void find_4bit_err_coefs(unsigned short, unsigned short, unsigned short, + unsigned short, unsigned short, unsigned short, + unsigned short, unsigned short, unsigned short *); +static void zero_4x5_col3(unsigned short[4][5]); +static void zero_4x5_col2(unsigned short[4][5]); +static void zero_4x5_col1(unsigned short[4][5]); +static void swap_4x5_rows(unsigned short[4][5], int, int, int); +static void swap_2x3_rows(unsigned short m[2][3]); +static void solve_4x5(unsigned short m[4][5], unsigned short *, int *); +static void sort_coefs(int *, unsigned short *, int); +static void find_4bit_err_pats(unsigned short, unsigned short, unsigned short, + unsigned short, unsigned short, unsigned short, + unsigned short, unsigned short, unsigned short *); +static void find_3bit_err_coefs(unsigned short, unsigned short, unsigned short, + unsigned short, unsigned short, unsigned short, + unsigned short *); +static void zero_3x4_col2(unsigned short[3][4]); +static void zero_3x4_col1(unsigned short[3][4]); +static void swap_3x4_rows(unsigned short[3][4], int, int, int); +static void solve_3x4(unsigned short[3][4], unsigned short *, int *); +static void find_3bit_err_pats(unsigned short, unsigned short, unsigned short, + unsigned short, unsigned short, unsigned short, + unsigned short *); + +static void find_2bit_err_pats(unsigned short, unsigned short, unsigned short, + unsigned short, unsigned short *); +static void find_2x2_soln(unsigned short, unsigned short, unsigned short, + unsigned short, unsigned short, unsigned short, + unsigned short *); +static void solve_2x3(unsigned short[2][3], unsigned short *); +static int chk_no_err_only(unsigned short *, unsigned short *); +static int chk_1_err_only(unsigned short *, unsigned short *); +static int chk_2_err_only(unsigned short *, unsigned short *); +static int chk_3_err_only(unsigned short *, unsigned short *); +static int chk_4_err_only(unsigned short *, unsigned short *); + +static unsigned short gf64_mul(unsigned short a, unsigned short b) +{ + unsigned short tmp1, tmp2, tmp3, tmp4, tmp5; + unsigned short c_bit0, c_bit1, c_bit2, c_bit3, c_bit4, c_bit5, c; + + tmp1 = ((a) ^ (a >> 5)); + tmp2 = ((a >> 4) ^ (a >> 5)); + tmp3 = ((a >> 3) ^ (a >> 4)); + tmp4 = ((a >> 2) ^ (a >> 3)); + tmp5 = ((a >> 1) ^ (a >> 2)); + + c_bit0 = ((a & b) ^ ((a >> 5) & (b >> 1)) ^ ((a >> 4) & (b >> 2)) ^ + ((a >> 3) & (b >> 3)) ^ ((a >> 2) & (b >> 4)) ^ ((a >> 1) & (b >> 5))) & 0x1; + + c_bit1 = (((a >> 1) & b) ^ (tmp1 & (b >> 1)) ^ (tmp2 & (b >> 2)) ^ + (tmp3 & (b >> 3)) ^ (tmp4 & (b >> 4)) ^ (tmp5 & (b >> 5))) & 0x1; + + c_bit2 = (((a >> 2) & b) ^ ((a >> 1) & (b >> 1)) ^ (tmp1 & (b >> 2)) ^ + (tmp2 & (b >> 3)) ^ (tmp3 & (b >> 4)) ^ (tmp4 & (b >> 5))) & 0x1; + + c_bit3 = (((a >> 3) & b) ^ ((a >> 2) & (b >> 1)) ^ ((a >> 1) & (b >> 2)) ^ + (tmp1 & (b >> 3)) ^ (tmp2 & (b >> 4)) ^ (tmp3 & (b >> 5))) & 0x1; + + c_bit4 = (((a >> 4) & b) ^ ((a >> 3) & (b >> 1)) ^ ((a >> 2) & (b >> 2)) ^ + ((a >> 1) & (b >> 3)) ^ (tmp1 & (b >> 4)) ^ (tmp2 & (b >> 5))) & 0x1; + + c_bit5 = (((a >> 5) & b) ^ ((a >> 4) & (b >> 1)) ^ ((a >> 3) & (b >> 2)) ^ + ((a >> 2) & (b >> 3)) ^ ((a >> 1) & (b >> 4)) ^ (tmp1 & (b >> 5))) & 0x1; + + c = c_bit0 | (c_bit1 << 1) | (c_bit2 << 2) | (c_bit3 << 3) | (c_bit4 << 4) | (c_bit5 << 5); + + return c; +} + +static unsigned short gf4096_mul(unsigned short a, unsigned short b) +{ + unsigned short ah, al, bh, bl, alxah, blxbh, ablh, albl, ahbh, ahbhB, c; + + ah = (a >> 6) & 0x3f; + al = a & 0x3f; + bh = (b >> 6) & 0x3f; + bl = b & 0x3f; + alxah = al ^ ah; + blxbh = bl ^ bh; + + ablh = gf64_mul(alxah, blxbh); + albl = gf64_mul(al, bl); + ahbh = gf64_mul(ah, bh); + + ahbhB = ((ahbh & 0x1) << 5) | + ((ahbh & 0x20) >> 1) | + ((ahbh & 0x10) >> 1) | ((ahbh & 0x8) >> 1) | ((ahbh & 0x4) >> 1) | (((ahbh >> 1) ^ ahbh) & 0x1); + + c = ((ablh ^ albl) << 6) | (ahbhB ^ albl); + return c; +} + +static void find_2bit_err_pats(unsigned short s0, unsigned short s1, unsigned short r0, unsigned short r1, unsigned short *pats) +{ + find_2x2_soln(0x1, 0x1, r0, r1, s0, s1, pats); +} + +static void find_3bit_err_coefs(unsigned short s0, unsigned short s1, + unsigned short s2, unsigned short s3, unsigned short s4, unsigned short s5, unsigned short *coefs) +{ + unsigned short m[3][4]; + int row_order[3]; + + row_order[0] = 0; + row_order[1] = 1; + row_order[2] = 2; + m[0][0] = s2; + m[0][1] = s1; + m[0][2] = s0; + m[0][3] = s3; + m[1][0] = s3; + m[1][1] = s2; + m[1][2] = s1; + m[1][3] = s4; + m[2][0] = s4; + m[2][1] = s3; + m[2][2] = s2; + m[2][3] = s5; + + if (m[0][2] != 0x0) { + zero_3x4_col2(m); + } else if (m[1][2] != 0x0) { + swap_3x4_rows(m, 0, 1, 4); + zero_3x4_col2(m); + } else if (m[2][2] != 0x0) { + swap_3x4_rows(m, 0, 2, 4); + zero_3x4_col2(m); + } else { + printk(KERN_ERR "Error: find_3bit_err_coefs, s0,s1,s2 all zeros!\n"); + } + + if (m[1][1] != 0x0) { + zero_3x4_col1(m); + } else if (m[2][1] != 0x0) { + swap_3x4_rows(m, 1, 2, 4); + zero_3x4_col1(m); + } else { + printk(KERN_ERR "Error: find_3bit_err_coefs, cannot resolve col 1!\n"); + } + + /* solve coefs */ + solve_3x4(m, coefs, row_order); +} + +static void zero_3x4_col2(unsigned short m[3][4]) +{ + unsigned short minv1, minv2; + + minv1 = gf4096_mul(m[1][2], gf4096_inv(m[0][2])); + minv2 = gf4096_mul(m[2][2], gf4096_inv(m[0][2])); + m[1][0] = m[1][0] ^ gf4096_mul(m[0][0], minv1); + m[1][1] = m[1][1] ^ gf4096_mul(m[0][1], minv1); + m[1][3] = m[1][3] ^ gf4096_mul(m[0][3], minv1); + m[2][0] = m[2][0] ^ gf4096_mul(m[0][0], minv2); + m[2][1] = m[2][1] ^ gf4096_mul(m[0][1], minv2); + m[2][3] = m[2][3] ^ gf4096_mul(m[0][3], minv2); +} + +static void zero_3x4_col1(unsigned short m[3][4]) +{ + unsigned short minv; + minv = gf4096_mul(m[2][1], gf4096_inv(m[1][1])); + m[2][0] = m[2][0] ^ gf4096_mul(m[1][0], minv); + m[2][3] = m[2][3] ^ gf4096_mul(m[1][3], minv); +} + +static void swap_3x4_rows(unsigned short m[3][4], int i, int j, int col_width) +{ + unsigned short tmp0; + int cnt; + for (cnt = 0; cnt < col_width; cnt++) { + tmp0 = m[i][cnt]; + m[i][cnt] = m[j][cnt]; + m[j][cnt] = tmp0; + } +} + +static void solve_3x4(unsigned short m[3][4], unsigned short *coefs, int *row_order) +{ + unsigned short tmp[3]; + tmp[0] = gf4096_mul(m[2][3], gf4096_inv(m[2][0])); + tmp[1] = gf4096_mul((gf4096_mul(tmp[0], m[1][0]) ^ m[1][3]), gf4096_inv(m[1][1])); + tmp[2] = gf4096_mul((gf4096_mul(tmp[0], m[0][0]) ^ gf4096_mul(tmp[1], m[0][1]) ^ m[0][3]), gf4096_inv(m[0][2])); + sort_coefs(row_order, tmp, 3); + coefs[0] = tmp[0]; + coefs[1] = tmp[1]; + coefs[2] = tmp[2]; +} + +static void find_3bit_err_pats(unsigned short s0, unsigned short s1, + unsigned short s2, unsigned short r0, + unsigned short r1, unsigned short r2, + unsigned short *pats) +{ + find_2x2_soln(r0 ^ r2, r1 ^ r2, + gf4096_mul(r0, r0 ^ r2), gf4096_mul(r1, r1 ^ r2), + gf4096_mul(s0, r2) ^ s1, gf4096_mul(s1, r2) ^ s2, pats); + pats[2] = s0 ^ pats[0] ^ pats[1]; +} + +static void find_4bit_err_coefs(unsigned short s0, unsigned short s1, + unsigned short s2, unsigned short s3, + unsigned short s4, unsigned short s5, + unsigned short s6, unsigned short s7, + unsigned short *coefs) +{ + unsigned short m[4][5]; + int row_order[4]; + + row_order[0] = 0; + row_order[1] = 1; + row_order[2] = 2; + row_order[3] = 3; + + m[0][0] = s3; + m[0][1] = s2; + m[0][2] = s1; + m[0][3] = s0; + m[0][4] = s4; + m[1][0] = s4; + m[1][1] = s3; + m[1][2] = s2; + m[1][3] = s1; + m[1][4] = s5; + m[2][0] = s5; + m[2][1] = s4; + m[2][2] = s3; + m[2][3] = s2; + m[2][4] = s6; + m[3][0] = s6; + m[3][1] = s5; + m[3][2] = s4; + m[3][3] = s3; + m[3][4] = s7; + + if (m[0][3] != 0x0) { + zero_4x5_col3(m); + } else if (m[1][3] != 0x0) { + swap_4x5_rows(m, 0, 1, 5); + zero_4x5_col3(m); + } else if (m[2][3] != 0x0) { + swap_4x5_rows(m, 0, 2, 5); + zero_4x5_col3(m); + } else if (m[3][3] != 0x0) { + swap_4x5_rows(m, 0, 3, 5); + zero_4x5_col3(m); + } else { + printk(KERN_ERR "Error: find_4bit_err_coefs, s0,s1,s2,s3 all zeros!\n"); + } + + if (m[1][2] != 0x0) { + zero_4x5_col2(m); + } else if (m[2][2] != 0x0) { + swap_4x5_rows(m, 1, 2, 5); + zero_4x5_col2(m); + } else if (m[3][2] != 0x0) { + swap_4x5_rows(m, 1, 3, 5); + zero_4x5_col2(m); + } else { + printk(KERN_ERR "Error: find_4bit_err_coefs, cannot resolve col 2!\n"); + } + + if (m[2][1] != 0x0) { + zero_4x5_col1(m); + } else if (m[3][1] != 0x0) { + swap_4x5_rows(m, 2, 3, 5); + zero_4x5_col1(m); + } else { + printk(KERN_ERR "Error: find_4bit_err_coefs, cannot resolve col 1!\n"); + } + + solve_4x5(m, coefs, row_order); +} + +static void zero_4x5_col3(unsigned short m[4][5]) +{ + unsigned short minv1, minv2, minv3; + + minv1 = gf4096_mul(m[1][3], gf4096_inv(m[0][3])); + minv2 = gf4096_mul(m[2][3], gf4096_inv(m[0][3])); + minv3 = gf4096_mul(m[3][3], gf4096_inv(m[0][3])); + + m[1][0] = m[1][0] ^ gf4096_mul(m[0][0], minv1); + m[1][1] = m[1][1] ^ gf4096_mul(m[0][1], minv1); + m[1][2] = m[1][2] ^ gf4096_mul(m[0][2], minv1); + m[1][4] = m[1][4] ^ gf4096_mul(m[0][4], minv1); + m[2][0] = m[2][0] ^ gf4096_mul(m[0][0], minv2); + m[2][1] = m[2][1] ^ gf4096_mul(m[0][1], minv2); + m[2][2] = m[2][2] ^ gf4096_mul(m[0][2], minv2); + m[2][4] = m[2][4] ^ gf4096_mul(m[0][4], minv2); + m[3][0] = m[3][0] ^ gf4096_mul(m[0][0], minv3); + m[3][1] = m[3][1] ^ gf4096_mul(m[0][1], minv3); + m[3][2] = m[3][2] ^ gf4096_mul(m[0][2], minv3); + m[3][4] = m[3][4] ^ gf4096_mul(m[0][4], minv3); +} + +static void zero_4x5_col2(unsigned short m[4][5]) +{ + unsigned short minv2, minv3; + + minv2 = gf4096_mul(m[2][2], gf4096_inv(m[1][2])); + minv3 = gf4096_mul(m[3][2], gf4096_inv(m[1][2])); + + m[2][0] = m[2][0] ^ gf4096_mul(m[1][0], minv2); + m[2][1] = m[2][1] ^ gf4096_mul(m[1][1], minv2); + m[2][4] = m[2][4] ^ gf4096_mul(m[1][4], minv2); + m[3][0] = m[3][0] ^ gf4096_mul(m[1][0], minv3); + m[3][1] = m[3][1] ^ gf4096_mul(m[1][1], minv3); + m[3][4] = m[3][4] ^ gf4096_mul(m[1][4], minv3); +} + +static void zero_4x5_col1(unsigned short m[4][5]) +{ + unsigned short minv; + + minv = gf4096_mul(m[3][1], gf4096_inv(m[2][1])); + + m[3][0] = m[3][0] ^ gf4096_mul(m[2][0], minv); + m[3][4] = m[3][4] ^ gf4096_mul(m[2][4], minv); +} + +static void swap_4x5_rows(unsigned short m[4][5], int i, int j, int col_width) +{ + unsigned short tmp0; + int cnt; + + for (cnt = 0; cnt < col_width; cnt++) { + tmp0 = m[i][cnt]; + m[i][cnt] = m[j][cnt]; + m[j][cnt] = tmp0; + } +} + +static void solve_4x5(unsigned short m[4][5], unsigned short *coefs, int *row_order) +{ + unsigned short tmp[4]; + + tmp[0] = gf4096_mul(m[3][4], gf4096_inv(m[3][0])); + tmp[1] = gf4096_mul((gf4096_mul(tmp[0], m[2][0]) ^ m[2][4]), gf4096_inv(m[2][1])); + tmp[2] = gf4096_mul((gf4096_mul(tmp[0], m[1][0]) ^ gf4096_mul(tmp[1], m[1][1]) ^ m[1][4]), gf4096_inv(m[1][2])); + tmp[3] = gf4096_mul((gf4096_mul(tmp[0], m[0][0]) ^ + gf4096_mul(tmp[1], m[0][1]) ^ gf4096_mul(tmp[2], m[0][2]) ^ m[0][4]), gf4096_inv(m[0][3])); + sort_coefs(row_order, tmp, 4); + coefs[0] = tmp[0]; + coefs[1] = tmp[1]; + coefs[2] = tmp[2]; + coefs[3] = tmp[3]; +} + +static void sort_coefs(int *order, unsigned short *soln, int len) +{ + int cnt, start_cnt, least_ord, least_cnt; + unsigned short tmp0; + for (start_cnt = 0; start_cnt < len; start_cnt++) { + for (cnt = start_cnt; cnt < len; cnt++) { + if (cnt == start_cnt) { + least_ord = order[cnt]; + least_cnt = start_cnt; + } else { + if (least_ord > order[cnt]) { + least_ord = order[cnt]; + least_cnt = cnt; + } + } + } + if (least_cnt != start_cnt) { + tmp0 = order[least_cnt]; + order[least_cnt] = order[start_cnt]; + order[start_cnt] = tmp0; + tmp0 = soln[least_cnt]; + soln[least_cnt] = soln[start_cnt]; + soln[start_cnt] = tmp0; + } + } +} + +static void find_4bit_err_pats(unsigned short s0, unsigned short s1, + unsigned short s2, unsigned short s3, + unsigned short z1, unsigned short z2, + unsigned short z3, unsigned short z4, + unsigned short *pats) +{ + unsigned short z4_z1, z3z4_z3z3, z4_z2, s0z4_s1, z1z4_z1z1, + z4_z3, z2z4_z2z2, s1z4_s2, z3z3z4_z3z3z3, z1z1z4_z1z1z1, z2z2z4_z2z2z2, s2z4_s3; + unsigned short tmp0, tmp1, tmp2, tmp3; + + z4_z1 = z4 ^ z1; + z3z4_z3z3 = gf4096_mul(z3, z4) ^ gf4096_mul(z3, z3); + z4_z2 = z4 ^ z2; + s0z4_s1 = gf4096_mul(s0, z4) ^ s1; + z1z4_z1z1 = gf4096_mul(z1, z4) ^ gf4096_mul(z1, z1); + z4_z3 = z4 ^ z3; + z2z4_z2z2 = gf4096_mul(z2, z4) ^ gf4096_mul(z2, z2); + s1z4_s2 = gf4096_mul(s1, z4) ^ s2; + z3z3z4_z3z3z3 = gf4096_mul(gf4096_mul(z3, z3), z4) ^ gf4096_mul(gf4096_mul(z3, z3), z3); + z1z1z4_z1z1z1 = gf4096_mul(gf4096_mul(z1, z1), z4) ^ gf4096_mul(gf4096_mul(z1, z1), z1); + z2z2z4_z2z2z2 = gf4096_mul(gf4096_mul(z2, z2), z4) ^ gf4096_mul(gf4096_mul(z2, z2), z2); + s2z4_s3 = gf4096_mul(s2, z4) ^ s3; + + //find err pat 0,1 + find_2x2_soln(gf4096_mul(z4_z1, z3z4_z3z3) ^ + gf4096_mul(z1z4_z1z1, z4_z3), gf4096_mul(z4_z2, + z3z4_z3z3) ^ + gf4096_mul(z2z4_z2z2, z4_z3), gf4096_mul(z1z4_z1z1, + z3z3z4_z3z3z3) ^ + gf4096_mul(z1z1z4_z1z1z1, z3z4_z3z3), + gf4096_mul(z2z4_z2z2, + z3z3z4_z3z3z3) ^ gf4096_mul(z2z2z4_z2z2z2, + z3z4_z3z3), + gf4096_mul(s0z4_s1, z3z4_z3z3) ^ gf4096_mul(s1z4_s2, + z4_z3), + gf4096_mul(s1z4_s2, z3z3z4_z3z3z3) ^ gf4096_mul(s2z4_s3, z3z4_z3z3), pats); + tmp0 = pats[0]; + tmp1 = pats[1]; + tmp2 = pats[0] ^ pats[1] ^ s0; + tmp3 = gf4096_mul(pats[0], z1) ^ gf4096_mul(pats[1], z2) ^ s1; + + //find err pat 2,3 + find_2x2_soln(0x1, 0x1, z3, z4, tmp2, tmp3, pats); + pats[2] = pats[0]; + pats[3] = pats[1]; + pats[0] = tmp0; + pats[1] = tmp1; +} + +static void find_2x2_soln(unsigned short c00, unsigned short c01, + unsigned short c10, unsigned short c11, + unsigned short lval0, unsigned short lval1, + unsigned short *soln) +{ + unsigned short m[2][3]; + m[0][0] = c00; + m[0][1] = c01; + m[0][2] = lval0; + m[1][0] = c10; + m[1][1] = c11; + m[1][2] = lval1; + + if (m[0][1] != 0x0) { + /* */ + } else if (m[1][1] != 0x0) { + swap_2x3_rows(m); + } else { + printk(KERN_ERR "Warning: find_2bit_err_coefs, s0,s1 all zeros!\n"); + } + + solve_2x3(m, soln); +} + +static void swap_2x3_rows(unsigned short m[2][3]) +{ + unsigned short tmp0; + int cnt; + + for (cnt = 0; cnt < 3; cnt++) { + tmp0 = m[0][cnt]; + m[0][cnt] = m[1][cnt]; + m[1][cnt] = tmp0; + } +} + +static void solve_2x3(unsigned short m[2][3], unsigned short *coefs) +{ + unsigned short minv; + + minv = gf4096_mul(m[1][1], gf4096_inv(m[0][1])); + m[1][0] = m[1][0] ^ gf4096_mul(m[0][0], minv); + m[1][2] = m[1][2] ^ gf4096_mul(m[0][2], minv); + coefs[0] = gf4096_mul(m[1][2], gf4096_inv(m[1][0])); + coefs[1] = gf4096_mul((gf4096_mul(coefs[0], m[0][0]) ^ m[0][2]), gf4096_inv(m[0][1])); +} + +static unsigned char gf64_inv[64] = { + 0, 1, 33, 62, 49, 43, 31, 44, 57, 37, 52, 28, 46, 40, 22, 25, + 61, 54, 51, 39, 26, 35, 14, 24, 23, 15, 20, 34, 11, 53, 45, 6, + 63, 2, 27, 21, 56, 9, 50, 19, 13, 47, 48, 5, 7, 30, 12, 41, + 42, 4, 38, 18, 10, 29, 17, 60, 36, 8, 59, 58, 55, 16, 3, 32}; + +static unsigned short gf4096_inv(unsigned short din) +{ + unsigned short alahxal, ah2B, deno, inv, bl, bh; + unsigned short ah, al, ahxal; + unsigned short dout; + + ah = (din >> 6) & 0x3f; + al = din & 0x3f; + ahxal = ah ^ al; + ah2B = (((ah ^ (ah >> 3)) & 0x1) << 5) | + ((ah >> 1) & 0x10) | + ((((ah >> 5) ^ (ah >> 2)) & 0x1) << 3) | + ((ah >> 2) & 0x4) | ((((ah >> 4) ^ (ah >> 1)) & 0x1) << 1) | (ah & 0x1); + alahxal = gf64_mul(ahxal, al); + deno = alahxal ^ ah2B; + inv = gf64_inv[deno]; + bl = gf64_mul(inv, ahxal); + bh = gf64_mul(inv, ah); + dout = ((bh & 0x3f) << 6) | (bl & 0x3f); + return (((bh & 0x3f) << 6) | (bl & 0x3f)); +} + +static unsigned short err_pos_lut[4096] = { + 0xfff, 0x000, 0x451, 0xfff, 0xfff, 0x3cf, 0xfff, 0x041, + 0xfff, 0xfff, 0xfff, 0xfff, 0x28a, 0xfff, 0x492, 0xfff, + 0x145, 0xfff, 0xfff, 0x514, 0xfff, 0x082, 0xfff, 0xfff, + 0xfff, 0x249, 0x38e, 0x410, 0xfff, 0x104, 0x208, 0x1c7, + 0xfff, 0xfff, 0xfff, 0xfff, 0x2cb, 0xfff, 0xfff, 0xfff, + 0x0c3, 0x34d, 0x4d3, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x186, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x30c, 0x555, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x166, 0xfff, 0xfff, 0xfff, 0xfff, + 0x385, 0x14e, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4e1, + 0xfff, 0xfff, 0xfff, 0xfff, 0x538, 0xfff, 0x16d, 0xfff, + 0xfff, 0xfff, 0x45b, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x29c, 0x2cc, 0x30b, 0x2b3, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x0b3, 0xfff, 0x2f7, + 0xfff, 0x32b, 0xfff, 0xfff, 0xfff, 0xfff, 0x0a7, 0xfff, + 0xfff, 0x2da, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x07e, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x11c, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x22f, 0xfff, 0x1f4, 0xfff, 0xfff, + 0x2b0, 0x504, 0xfff, 0x114, 0xfff, 0xfff, 0xfff, 0x21d, + 0xfff, 0xfff, 0xfff, 0xfff, 0x00d, 0x3c4, 0x340, 0x10f, + 0xfff, 0xfff, 0x266, 0x02e, 0xfff, 0xfff, 0xfff, 0x4f8, + 0x337, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x07b, 0x168, 0xfff, 0xfff, 0x0fe, + 0xfff, 0xfff, 0x51a, 0xfff, 0x458, 0xfff, 0x36d, 0xfff, + 0xfff, 0xfff, 0xfff, 0x073, 0x37d, 0x415, 0x550, 0xfff, + 0xfff, 0xfff, 0x23b, 0x4b4, 0xfff, 0xfff, 0xfff, 0x1a1, + 0xfff, 0xfff, 0x3aa, 0xfff, 0x117, 0x04d, 0x341, 0xfff, + 0xfff, 0xfff, 0xfff, 0x518, 0x03e, 0x0f2, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x363, 0xfff, 0x0b9, 0xfff, 0xfff, + 0x241, 0xfff, 0xfff, 0x049, 0xfff, 0xfff, 0xfff, 0xfff, + 0x15f, 0x52d, 0xfff, 0xfff, 0xfff, 0x29e, 0xfff, 0xfff, + 0xfff, 0xfff, 0x4cf, 0x0fc, 0xfff, 0x36f, 0x3d3, 0xfff, + 0x228, 0xfff, 0xfff, 0x45e, 0xfff, 0xfff, 0xfff, 0xfff, + 0x238, 0xfff, 0xfff, 0xfff, 0xfff, 0x47f, 0xfff, 0xfff, + 0x43a, 0x265, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x3e8, + 0xfff, 0xfff, 0x01a, 0xfff, 0xfff, 0xfff, 0xfff, 0x21e, + 0x1fc, 0x40b, 0xfff, 0xfff, 0xfff, 0x2d0, 0x159, 0xfff, + 0xfff, 0x313, 0xfff, 0xfff, 0x05c, 0x4cc, 0xfff, 0xfff, + 0x0f6, 0x3d5, 0xfff, 0xfff, 0xfff, 0x54f, 0xfff, 0xfff, + 0xfff, 0x172, 0x1e4, 0x07c, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x53c, 0x1ad, 0x535, + 0x19b, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0x092, 0xfff, 0x2be, 0xfff, 0xfff, 0x482, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x0e6, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x476, 0xfff, 0x51d, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0x342, 0x2b5, 0x22e, 0x09a, 0xfff, 0x08d, + 0x44f, 0x3ed, 0xfff, 0xfff, 0xfff, 0xfff, 0x3d1, 0xfff, + 0xfff, 0x543, 0xfff, 0x48f, 0xfff, 0x3d2, 0xfff, 0x0d5, + 0x113, 0x0ec, 0x427, 0xfff, 0xfff, 0xfff, 0x4c4, 0xfff, + 0xfff, 0x50a, 0xfff, 0x144, 0xfff, 0x105, 0x39f, 0x294, + 0x164, 0xfff, 0x31a, 0xfff, 0xfff, 0x49a, 0xfff, 0x130, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0x1be, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0x49e, 0x371, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x0e8, 0x49c, 0x0f4, 0xfff, + 0x338, 0x1a7, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x36c, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x1ae, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x31b, 0xfff, 0xfff, 0x2dd, 0x522, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x2f4, + 0x3c6, 0x30d, 0xfff, 0xfff, 0xfff, 0xfff, 0x34c, 0x18f, + 0x30a, 0xfff, 0x01f, 0x079, 0xfff, 0xfff, 0x54d, 0x46b, + 0x28c, 0x37f, 0xfff, 0xfff, 0xfff, 0xfff, 0x355, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x14f, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x359, 0x3fe, 0x3c5, 0xfff, 0xfff, + 0xfff, 0xfff, 0x423, 0xfff, 0xfff, 0x34a, 0x22c, 0xfff, + 0x25a, 0xfff, 0xfff, 0x4ad, 0xfff, 0x28d, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x547, 0xfff, 0xfff, 0xfff, 0xfff, + 0x2e2, 0xfff, 0xfff, 0x1d5, 0xfff, 0x2a8, 0xfff, 0xfff, + 0x03f, 0xfff, 0xfff, 0xfff, 0xfff, 0x3eb, 0x0fa, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x55b, 0xfff, + 0x08e, 0xfff, 0x3ae, 0xfff, 0x3a4, 0xfff, 0x282, 0x158, + 0xfff, 0x382, 0xfff, 0xfff, 0x499, 0xfff, 0xfff, 0x08a, + 0xfff, 0xfff, 0xfff, 0x456, 0x3be, 0xfff, 0x1e2, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x559, 0xfff, 0x1a0, 0xfff, + 0xfff, 0x0b4, 0xfff, 0xfff, 0xfff, 0x2df, 0xfff, 0xfff, + 0xfff, 0x07f, 0x4f5, 0xfff, 0xfff, 0x27c, 0x133, 0x017, + 0xfff, 0x3fd, 0xfff, 0xfff, 0xfff, 0x44d, 0x4cd, 0x17a, + 0x0d7, 0x537, 0xfff, 0xfff, 0x353, 0xfff, 0xfff, 0x351, + 0x366, 0xfff, 0x44a, 0xfff, 0x1a6, 0xfff, 0xfff, 0xfff, + 0x291, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x1e3, + 0xfff, 0xfff, 0xfff, 0xfff, 0x389, 0xfff, 0x07a, 0xfff, + 0x1b6, 0x2ed, 0xfff, 0xfff, 0xfff, 0xfff, 0x24e, 0x074, + 0xfff, 0xfff, 0x3dc, 0xfff, 0x4e3, 0xfff, 0xfff, 0xfff, + 0xfff, 0x4eb, 0xfff, 0xfff, 0x3b8, 0x4de, 0xfff, 0x19c, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x262, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x076, 0x4e8, 0x3da, + 0xfff, 0x531, 0xfff, 0xfff, 0x14a, 0xfff, 0x0a2, 0x433, + 0x3df, 0x1e9, 0xfff, 0xfff, 0xfff, 0xfff, 0x3e7, 0x285, + 0x2d8, 0xfff, 0xfff, 0xfff, 0x349, 0x18d, 0x098, 0xfff, + 0x0df, 0x4bf, 0xfff, 0xfff, 0x0b2, 0xfff, 0x346, 0x24d, + 0xfff, 0xfff, 0xfff, 0x24f, 0x4fa, 0x2f9, 0xfff, 0xfff, + 0x3c9, 0xfff, 0x2b4, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x056, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x179, 0xfff, 0x0e9, 0x3f0, 0x33d, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x1fd, 0xfff, 0xfff, 0x526, 0xfff, + 0xfff, 0xfff, 0x53d, 0xfff, 0xfff, 0xfff, 0x170, 0x331, + 0xfff, 0x068, 0xfff, 0xfff, 0xfff, 0x3f7, 0xfff, 0x3d8, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x09f, 0x556, 0xfff, 0xfff, 0x02d, 0xfff, 0xfff, + 0x553, 0xfff, 0xfff, 0xfff, 0x1f0, 0xfff, 0xfff, 0x4d6, + 0x41e, 0xfff, 0xfff, 0xfff, 0xfff, 0x4d5, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x248, 0xfff, 0xfff, 0xfff, 0x0a3, + 0xfff, 0x217, 0xfff, 0xfff, 0xfff, 0x4f1, 0x209, 0xfff, + 0xfff, 0x475, 0x234, 0x52b, 0x398, 0xfff, 0x08b, 0xfff, + 0xfff, 0xfff, 0xfff, 0x2c2, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0x268, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x4a3, 0xfff, 0x0aa, 0xfff, 0x1d9, 0xfff, 0xfff, + 0xfff, 0xfff, 0x155, 0xfff, 0xfff, 0xfff, 0xfff, 0x0bf, + 0x539, 0xfff, 0xfff, 0x2f1, 0x545, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x2a7, 0x06f, 0xfff, 0x378, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x25e, 0xfff, + 0xfff, 0xfff, 0xfff, 0x15d, 0x02a, 0xfff, 0xfff, 0x0bc, + 0x235, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0x150, 0xfff, 0x1a9, 0xfff, 0xfff, 0xfff, 0xfff, 0x381, + 0xfff, 0x04e, 0x270, 0x13f, 0xfff, 0xfff, 0x405, 0xfff, + 0x3cd, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x2ef, 0xfff, 0x06a, 0xfff, 0xfff, 0xfff, 0x34f, + 0x212, 0xfff, 0xfff, 0x0e2, 0xfff, 0x083, 0x298, 0xfff, + 0xfff, 0xfff, 0x0c2, 0xfff, 0xfff, 0x52e, 0xfff, 0x488, + 0xfff, 0xfff, 0xfff, 0x36b, 0xfff, 0xfff, 0xfff, 0x442, + 0x091, 0xfff, 0x41c, 0xfff, 0xfff, 0x3a5, 0xfff, 0x4e6, + 0xfff, 0xfff, 0x40d, 0x31d, 0xfff, 0xfff, 0xfff, 0x4c1, + 0x053, 0xfff, 0x418, 0x13c, 0xfff, 0x350, 0xfff, 0x0ae, + 0xfff, 0xfff, 0x41f, 0xfff, 0x470, 0xfff, 0x4ca, 0xfff, + 0xfff, 0xfff, 0x02b, 0x450, 0xfff, 0x1f8, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x293, 0xfff, + 0xfff, 0xfff, 0xfff, 0x411, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x0b8, 0xfff, 0xfff, 0xfff, + 0x3e1, 0xfff, 0xfff, 0xfff, 0xfff, 0x43c, 0xfff, 0x2b2, + 0x2ab, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x1ec, + 0xfff, 0xfff, 0xfff, 0x3f8, 0x034, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x11a, 0xfff, 0x541, 0x45c, 0x134, + 0x1cc, 0xfff, 0xfff, 0xfff, 0x469, 0xfff, 0xfff, 0x44b, + 0x161, 0xfff, 0xfff, 0xfff, 0x055, 0xfff, 0xfff, 0xfff, + 0xfff, 0x307, 0xfff, 0xfff, 0xfff, 0xfff, 0x2d1, 0xfff, + 0xfff, 0xfff, 0x124, 0x37b, 0x26b, 0x336, 0xfff, 0xfff, + 0x2e4, 0x3cb, 0xfff, 0xfff, 0x0f8, 0x3c8, 0xfff, 0xfff, + 0xfff, 0x461, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4b5, + 0x2cf, 0xfff, 0xfff, 0xfff, 0x20f, 0xfff, 0x35a, 0xfff, + 0x490, 0xfff, 0x185, 0xfff, 0xfff, 0xfff, 0xfff, 0x42e, + 0xfff, 0xfff, 0xfff, 0xfff, 0x54b, 0xfff, 0xfff, 0xfff, + 0x146, 0xfff, 0x412, 0xfff, 0xfff, 0xfff, 0x1ff, 0xfff, + 0xfff, 0x3e0, 0xfff, 0xfff, 0xfff, 0xfff, 0x2d5, 0xfff, + 0x4df, 0x505, 0xfff, 0x413, 0xfff, 0x1a5, 0xfff, 0x3b2, + 0xfff, 0xfff, 0xfff, 0x35b, 0xfff, 0x116, 0xfff, 0xfff, + 0x171, 0x4d0, 0xfff, 0x154, 0x12d, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x468, 0x4db, 0xfff, + 0xfff, 0x1df, 0xfff, 0xfff, 0xfff, 0xfff, 0x05a, 0xfff, + 0x0f1, 0x403, 0xfff, 0x22b, 0x2e0, 0xfff, 0xfff, 0xfff, + 0x2b7, 0x373, 0xfff, 0xfff, 0xfff, 0xfff, 0x13e, 0xfff, + 0xfff, 0xfff, 0x0d0, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0x329, 0x1d2, 0x3fa, 0x047, 0xfff, 0x2f2, 0xfff, 0xfff, + 0x141, 0x0ac, 0x1d7, 0xfff, 0x07d, 0xfff, 0xfff, 0xfff, + 0x1c1, 0xfff, 0x487, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x045, 0xfff, 0xfff, 0xfff, 0xfff, + 0x288, 0x0cd, 0xfff, 0xfff, 0xfff, 0xfff, 0x226, 0x1d8, + 0xfff, 0x153, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4cb, + 0x528, 0xfff, 0xfff, 0xfff, 0x20a, 0x343, 0x3a1, 0xfff, + 0xfff, 0xfff, 0x2d7, 0x2d3, 0x1aa, 0x4c5, 0xfff, 0xfff, + 0xfff, 0x42b, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x3e9, 0xfff, 0x20b, 0x260, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x37c, 0x2fd, + 0xfff, 0xfff, 0x2c8, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x31e, 0xfff, 0x335, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0x135, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0x35c, 0x4dd, 0x129, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0x1ef, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x34e, 0xfff, 0xfff, 0xfff, 0xfff, 0x407, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x3ad, 0xfff, 0xfff, 0xfff, + 0x379, 0xfff, 0xfff, 0x1d0, 0x38d, 0xfff, 0xfff, 0x1e8, + 0x184, 0x3c1, 0x1c4, 0xfff, 0x1f9, 0xfff, 0xfff, 0x424, + 0xfff, 0xfff, 0xfff, 0xfff, 0x1d3, 0x0d4, 0xfff, 0x4e9, + 0xfff, 0xfff, 0xfff, 0x530, 0x107, 0xfff, 0x106, 0x04f, + 0xfff, 0xfff, 0x4c7, 0x503, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x15c, 0xfff, 0x23f, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x4f3, 0xfff, 0xfff, 0x3c7, + 0xfff, 0x278, 0xfff, 0xfff, 0x0a6, 0xfff, 0xfff, 0xfff, + 0x122, 0x1cf, 0xfff, 0x327, 0xfff, 0x2e5, 0xfff, 0x29d, + 0xfff, 0xfff, 0x3f1, 0xfff, 0xfff, 0x48d, 0xfff, 0xfff, + 0xfff, 0xfff, 0x054, 0xfff, 0xfff, 0xfff, 0xfff, 0x178, + 0x27e, 0x4e0, 0x352, 0x02f, 0x09c, 0xfff, 0x2a0, 0xfff, + 0xfff, 0x46a, 0x457, 0xfff, 0xfff, 0x501, 0xfff, 0x2ba, + 0xfff, 0xfff, 0xfff, 0x54e, 0x2e7, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0x551, 0xfff, 0xfff, 0x1db, 0x2aa, 0xfff, + 0xfff, 0x4bc, 0xfff, 0xfff, 0x395, 0xfff, 0x0de, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x455, 0xfff, 0x17e, + 0xfff, 0x221, 0x4a7, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0x388, 0xfff, 0xfff, 0xfff, 0x308, 0xfff, 0xfff, 0xfff, + 0x20e, 0x4b9, 0xfff, 0x273, 0x20c, 0x09e, 0xfff, 0x057, + 0xfff, 0xfff, 0xfff, 0xfff, 0x3f2, 0xfff, 0x1a8, 0x3a6, + 0x14c, 0xfff, 0xfff, 0x071, 0xfff, 0xfff, 0x53a, 0xfff, + 0xfff, 0xfff, 0xfff, 0x109, 0xfff, 0xfff, 0x399, 0xfff, + 0x061, 0x4f0, 0x39e, 0x244, 0xfff, 0x035, 0xfff, 0xfff, + 0x305, 0x47e, 0x297, 0xfff, 0xfff, 0x2b8, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x1bc, 0xfff, 0x2fc, + 0xfff, 0xfff, 0x554, 0xfff, 0xfff, 0xfff, 0xfff, 0x3b6, + 0xfff, 0xfff, 0xfff, 0x515, 0x397, 0xfff, 0xfff, 0x12f, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4e5, + 0xfff, 0x4fc, 0xfff, 0xfff, 0x05e, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0x0a8, 0x3af, 0x015, 0xfff, 0xfff, 0xfff, + 0xfff, 0x138, 0xfff, 0xfff, 0xfff, 0x540, 0xfff, 0xfff, + 0xfff, 0x027, 0x523, 0x2f0, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0x16c, 0xfff, 0x27d, 0xfff, 0xfff, 0xfff, + 0xfff, 0x04c, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4dc, + 0xfff, 0xfff, 0x059, 0x301, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x1a3, 0xfff, 0x15a, 0xfff, 0xfff, + 0x0a5, 0xfff, 0x435, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x051, 0xfff, 0xfff, 0x131, 0xfff, 0x4f4, 0xfff, + 0xfff, 0xfff, 0xfff, 0x441, 0xfff, 0x4fb, 0xfff, 0x03b, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x1ed, 0x274, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x0d3, 0x55e, 0x1b3, + 0xfff, 0x0bd, 0xfff, 0xfff, 0xfff, 0xfff, 0x225, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x4b7, 0xfff, 0xfff, 0x2ff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4c3, 0xfff, + 0x383, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x2f6, + 0xfff, 0xfff, 0x1ee, 0xfff, 0x03d, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0x26f, 0x1dc, 0xfff, 0x0db, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x0ce, 0xfff, 0xfff, 0x127, 0x03a, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x311, 0xfff, + 0xfff, 0x13d, 0x09d, 0x47b, 0x2a6, 0x50d, 0x510, 0x19a, + 0xfff, 0x354, 0x414, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0x44c, 0x3b0, 0xfff, 0x23d, 0x429, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0x4c0, 0x416, 0xfff, 0x05b, 0xfff, 0xfff, 0x137, 0xfff, + 0x25f, 0x49f, 0xfff, 0x279, 0x013, 0xfff, 0xfff, 0xfff, + 0x269, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x3d0, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x077, 0xfff, 0xfff, 0x3fb, + 0xfff, 0xfff, 0xfff, 0xfff, 0x271, 0x3a0, 0xfff, 0xfff, + 0x40f, 0xfff, 0xfff, 0x3de, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x1ab, 0x26a, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x489, 0xfff, 0xfff, + 0x252, 0xfff, 0xfff, 0xfff, 0xfff, 0x1b7, 0x42f, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x3b7, + 0xfff, 0x2bb, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x0f7, 0x01d, 0xfff, 0x067, 0xfff, + 0xfff, 0xfff, 0xfff, 0x4e2, 0xfff, 0xfff, 0x4bb, 0xfff, + 0xfff, 0xfff, 0x17b, 0xfff, 0x0ee, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0x36e, 0xfff, 0xfff, 0xfff, 0x533, 0xfff, + 0xfff, 0xfff, 0x4d4, 0x356, 0xfff, 0xfff, 0x375, 0xfff, + 0xfff, 0xfff, 0xfff, 0x4a4, 0x513, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4ff, 0xfff, 0x2af, + 0xfff, 0xfff, 0x026, 0xfff, 0x0ad, 0xfff, 0xfff, 0xfff, + 0xfff, 0x26e, 0xfff, 0xfff, 0xfff, 0xfff, 0x493, 0xfff, + 0x463, 0x4d2, 0x4be, 0xfff, 0xfff, 0xfff, 0xfff, 0x4f2, + 0x0b6, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x32d, 0x315, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x13a, 0x4a1, 0xfff, 0x27a, 0xfff, 0xfff, 0xfff, + 0x47a, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0x334, 0xfff, 0xfff, 0xfff, 0xfff, 0x54c, 0xfff, 0xfff, + 0xfff, 0x0c9, 0x007, 0xfff, 0xfff, 0x12e, 0xfff, 0x0ff, + 0xfff, 0xfff, 0x3f5, 0x509, 0xfff, 0xfff, 0xfff, 0xfff, + 0x1c3, 0x2ad, 0xfff, 0xfff, 0x47c, 0x261, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x152, 0xfff, 0xfff, 0xfff, 0x339, + 0xfff, 0x243, 0x1c0, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0x063, 0xfff, 0xfff, 0x254, 0xfff, 0xfff, 0x173, 0xfff, + 0x0c7, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x362, 0x259, 0x485, 0x374, 0x0dc, 0x3ab, 0xfff, + 0x1c5, 0x534, 0x544, 0xfff, 0xfff, 0x508, 0xfff, 0x402, + 0x408, 0xfff, 0x0e7, 0xfff, 0xfff, 0x00a, 0x205, 0xfff, + 0xfff, 0x2b9, 0xfff, 0xfff, 0xfff, 0x465, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x23a, 0xfff, 0xfff, 0xfff, + 0xfff, 0x147, 0x19d, 0x115, 0x214, 0xfff, 0x090, 0x368, + 0xfff, 0x210, 0xfff, 0xfff, 0x280, 0x52a, 0x163, 0x148, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x326, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x2de, 0xfff, 0xfff, 0xfff, 0xfff, + 0x206, 0x2c1, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0x189, 0xfff, 0xfff, 0xfff, 0xfff, 0x367, 0xfff, 0x1a4, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x443, 0xfff, 0x27b, + 0xfff, 0xfff, 0x251, 0x549, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0x188, 0x04b, 0xfff, 0xfff, 0xfff, 0x31f, + 0x4a6, 0xfff, 0x246, 0x1de, 0x156, 0xfff, 0xfff, 0xfff, + 0x3a9, 0xfff, 0xfff, 0xfff, 0x2fa, 0xfff, 0x128, 0x0d1, + 0x449, 0x255, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x258, 0xfff, 0xfff, 0xfff, + 0x532, 0xfff, 0xfff, 0xfff, 0x303, 0x517, 0xfff, 0xfff, + 0x2a9, 0x24a, 0xfff, 0xfff, 0x231, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0x4b6, 0x516, 0xfff, 0xfff, 0x0e4, 0x0eb, + 0xfff, 0x4e4, 0xfff, 0x275, 0xfff, 0xfff, 0x031, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x025, 0x21a, 0xfff, 0x0cc, + 0x45f, 0x3d9, 0x289, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0x23e, 0xfff, 0xfff, 0xfff, 0x438, 0x097, + 0x419, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0x0a9, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0x37e, 0x0e0, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x431, + 0x372, 0xfff, 0xfff, 0xfff, 0x1ba, 0x06e, 0xfff, 0x1b1, + 0xfff, 0xfff, 0x12a, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0x193, 0xfff, 0xfff, 0xfff, 0xfff, 0x10a, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x048, 0x1b4, + 0xfff, 0xfff, 0xfff, 0xfff, 0x295, 0x140, 0x108, 0xfff, + 0xfff, 0xfff, 0xfff, 0x16f, 0xfff, 0x0a4, 0x37a, 0xfff, + 0x29a, 0xfff, 0x284, 0xfff, 0xfff, 0xfff, 0xfff, 0x4c6, + 0x2a2, 0x3a3, 0xfff, 0x201, 0xfff, 0xfff, 0xfff, 0x4bd, + 0x005, 0x54a, 0x3b5, 0x204, 0x2ee, 0x11d, 0x436, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x3ec, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x11f, 0x498, 0x21c, 0xfff, + 0xfff, 0xfff, 0x3d6, 0xfff, 0x4ab, 0xfff, 0x432, 0x2eb, + 0x542, 0x4fd, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x4ce, 0xfff, 0xfff, 0x2fb, 0xfff, + 0xfff, 0x2e1, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x1b9, 0x037, 0x0dd, + 0xfff, 0xfff, 0xfff, 0x2bf, 0x521, 0x496, 0x095, 0xfff, + 0xfff, 0x328, 0x070, 0x1bf, 0xfff, 0x393, 0xfff, 0xfff, + 0x102, 0xfff, 0xfff, 0x21b, 0xfff, 0x142, 0x263, 0x519, + 0xfff, 0x2a5, 0x177, 0xfff, 0x14d, 0x471, 0x4ae, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0x1f6, 0xfff, 0x481, 0xfff, 0xfff, 0xfff, 0x151, 0xfff, + 0xfff, 0xfff, 0x085, 0x33f, 0xfff, 0xfff, 0xfff, 0x084, + 0xfff, 0xfff, 0xfff, 0x345, 0x3a2, 0xfff, 0xfff, 0x0a0, + 0x0da, 0x024, 0xfff, 0xfff, 0xfff, 0x1bd, 0xfff, 0x55c, + 0x467, 0x445, 0xfff, 0xfff, 0xfff, 0x052, 0xfff, 0xfff, + 0xfff, 0xfff, 0x51e, 0xfff, 0xfff, 0x39d, 0xfff, 0x35f, + 0xfff, 0x376, 0x3ee, 0xfff, 0xfff, 0xfff, 0xfff, 0x448, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x16a, + 0xfff, 0x036, 0x38f, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x211, + 0xfff, 0xfff, 0xfff, 0x230, 0xfff, 0xfff, 0x3ba, 0xfff, + 0xfff, 0xfff, 0x3ce, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x229, 0xfff, 0x176, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x00b, 0xfff, 0x162, 0x018, 0xfff, + 0xfff, 0x233, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0x400, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x12b, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x3f4, 0xfff, 0x0f0, 0xfff, 0x1ac, 0xfff, 0xfff, + 0x119, 0xfff, 0x2c0, 0xfff, 0xfff, 0xfff, 0x49b, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x23c, 0xfff, + 0x4b3, 0x010, 0x064, 0xfff, 0xfff, 0x4ba, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x3c2, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x006, 0x196, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x100, 0x191, 0xfff, + 0x1ea, 0x29f, 0xfff, 0xfff, 0xfff, 0x276, 0xfff, 0xfff, + 0x2b1, 0x3b9, 0xfff, 0x03c, 0xfff, 0xfff, 0xfff, 0x180, + 0xfff, 0x08f, 0xfff, 0xfff, 0x19e, 0x019, 0xfff, 0x0b0, + 0x0fd, 0x332, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x06b, 0x2e8, 0xfff, 0x446, 0xfff, 0xfff, 0x004, + 0x247, 0x197, 0xfff, 0x112, 0x169, 0x292, 0xfff, 0x302, + 0xfff, 0xfff, 0x33b, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x287, 0x21f, 0xfff, 0x3ea, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4e7, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x3a8, 0xfff, 0xfff, 0x2bc, 0xfff, + 0x484, 0x296, 0xfff, 0x1c9, 0x08c, 0x1e5, 0x48a, 0xfff, + 0x360, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0x1ca, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x10d, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0x066, 0x2ea, 0x28b, 0x25b, 0xfff, 0x072, + 0xfff, 0xfff, 0xfff, 0xfff, 0x2b6, 0xfff, 0xfff, 0x272, + 0xfff, 0xfff, 0x525, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0x2ca, 0xfff, 0xfff, 0xfff, 0x299, 0xfff, 0xfff, 0xfff, + 0x558, 0x41a, 0xfff, 0x4f7, 0x557, 0xfff, 0x4a0, 0x344, + 0x12c, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x125, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0x40e, 0xfff, 0xfff, 0x502, 0xfff, 0x103, 0x3e6, 0xfff, + 0x527, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x45d, 0xfff, 0xfff, 0xfff, 0xfff, + 0x44e, 0xfff, 0xfff, 0xfff, 0xfff, 0x0d2, 0x4c9, 0x35e, + 0x459, 0x2d9, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x17d, + 0x0c4, 0xfff, 0xfff, 0xfff, 0x3ac, 0x390, 0x094, 0xfff, + 0x483, 0x0ab, 0xfff, 0x253, 0xfff, 0x391, 0xfff, 0xfff, + 0xfff, 0xfff, 0x123, 0x0ef, 0xfff, 0xfff, 0xfff, 0x330, + 0x38c, 0xfff, 0xfff, 0x2ae, 0xfff, 0xfff, 0xfff, 0x042, + 0x012, 0x06d, 0xfff, 0xfff, 0xfff, 0x32a, 0x3db, 0x364, + 0x2dc, 0xfff, 0x30f, 0x3d7, 0x4a5, 0x050, 0xfff, 0xfff, + 0x029, 0xfff, 0xfff, 0xfff, 0xfff, 0x1d1, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x480, 0xfff, + 0x4ed, 0x081, 0x0a1, 0xfff, 0xfff, 0xfff, 0x30e, 0x52f, + 0x257, 0xfff, 0xfff, 0x447, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x401, 0x3cc, 0xfff, 0xfff, 0x0fb, + 0x2c9, 0x42a, 0x314, 0x33e, 0x3bd, 0x318, 0xfff, 0x10e, + 0x2a1, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x24c, + 0x506, 0xfff, 0x267, 0xfff, 0xfff, 0x219, 0xfff, 0x1eb, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0x309, 0x3e2, 0x46c, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0x384, 0xfff, 0xfff, 0xfff, 0xfff, 0x50c, 0xfff, 0x24b, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x038, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x194, + 0x143, 0x3e3, 0xfff, 0xfff, 0xfff, 0x4c2, 0xfff, 0xfff, + 0x0e1, 0x25c, 0xfff, 0x237, 0xfff, 0x1fe, 0xfff, 0xfff, + 0xfff, 0x065, 0x2a4, 0xfff, 0x386, 0x55a, 0x11b, 0xfff, + 0xfff, 0x192, 0xfff, 0x183, 0x00e, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x4b2, 0x18e, 0xfff, 0xfff, 0xfff, + 0xfff, 0x486, 0x4ef, 0x0c6, 0x380, 0xfff, 0x4a8, 0xfff, + 0x0c5, 0xfff, 0xfff, 0xfff, 0xfff, 0x093, 0x1b8, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x2e6, + 0xfff, 0x0f3, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0x28e, 0xfff, 0x53b, 0x420, 0x22a, 0x33a, 0xfff, 0x387, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x2a3, 0xfff, 0xfff, + 0xfff, 0x428, 0x500, 0xfff, 0xfff, 0x120, 0x2c6, 0x290, + 0x2f5, 0x0e3, 0xfff, 0x0b7, 0xfff, 0x319, 0x474, 0xfff, + 0xfff, 0xfff, 0x529, 0x014, 0xfff, 0x41b, 0x40a, 0x18b, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x0d9, + 0xfff, 0x38a, 0xfff, 0xfff, 0xfff, 0xfff, 0x1ce, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x3b1, 0xfff, 0xfff, 0x05d, + 0x2c4, 0xfff, 0xfff, 0x4af, 0xfff, 0x030, 0xfff, 0xfff, + 0x203, 0xfff, 0x277, 0x256, 0xfff, 0xfff, 0xfff, 0x4f9, + 0xfff, 0x2c7, 0xfff, 0x466, 0x016, 0x1cd, 0xfff, 0x167, + 0xfff, 0xfff, 0x0c8, 0xfff, 0x43d, 0xfff, 0xfff, 0x020, + 0xfff, 0xfff, 0x232, 0x1cb, 0x1e0, 0xfff, 0xfff, 0x347, + 0xfff, 0x478, 0xfff, 0x365, 0xfff, 0xfff, 0xfff, 0xfff, + 0x358, 0xfff, 0x10b, 0xfff, 0x35d, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0x452, 0x22d, 0xfff, 0xfff, 0x47d, 0xfff, + 0x2f3, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x460, 0xfff, + 0xfff, 0xfff, 0x50b, 0xfff, 0xfff, 0xfff, 0x2ec, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x4b1, 0x422, 0xfff, 0xfff, + 0xfff, 0x2d4, 0xfff, 0x239, 0xfff, 0xfff, 0xfff, 0x439, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x491, 0x075, 0xfff, 0xfff, 0xfff, 0x06c, 0xfff, + 0xfff, 0x0f9, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x139, 0xfff, 0x4f6, 0xfff, 0xfff, 0x409, 0xfff, + 0xfff, 0x15b, 0xfff, 0xfff, 0x348, 0xfff, 0xfff, 0xfff, + 0xfff, 0x4a2, 0x49d, 0xfff, 0x033, 0x175, 0xfff, 0x039, + 0xfff, 0x312, 0x40c, 0xfff, 0xfff, 0x325, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x4aa, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x165, 0x3bc, 0x48c, 0x310, 0x096, + 0xfff, 0xfff, 0x250, 0x1a2, 0xfff, 0xfff, 0xfff, 0xfff, + 0x20d, 0x2ac, 0xfff, 0xfff, 0x39b, 0xfff, 0x377, 0xfff, + 0x512, 0x495, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x357, 0x4ea, 0xfff, 0xfff, + 0xfff, 0xfff, 0x198, 0xfff, 0xfff, 0xfff, 0x434, 0x04a, + 0xfff, 0xfff, 0xfff, 0xfff, 0x062, 0xfff, 0x1d6, 0x1c8, + 0xfff, 0x1f3, 0x281, 0xfff, 0x462, 0xfff, 0xfff, 0xfff, + 0x4b0, 0xfff, 0x207, 0xfff, 0xfff, 0xfff, 0xfff, 0x3dd, + 0xfff, 0xfff, 0x55d, 0xfff, 0x552, 0x494, 0x1af, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x227, 0xfff, 0xfff, 0x069, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x43e, + 0x0b5, 0xfff, 0x524, 0x2d2, 0xfff, 0xfff, 0xfff, 0x28f, + 0xfff, 0x01b, 0x50e, 0xfff, 0xfff, 0x1bb, 0xfff, 0xfff, + 0x41d, 0xfff, 0x32e, 0x48e, 0xfff, 0x1f7, 0x224, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x394, 0xfff, 0xfff, 0xfff, + 0xfff, 0x52c, 0xfff, 0xfff, 0xfff, 0x392, 0xfff, 0x1e7, + 0xfff, 0xfff, 0x3f9, 0x3a7, 0xfff, 0x51f, 0xfff, 0x0bb, + 0x118, 0x3ca, 0xfff, 0x1dd, 0xfff, 0x48b, 0xfff, 0xfff, + 0xfff, 0xfff, 0x50f, 0xfff, 0x0d6, 0xfff, 0x1fa, 0xfff, + 0x11e, 0xfff, 0xfff, 0xfff, 0xfff, 0x4d7, 0xfff, 0x078, + 0x008, 0xfff, 0x25d, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0x032, 0x33c, 0xfff, 0x4d9, 0x160, 0xfff, 0xfff, 0x300, + 0x0b1, 0xfff, 0x322, 0xfff, 0x4ec, 0xfff, 0xfff, 0x200, + 0x00c, 0x369, 0x473, 0xfff, 0xfff, 0x32c, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x53e, 0x3d4, 0x417, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0x34b, 0x001, 0x39a, 0x02c, 0xfff, 0xfff, 0x2ce, 0x00f, + 0xfff, 0x0ba, 0xfff, 0xfff, 0xfff, 0xfff, 0x060, 0xfff, + 0x406, 0xfff, 0xfff, 0xfff, 0x4ee, 0x4ac, 0xfff, 0x43f, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x29b, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x216, + 0x190, 0xfff, 0x396, 0x464, 0xfff, 0xfff, 0x323, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x2e9, 0xfff, 0x26d, + 0x2cd, 0x040, 0xfff, 0xfff, 0xfff, 0xfff, 0x38b, 0x3c0, + 0xfff, 0xfff, 0xfff, 0x1f2, 0xfff, 0x0ea, 0xfff, 0xfff, + 0x472, 0xfff, 0x1fb, 0xfff, 0xfff, 0x0af, 0x27f, 0xfff, + 0xfff, 0xfff, 0x479, 0x023, 0xfff, 0x0d8, 0x3b3, 0xfff, + 0xfff, 0xfff, 0x121, 0xfff, 0xfff, 0x3bf, 0xfff, 0xfff, + 0x16b, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0x45a, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x0be, 0xfff, 0xfff, 0xfff, 0x111, 0xfff, 0x220, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0x09b, 0x218, 0xfff, 0x022, 0x202, 0xfff, + 0x4c8, 0xfff, 0x0ed, 0xfff, 0xfff, 0x182, 0xfff, 0xfff, + 0xfff, 0x17f, 0x213, 0xfff, 0x321, 0x36a, 0xfff, 0x086, + 0xfff, 0xfff, 0xfff, 0x43b, 0x088, 0xfff, 0xfff, 0xfff, + 0xfff, 0x26c, 0xfff, 0x2f8, 0x3b4, 0xfff, 0xfff, 0xfff, + 0x132, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x333, 0x444, + 0x0c1, 0x4d8, 0x46d, 0x264, 0xfff, 0xfff, 0xfff, 0xfff, + 0x426, 0xfff, 0xfff, 0xfff, 0xfff, 0x2fe, 0xfff, 0xfff, + 0xfff, 0xfff, 0x011, 0xfff, 0x05f, 0xfff, 0xfff, 0xfff, + 0xfff, 0x10c, 0x101, 0xfff, 0xfff, 0xfff, 0xfff, 0x110, + 0xfff, 0x044, 0x304, 0x361, 0x404, 0xfff, 0x51b, 0x099, + 0xfff, 0x440, 0xfff, 0xfff, 0xfff, 0x222, 0xfff, 0xfff, + 0xfff, 0xfff, 0x1b5, 0xfff, 0x136, 0x430, 0xfff, 0x1da, + 0xfff, 0xfff, 0xfff, 0x043, 0xfff, 0x17c, 0xfff, 0xfff, + 0xfff, 0x01c, 0xfff, 0xfff, 0xfff, 0x425, 0x236, 0xfff, + 0x317, 0xfff, 0xfff, 0x437, 0x3fc, 0xfff, 0x1f1, 0xfff, + 0x324, 0xfff, 0xfff, 0x0ca, 0x306, 0xfff, 0x548, 0xfff, + 0x46e, 0xfff, 0xfff, 0xfff, 0x4b8, 0x1c2, 0x286, 0xfff, + 0xfff, 0x087, 0x18a, 0x19f, 0xfff, 0xfff, 0xfff, 0xfff, + 0x18c, 0xfff, 0x215, 0xfff, 0xfff, 0xfff, 0xfff, 0x283, + 0xfff, 0xfff, 0xfff, 0x126, 0xfff, 0xfff, 0x370, 0xfff, + 0x53f, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x31c, 0xfff, + 0x4d1, 0xfff, 0xfff, 0xfff, 0x021, 0xfff, 0x157, 0xfff, + 0xfff, 0x028, 0x16e, 0xfff, 0x421, 0xfff, 0x1c6, 0xfff, + 0xfff, 0x511, 0xfff, 0xfff, 0x39c, 0x46f, 0x1b2, 0xfff, + 0xfff, 0x316, 0xfff, 0xfff, 0x009, 0xfff, 0xfff, 0x195, + 0xfff, 0x240, 0x546, 0xfff, 0xfff, 0x520, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x454, 0xfff, 0xfff, 0xfff, + 0x3f3, 0xfff, 0xfff, 0x187, 0xfff, 0x4a9, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x51c, 0x453, 0x1e6, 0xfff, + 0xfff, 0xfff, 0x1b0, 0xfff, 0x477, 0xfff, 0xfff, 0xfff, + 0x4fe, 0xfff, 0x32f, 0xfff, 0xfff, 0x15e, 0x1d4, 0xfff, + 0x0e5, 0xfff, 0xfff, 0xfff, 0x242, 0x14b, 0x046, 0xfff, + 0x3f6, 0x3bb, 0x3e4, 0xfff, 0xfff, 0x2e3, 0xfff, 0x245, + 0xfff, 0x149, 0xfff, 0xfff, 0xfff, 0x2db, 0xfff, 0xfff, + 0x181, 0xfff, 0x089, 0x2c5, 0xfff, 0x1f5, 0xfff, 0x2d6, + 0x507, 0xfff, 0x42d, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0x080, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x3c3, 0x320, 0xfff, 0x1e1, + 0xfff, 0x0f5, 0x13b, 0xfff, 0xfff, 0xfff, 0x003, 0x4da, + 0xfff, 0xfff, 0xfff, 0x42c, 0xfff, 0xfff, 0x0cb, 0xfff, + 0x536, 0x2c3, 0xfff, 0xfff, 0xfff, 0xfff, 0x199, 0xfff, + 0xfff, 0x0c0, 0xfff, 0x01e, 0x497, 0xfff, 0xfff, 0x3e5, + 0xfff, 0xfff, 0xfff, 0x0cf, 0xfff, 0x2bd, 0xfff, 0x223, + 0xfff, 0x3ff, 0xfff, 0x058, 0x174, 0x3ef, 0xfff, 0x002 +}; + +static unsigned short err_pos(unsigned short din) +{ + BUG_ON(din > 4096); + BUG_ON(err_pos_lut[din] == 0xfff); + return err_pos_lut[din]; +} +static int chk_no_err_only(unsigned short *chk_syndrome_list, unsigned short *err_info) +{ + if ((chk_syndrome_list[0] | chk_syndrome_list[1] | + chk_syndrome_list[2] | chk_syndrome_list[3] | + chk_syndrome_list[4] | chk_syndrome_list[5] | + chk_syndrome_list[6] | chk_syndrome_list[7]) != 0x0) { + return -EINVAL; + } else { + err_info[0] = 0x0; + return 0; + } +} +static int chk_1_err_only(unsigned short *chk_syndrome_list, unsigned short *err_info) +{ + unsigned short tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6; + tmp0 = gf4096_mul(chk_syndrome_list[1], gf4096_inv(chk_syndrome_list[0])); + tmp1 = gf4096_mul(chk_syndrome_list[2], gf4096_inv(chk_syndrome_list[1])); + tmp2 = gf4096_mul(chk_syndrome_list[3], gf4096_inv(chk_syndrome_list[2])); + tmp3 = gf4096_mul(chk_syndrome_list[4], gf4096_inv(chk_syndrome_list[3])); + tmp4 = gf4096_mul(chk_syndrome_list[5], gf4096_inv(chk_syndrome_list[4])); + tmp5 = gf4096_mul(chk_syndrome_list[6], gf4096_inv(chk_syndrome_list[5])); + tmp6 = gf4096_mul(chk_syndrome_list[7], gf4096_inv(chk_syndrome_list[6])); + if ((tmp0 == tmp1) & (tmp1 == tmp2) & (tmp2 == tmp3) & (tmp3 == tmp4) & (tmp4 == tmp5) & (tmp5 == tmp6)) { + err_info[0] = 0x1; // encode 1-symbol error as 0x1 + err_info[1] = err_pos(tmp0); + err_info[1] = (unsigned short)(0x55e - err_info[1]); + err_info[5] = chk_syndrome_list[0]; + return 0; + } else + return -EINVAL; +} +static int chk_2_err_only(unsigned short *chk_syndrome_list, unsigned short *err_info) +{ + unsigned short tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + unsigned short coefs[4]; + unsigned short err_pats[4]; + int found_num_root = 0; + unsigned short bit2_root0, bit2_root1; + unsigned short bit2_root0_inv, bit2_root1_inv; + unsigned short err_loc_eqn, test_root; + unsigned short bit2_loc0, bit2_loc1; + unsigned short bit2_pat0, bit2_pat1; + + find_2x2_soln(chk_syndrome_list[1], + chk_syndrome_list[0], + chk_syndrome_list[2], chk_syndrome_list[1], chk_syndrome_list[2], chk_syndrome_list[3], coefs); + for (test_root = 0x1; test_root < 0xfff; test_root++) { + err_loc_eqn = + gf4096_mul(coefs[1], gf4096_mul(test_root, test_root)) ^ gf4096_mul(coefs[0], test_root) ^ 0x1; + if (err_loc_eqn == 0x0) { + if (found_num_root == 0) { + bit2_root0 = test_root; + found_num_root = 1; + } else if (found_num_root == 1) { + bit2_root1 = test_root; + found_num_root = 2; + break; + } + } + } + if (found_num_root != 2) + return -EINVAL; + else { + bit2_root0_inv = gf4096_inv(bit2_root0); + bit2_root1_inv = gf4096_inv(bit2_root1); + find_2bit_err_pats(chk_syndrome_list[0], + chk_syndrome_list[1], bit2_root0_inv, bit2_root1_inv, err_pats); + bit2_pat0 = err_pats[0]; + bit2_pat1 = err_pats[1]; + //for(x+1) + tmp0 = gf4096_mul(gf4096_mul(bit2_root0_inv, bit2_root0_inv), gf4096_mul(bit2_root0_inv, bit2_root0_inv)); //rinv0^4 + tmp1 = gf4096_mul(bit2_root0_inv, tmp0); //rinv0^5 + tmp2 = gf4096_mul(bit2_root0_inv, tmp1); //rinv0^6 + tmp3 = gf4096_mul(bit2_root0_inv, tmp2); //rinv0^7 + tmp4 = gf4096_mul(gf4096_mul(bit2_root1_inv, bit2_root1_inv), gf4096_mul(bit2_root1_inv, bit2_root1_inv)); //rinv1^4 + tmp5 = gf4096_mul(bit2_root1_inv, tmp4); //rinv1^5 + tmp6 = gf4096_mul(bit2_root1_inv, tmp5); //rinv1^6 + tmp7 = gf4096_mul(bit2_root1_inv, tmp6); //rinv1^7 + //check if only 2-bit error + if ((chk_syndrome_list[4] == + (gf4096_mul(bit2_pat0, tmp0) ^ + gf4096_mul(bit2_pat1, + tmp4))) & (chk_syndrome_list[5] == + (gf4096_mul(bit2_pat0, tmp1) ^ + gf4096_mul(bit2_pat1, + tmp5))) & + (chk_syndrome_list[6] == + (gf4096_mul(bit2_pat0, tmp2) ^ + gf4096_mul(bit2_pat1, + tmp6))) & (chk_syndrome_list[7] == + (gf4096_mul(bit2_pat0, tmp3) ^ gf4096_mul(bit2_pat1, tmp7)))) { + if ((err_pos(bit2_root0_inv) == 0xfff) | (err_pos(bit2_root1_inv) == 0xfff)) { + return -EINVAL; + } else { + bit2_loc0 = 0x55e - err_pos(bit2_root0_inv); + bit2_loc1 = 0x55e - err_pos(bit2_root1_inv); + err_info[0] = 0x2; // encode 2-symbol error as 0x2 + err_info[1] = bit2_loc0; + err_info[2] = bit2_loc1; + err_info[5] = bit2_pat0; + err_info[6] = bit2_pat1; + return 0; + } + } else + return -EINVAL; + } +} +static int chk_3_err_only(unsigned short *chk_syndrome_list, unsigned short *err_info) +{ + unsigned short tmp0, tmp1, tmp2, tmp3, tmp4, tmp5; + unsigned short coefs[4]; + unsigned short err_pats[4]; + int found_num_root = 0; + unsigned short bit3_root0, bit3_root1, bit3_root2; + unsigned short bit3_root0_inv, bit3_root1_inv, bit3_root2_inv; + unsigned short err_loc_eqn, test_root; + + find_3bit_err_coefs(chk_syndrome_list[0], chk_syndrome_list[1], + chk_syndrome_list[2], chk_syndrome_list[3], + chk_syndrome_list[4], chk_syndrome_list[5], coefs); + + for (test_root = 0x1; test_root < 0xfff; test_root++) { + err_loc_eqn = gf4096_mul(coefs[2], + gf4096_mul(gf4096_mul(test_root, test_root), + test_root)) ^ gf4096_mul(coefs[1], gf4096_mul(test_root, test_root)) + ^ gf4096_mul(coefs[0], test_root) ^ 0x1; + + if (err_loc_eqn == 0x0) { + if (found_num_root == 0) { + bit3_root0 = test_root; + found_num_root = 1; + } else if (found_num_root == 1) { + bit3_root1 = test_root; + found_num_root = 2; + } else if (found_num_root == 2) { + bit3_root2 = test_root; + found_num_root = 3; + break; + } + } + } + if (found_num_root != 3) + return -EINVAL; + else { + bit3_root0_inv = gf4096_inv(bit3_root0); + bit3_root1_inv = gf4096_inv(bit3_root1); + bit3_root2_inv = gf4096_inv(bit3_root2); + + find_3bit_err_pats(chk_syndrome_list[0], chk_syndrome_list[1], + chk_syndrome_list[2], bit3_root0_inv, + bit3_root1_inv, bit3_root2_inv, err_pats); + + //check if only 3-bit error + tmp0 = gf4096_mul(bit3_root0_inv, bit3_root0_inv); + tmp0 = gf4096_mul(tmp0, tmp0); + tmp0 = gf4096_mul(tmp0, bit3_root0_inv); + tmp0 = gf4096_mul(tmp0, bit3_root0_inv); //rinv0^6 + tmp1 = gf4096_mul(tmp0, bit3_root0_inv); //rinv0^7 + tmp2 = gf4096_mul(bit3_root1_inv, bit3_root1_inv); + tmp2 = gf4096_mul(tmp2, tmp2); + tmp2 = gf4096_mul(tmp2, bit3_root1_inv); + tmp2 = gf4096_mul(tmp2, bit3_root1_inv); //rinv1^6 + tmp3 = gf4096_mul(tmp2, bit3_root1_inv); //rinv1^7 + tmp4 = gf4096_mul(bit3_root2_inv, bit3_root2_inv); + tmp4 = gf4096_mul(tmp4, tmp4); + tmp4 = gf4096_mul(tmp4, bit3_root2_inv); + tmp4 = gf4096_mul(tmp4, bit3_root2_inv); //rinv2^6 + tmp5 = gf4096_mul(tmp4, bit3_root2_inv); //rinv2^7 + + //check if only 3 errors + if ((chk_syndrome_list[6] == (gf4096_mul(err_pats[0], tmp0) ^ + gf4096_mul(err_pats[1], tmp2) ^ + gf4096_mul(err_pats[2], tmp4))) & + (chk_syndrome_list[7] == (gf4096_mul(err_pats[0], tmp1) ^ + gf4096_mul(err_pats[1], tmp3) ^ gf4096_mul(err_pats[2], tmp5)))) { + if ((err_pos(bit3_root0_inv) == 0xfff) | + (err_pos(bit3_root1_inv) == 0xfff) | (err_pos(bit3_root2_inv) == 0xfff)) { + return -EINVAL; + } else { + err_info[0] = 0x3; + err_info[1] = (0x55e - err_pos(bit3_root0_inv)); + err_info[2] = (0x55e - err_pos(bit3_root1_inv)); + err_info[3] = (0x55e - err_pos(bit3_root2_inv)); + err_info[5] = err_pats[0]; + err_info[6] = err_pats[1]; + err_info[7] = err_pats[2]; + return 0; + } + } else + return -EINVAL; + } +} +static int chk_4_err_only(unsigned short *chk_syndrome_list, unsigned short *err_info) +{ + unsigned short coefs[4]; + unsigned short err_pats[4]; + int found_num_root = 0; + unsigned short bit4_root0, bit4_root1, bit4_root2, bit4_root3; + unsigned short bit4_root0_inv, bit4_root1_inv, bit4_root2_inv, bit4_root3_inv; + unsigned short err_loc_eqn, test_root; + + find_4bit_err_coefs(chk_syndrome_list[0], + chk_syndrome_list[1], + chk_syndrome_list[2], + chk_syndrome_list[3], + chk_syndrome_list[4], + chk_syndrome_list[5], chk_syndrome_list[6], chk_syndrome_list[7], coefs); + + for (test_root = 0x1; test_root < 0xfff; test_root++) { + err_loc_eqn = + gf4096_mul(coefs[3], + gf4096_mul(gf4096_mul + (gf4096_mul(test_root, test_root), + test_root), + test_root)) ^ gf4096_mul(coefs[2], + gf4096_mul + (gf4096_mul(test_root, test_root), test_root)) + ^ gf4096_mul(coefs[1], gf4096_mul(test_root, test_root)) ^ gf4096_mul(coefs[0], test_root) + ^ 0x1; + if (err_loc_eqn == 0x0) { + if (found_num_root == 0) { + bit4_root0 = test_root; + found_num_root = 1; + } else if (found_num_root == 1) { + bit4_root1 = test_root; + found_num_root = 2; + } else if (found_num_root == 2) { + bit4_root2 = test_root; + found_num_root = 3; + } else { + found_num_root = 4; + bit4_root3 = test_root; + break; + } + } + } + if (found_num_root != 4) { + return -EINVAL; + } else { + bit4_root0_inv = gf4096_inv(bit4_root0); + bit4_root1_inv = gf4096_inv(bit4_root1); + bit4_root2_inv = gf4096_inv(bit4_root2); + bit4_root3_inv = gf4096_inv(bit4_root3); + find_4bit_err_pats(chk_syndrome_list[0], + chk_syndrome_list[1], + chk_syndrome_list[2], + chk_syndrome_list[3], + bit4_root0_inv, bit4_root1_inv, bit4_root2_inv, bit4_root3_inv, err_pats); + err_info[0] = 0x4; + err_info[1] = (0x55e - err_pos(bit4_root0_inv)); + err_info[2] = (0x55e - err_pos(bit4_root1_inv)); + err_info[3] = (0x55e - err_pos(bit4_root2_inv)); + err_info[4] = (0x55e - err_pos(bit4_root3_inv)); + err_info[5] = err_pats[0]; + err_info[6] = err_pats[1]; + err_info[7] = err_pats[2]; + err_info[8] = err_pats[3]; + return 0; + } +} + +void correct_12bit_symbol(unsigned char *buf, unsigned short sym, + unsigned short val) +{ + if (unlikely(sym > 1366)) { + printk(KERN_ERR "Error: symbol %d out of range; cannot correct\n", sym); + } else if (sym == 0) { + buf[0] ^= val; + } else if (sym & 1) { + buf[1+(3*(sym-1))/2] ^= (val >> 4); + buf[2+(3*(sym-1))/2] ^= ((val & 0xf) << 4); + } else { + buf[2+(3*(sym-2))/2] ^= (val >> 8); + buf[3+(3*(sym-2))/2] ^= (val & 0xff); + } + + +} + +int cafe_correct_ecc(unsigned char *buf, + unsigned short *chk_syndrome_list) +{ + unsigned short err_info[9]; + int i; + + if (chk_no_err_only(chk_syndrome_list, err_info) && + chk_1_err_only(chk_syndrome_list, err_info) && + chk_2_err_only(chk_syndrome_list, err_info) && + chk_3_err_only(chk_syndrome_list, err_info) && + chk_4_err_only(chk_syndrome_list, err_info)) { + return -EIO; + } + + for (i=0; i < err_info[0]; i++) + correct_12bit_symbol(buf, err_info[1+i], err_info[5+i]); + + return err_info[0]; +} + diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c deleted file mode 100644 index 60cb019..0000000 --- a/drivers/mtd/nand/cafe_nand.c +++ /dev/null @@ -1,698 +0,0 @@ -/* - * cafe_nand.c - * - * Copyright © 2006 Red Hat, Inc. - * Copyright © 2006 David Woodhouse - */ - -#define DEBUG - -#include -#undef DEBUG -#include -#include -#include -#include -#include -#include - -#define CAFE_NAND_CTRL1 0x00 -#define CAFE_NAND_CTRL2 0x04 -#define CAFE_NAND_CTRL3 0x08 -#define CAFE_NAND_STATUS 0x0c -#define CAFE_NAND_IRQ 0x10 -#define CAFE_NAND_IRQ_MASK 0x14 -#define CAFE_NAND_DATA_LEN 0x18 -#define CAFE_NAND_ADDR1 0x1c -#define CAFE_NAND_ADDR2 0x20 -#define CAFE_NAND_TIMING1 0x24 -#define CAFE_NAND_TIMING2 0x28 -#define CAFE_NAND_TIMING3 0x2c -#define CAFE_NAND_NONMEM 0x30 -#define CAFE_NAND_DMA_CTRL 0x40 -#define CAFE_NAND_DMA_ADDR0 0x44 -#define CAFE_NAND_DMA_ADDR1 0x48 -#define CAFE_NAND_READ_DATA 0x1000 -#define CAFE_NAND_WRITE_DATA 0x2000 - -struct cafe_priv { - struct nand_chip nand; - struct pci_dev *pdev; - void __iomem *mmio; - uint32_t ctl1; - uint32_t ctl2; - int datalen; - int nr_data; - int data_pos; - int page_addr; - dma_addr_t dmaaddr; - unsigned char *dmabuf; - -}; - -static int usedma = 1; -module_param(usedma, int, 0644); - -static int skipbbt = 0; -module_param(skipbbt, int, 0644); - -static int debug = 0; -module_param(debug, int, 0644); - -#define cafe_dev_dbg(dev, args...) do { if (debug) dev_dbg(dev, ##args); } while(0) - - -static int cafe_device_ready(struct mtd_info *mtd) -{ - struct cafe_priv *cafe = mtd->priv; - int result = !!(readl(cafe->mmio + CAFE_NAND_STATUS) | 0x40000000); - - uint32_t irqs = readl(cafe->mmio + CAFE_NAND_IRQ); - writel(irqs, cafe->mmio+CAFE_NAND_IRQ); - cafe_dev_dbg(&cafe->pdev->dev, "NAND device is%s ready, IRQ %x (%x) (%x,%x)\n", - result?"":" not", irqs, readl(cafe->mmio + CAFE_NAND_IRQ), - readl(cafe->mmio + 0x3008), readl(cafe->mmio + 0x300c)); - return result; -} - - -static void cafe_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) -{ - struct cafe_priv *cafe = mtd->priv; - - if (usedma) - memcpy(cafe->dmabuf + cafe->datalen, buf, len); - else - memcpy_toio(cafe->mmio + CAFE_NAND_WRITE_DATA + cafe->datalen, buf, len); - cafe->datalen += len; - - cafe_dev_dbg(&cafe->pdev->dev, "Copy 0x%x bytes to write buffer. datalen 0x%x\n", - len, cafe->datalen); -} - -static void cafe_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) -{ - struct cafe_priv *cafe = mtd->priv; - - if (usedma) - memcpy(buf, cafe->dmabuf + cafe->datalen, len); - else - memcpy_fromio(buf, cafe->mmio + CAFE_NAND_READ_DATA + cafe->datalen, len); - - cafe_dev_dbg(&cafe->pdev->dev, "Copy 0x%x bytes from position 0x%x in read buffer.\n", - len, cafe->datalen); - cafe->datalen += len; -} - -static uint8_t cafe_read_byte(struct mtd_info *mtd) -{ - struct cafe_priv *cafe = mtd->priv; - uint8_t d; - - cafe_read_buf(mtd, &d, 1); - cafe_dev_dbg(&cafe->pdev->dev, "Read %02x\n", d); - - return d; -} - -static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, - int column, int page_addr) -{ - struct cafe_priv *cafe = mtd->priv; - int adrbytes = 0; - uint32_t ctl1; - uint32_t doneint = 0x80000000; - int i; - - cafe_dev_dbg(&cafe->pdev->dev, "cmdfunc %02x, 0x%x, 0x%x\n", - command, column, page_addr); - - if (command == NAND_CMD_ERASE2 || command == NAND_CMD_PAGEPROG) { - /* Second half of a command we already calculated */ - writel(cafe->ctl2 | 0x100 | command, cafe->mmio + 0x04); - ctl1 = cafe->ctl1; - cafe_dev_dbg(&cafe->pdev->dev, "Continue command, ctl1 %08x, #data %d\n", - cafe->ctl1, cafe->nr_data); - goto do_command; - } - /* Reset ECC engine */ - writel(0, cafe->mmio + CAFE_NAND_CTRL2); - - /* Emulate NAND_CMD_READOOB on large-page chips */ - if (mtd->writesize > 512 && - command == NAND_CMD_READOOB) { - column += mtd->writesize; - command = NAND_CMD_READ0; - } - - /* FIXME: Do we need to send read command before sending data - for small-page chips, to position the buffer correctly? */ - - if (column != -1) { - writel(column, cafe->mmio + 0x1c); - adrbytes = 2; - if (page_addr != -1) - goto write_adr2; - } else if (page_addr != -1) { - writel(page_addr & 0xffff, cafe->mmio + 0x1c); - page_addr >>= 16; - write_adr2: - writel(page_addr, cafe->mmio+0x20); - adrbytes += 2; - if (mtd->size > mtd->writesize << 16) - adrbytes++; - } - - cafe->data_pos = cafe->datalen = 0; - - /* Set command valid bit */ - ctl1 = 0x80000000 | command; - - /* Set RD or WR bits as appropriate */ - if (command == NAND_CMD_READID || command == NAND_CMD_STATUS) { - ctl1 |= (1<<26); /* rd */ - /* Always 5 bytes, for now */ - cafe->datalen = 4; - /* And one address cycle -- even for STATUS, since the controller doesn't work without */ - adrbytes = 1; - } else if (command == NAND_CMD_READ0 || command == NAND_CMD_READ1 || - command == NAND_CMD_READOOB || command == NAND_CMD_RNDOUT) { - ctl1 |= 1<<26; /* rd */ - /* For now, assume just read to end of page */ - cafe->datalen = mtd->writesize + mtd->oobsize - column; - } else if (command == NAND_CMD_SEQIN) - ctl1 |= 1<<25; /* wr */ - - /* Set number of address bytes */ - if (adrbytes) - ctl1 |= ((adrbytes-1)|8) << 27; - - if (command == NAND_CMD_SEQIN || command == NAND_CMD_ERASE1) { - /* Ignore the first command of a pair; the hardware - deals with them both at once, later */ - cafe->ctl1 = ctl1; - cafe->ctl2 = 0; - cafe_dev_dbg(&cafe->pdev->dev, "Setup for delayed command, ctl1 %08x, dlen %x\n", - cafe->ctl1, cafe->datalen); - return; - } - /* RNDOUT and READ0 commands need a following byte */ - if (command == NAND_CMD_RNDOUT) - writel(cafe->ctl2 | 0x100 | NAND_CMD_RNDOUTSTART, cafe->mmio + CAFE_NAND_CTRL2); - else if (command == NAND_CMD_READ0 && mtd->writesize > 512) - writel(cafe->ctl2 | 0x100 | NAND_CMD_READSTART, cafe->mmio + CAFE_NAND_CTRL2); - - do_command: - // ECC on read only works if we ... - // if (cafe->datalen == 2112) - // cafe->datalen = 2062; - cafe_dev_dbg(&cafe->pdev->dev, "dlen %x, ctl1 %x, ctl2 %x\n", - cafe->datalen, ctl1, readl(cafe->mmio+CAFE_NAND_CTRL2)); - /* NB: The datasheet lies -- we really should be subtracting 1 here */ - writel(cafe->datalen, cafe->mmio + CAFE_NAND_DATA_LEN); - writel(0x90000000, cafe->mmio + CAFE_NAND_IRQ); - if (usedma && (ctl1 & (3<<25))) { - uint32_t dmactl = 0xc0000000 + cafe->datalen; - /* If WR or RD bits set, set up DMA */ - if (ctl1 & (1<<26)) { - /* It's a read */ - dmactl |= (1<<29); - /* ... so it's done when the DMA is done, not just - the command. */ - doneint = 0x10000000; - } - writel(dmactl, cafe->mmio + 0x40); - } -#if 0 - printk("DMA setup is %x, status %x, ctl1 %x\n", readl(cafe->mmio + 0x40), readl(cafe->mmio + 0x0c), readl(cafe->mmio)); - printk("DMA setup is %x, status %x, ctl1 %x\n", readl(cafe->mmio + 0x40), readl(cafe->mmio + 0x0c), readl(cafe->mmio)); -#endif - cafe->datalen = 0; - -#if 0 - printk("About to write command %08x\n", ctl1); - for (i=0; i< 0x5c; i+=4) - printk("Register %x: %08x\n", i, readl(cafe->mmio + i)); -#endif - writel(ctl1, cafe->mmio + CAFE_NAND_CTRL1); - /* Apply this short delay always to ensure that we do wait tWB in - * any case on any machine. */ - ndelay(100); - - if (1) { - int c = 500000; - uint32_t irqs; - - while (c--) { - irqs = readl(cafe->mmio + CAFE_NAND_IRQ); - if (irqs & doneint) - break; - udelay(1); - if (!(c % 100000)) - cafe_dev_dbg(&cafe->pdev->dev, "Wait for ready, IRQ %x\n", irqs); - cpu_relax(); - } - writel(doneint, cafe->mmio + CAFE_NAND_IRQ); - cafe_dev_dbg(&cafe->pdev->dev, "Command %x completed after %d usec, irqs %x (%x)\n", command, 50000-c, irqs, readl(cafe->mmio + CAFE_NAND_IRQ)); - } - - - cafe->ctl2 &= ~(1<<8); - cafe->ctl2 &= ~(1<<30); - - switch (command) { - - case NAND_CMD_CACHEDPROG: - case NAND_CMD_PAGEPROG: - case NAND_CMD_ERASE1: - case NAND_CMD_ERASE2: - case NAND_CMD_SEQIN: - case NAND_CMD_RNDIN: - case NAND_CMD_STATUS: - case NAND_CMD_DEPLETE1: - case NAND_CMD_RNDOUT: - case NAND_CMD_STATUS_ERROR: - case NAND_CMD_STATUS_ERROR0: - case NAND_CMD_STATUS_ERROR1: - case NAND_CMD_STATUS_ERROR2: - case NAND_CMD_STATUS_ERROR3: - writel(cafe->ctl2, cafe->mmio + CAFE_NAND_CTRL2); - return; - } - nand_wait_ready(mtd); - writel(cafe->ctl2, cafe->mmio + CAFE_NAND_CTRL2); -} - -static void cafe_select_chip(struct mtd_info *mtd, int chipnr) -{ - //struct cafe_priv *cafe = mtd->priv; - // cafe_dev_dbg(&cafe->pdev->dev, "select_chip %d\n", chipnr); -} -static int cafe_nand_interrupt(int irq, void *id, struct pt_regs *regs) -{ - struct mtd_info *mtd = id; - struct cafe_priv *cafe = mtd->priv; - uint32_t irqs = readl(cafe->mmio + CAFE_NAND_IRQ); - writel(irqs & ~0x90000000, cafe->mmio + CAFE_NAND_IRQ); - if (!irqs) - return IRQ_NONE; - - cafe_dev_dbg(&cafe->pdev->dev, "irq, bits %x (%x)\n", irqs, readl(cafe->mmio + CAFE_NAND_IRQ)); - return IRQ_HANDLED; -} - -static void cafe_nand_bug(struct mtd_info *mtd) -{ - BUG(); -} - -static int cafe_nand_write_oob(struct mtd_info *mtd, - struct nand_chip *chip, int page) -{ - int status = 0; - - WARN_ON(chip->oob_poi != chip->buffers->oobwbuf); - - chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); - chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - status = chip->waitfunc(mtd, chip); - - return status & NAND_STATUS_FAIL ? -EIO : 0; -} - -/* Don't use -- use nand_read_oob_std for now */ -static int cafe_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page, int sndcmd) -{ - chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - return 1; -} -/** - * cafe_nand_read_page_syndrome - {REPLACABLE] hardware ecc syndrom based page read - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: buffer to store read data - * - * The hw generator calculates the error syndrome automatically. Therefor - * we need a special oob layout and handling. - */ -static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf) -{ - struct cafe_priv *cafe = mtd->priv; - - WARN_ON(chip->oob_poi != chip->buffers->oobrbuf); - - cafe_dev_dbg(&cafe->pdev->dev, "ECC result %08x SYN1,2 %08x\n", readl(cafe->mmio + 0x3c), readl(cafe->mmio + 0x50)); - - chip->read_buf(mtd, buf, mtd->writesize); - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - - return 0; -} - -static struct nand_ecclayout cafe_oobinfo_2048 = { - .eccbytes = 14, - .eccpos = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, - .oobfree = {{14, 50}} -}; - -/* Ick. The BBT code really ought to be able to work this bit out - for itself from the above */ -static uint8_t cafe_bbt_pattern[] = {'B', 'b', 't', '0' }; -static uint8_t cafe_mirror_pattern[] = {'1', 't', 'b', 'B' }; - -static struct nand_bbt_descr cafe_bbt_main_descr_2048 = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE - | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, - .offs = 14, - .len = 4, - .veroffs = 18, - .maxblocks = 4, - .pattern = cafe_bbt_pattern -}; - -static struct nand_bbt_descr cafe_bbt_mirror_descr_2048 = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE - | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, - .offs = 14, - .len = 4, - .veroffs = 18, - .maxblocks = 4, - .pattern = cafe_mirror_pattern -}; - -static struct nand_ecclayout cafe_oobinfo_512 = { - .eccbytes = 14, - .eccpos = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, - .oobfree = {{14, 2}} -}; - - -static void cafe_nand_write_page_lowlevel(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf) -{ - struct cafe_priv *cafe = mtd->priv; - - WARN_ON(chip->oob_poi != chip->buffers->oobwbuf); - - chip->write_buf(mtd, buf, mtd->writesize); - chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); - - /* Set up ECC autogeneration */ - cafe->ctl2 |= (1<<27) | (1<<30); - if (mtd->writesize == 2048) - cafe->ctl2 |= (1<<29); -} - -static int cafe_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int page, int cached, int raw) -{ - int status; - - WARN_ON(chip->oob_poi != chip->buffers->oobwbuf); - - chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); - - if (unlikely(raw)) - chip->ecc.write_page_raw(mtd, chip, buf); - else - chip->ecc.write_page(mtd, chip, buf); - - /* - * Cached progamming disabled for now, Not sure if its worth the - * trouble. The speed gain is not very impressive. (2.3->2.6Mib/s) - */ - cached = 0; - - if (!cached || !(chip->options & NAND_CACHEPRG)) { - - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - status = chip->waitfunc(mtd, chip); - /* - * See if operation failed and additional status checks are - * available - */ - if ((status & NAND_STATUS_FAIL) && (chip->errstat)) - status = chip->errstat(mtd, chip, FL_WRITING, status, - page); - - if (status & NAND_STATUS_FAIL) - return -EIO; - } else { - chip->cmdfunc(mtd, NAND_CMD_CACHEDPROG, -1, -1); - status = chip->waitfunc(mtd, chip); - } - -#ifdef CONFIG_MTD_NAND_VERIFY_WRITE - /* Send command to read back the data */ - chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); - - if (chip->verify_buf(mtd, buf, mtd->writesize)) - return -EIO; -#endif - return 0; -} - -static int cafe_nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) -{ - return 0; -} - -static int __devinit cafe_nand_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - struct mtd_info *mtd; - struct cafe_priv *cafe; - uint32_t ctrl; - int err = 0; - - err = pci_enable_device(pdev); - if (err) - return err; - - pci_set_master(pdev); - - mtd = kzalloc(sizeof(*mtd) + sizeof(struct cafe_priv), GFP_KERNEL); - if (!mtd) { - dev_warn(&pdev->dev, "failed to alloc mtd_info\n"); - return -ENOMEM; - } - cafe = (void *)(&mtd[1]); - - mtd->priv = cafe; - mtd->owner = THIS_MODULE; - - cafe->pdev = pdev; - cafe->mmio = pci_iomap(pdev, 0, 0); - if (!cafe->mmio) { - dev_warn(&pdev->dev, "failed to iomap\n"); - err = -ENOMEM; - goto out_free_mtd; - } - cafe->dmabuf = dma_alloc_coherent(&cafe->pdev->dev, 2112 + sizeof(struct nand_buffers), - &cafe->dmaaddr, GFP_KERNEL); - if (!cafe->dmabuf) { - err = -ENOMEM; - goto out_ior; - } - cafe->nand.buffers = (void *)cafe->dmabuf + 2112; - - cafe->nand.cmdfunc = cafe_nand_cmdfunc; - cafe->nand.dev_ready = cafe_device_ready; - cafe->nand.read_byte = cafe_read_byte; - cafe->nand.read_buf = cafe_read_buf; - cafe->nand.write_buf = cafe_write_buf; - cafe->nand.select_chip = cafe_select_chip; - - cafe->nand.chip_delay = 0; - - /* Enable the following for a flash based bad block table */ - cafe->nand.options = NAND_USE_FLASH_BBT | NAND_NO_AUTOINCR | NAND_OWN_BUFFERS; - - if (skipbbt) { - cafe->nand.options |= NAND_SKIP_BBTSCAN; - cafe->nand.block_bad = cafe_nand_block_bad; - } - - /* Timings from Marvell's test code (not verified or calculated by us) */ - writel(0xffffffff, cafe->mmio + CAFE_NAND_IRQ_MASK); -#if 1 - writel(0x01010a0a, cafe->mmio + CAFE_NAND_TIMING1); - writel(0x24121212, cafe->mmio + CAFE_NAND_TIMING2); - writel(0x11000000, cafe->mmio + CAFE_NAND_TIMING3); -#else - writel(0xffffffff, cafe->mmio + CAFE_NAND_TIMING1); - writel(0xffffffff, cafe->mmio + CAFE_NAND_TIMING2); - writel(0xffffffff, cafe->mmio + CAFE_NAND_TIMING3); -#endif - writel(0xffffffff, cafe->mmio + CAFE_NAND_IRQ_MASK); - err = request_irq(pdev->irq, &cafe_nand_interrupt, SA_SHIRQ, "CAFE NAND", mtd); - if (err) { - dev_warn(&pdev->dev, "Could not register IRQ %d\n", pdev->irq); - - goto out_free_dma; - } -#if 1 - /* Disable master reset, enable NAND clock */ - ctrl = readl(cafe->mmio + 0x3004); - ctrl &= 0xffffeff0; - ctrl |= 0x00007000; - writel(ctrl | 0x05, cafe->mmio + 0x3004); - writel(ctrl | 0x0a, cafe->mmio + 0x3004); - writel(0, cafe->mmio + 0x40); - - writel(0x7006, cafe->mmio + 0x3004); - writel(0x700a, cafe->mmio + 0x3004); - - /* Set up DMA address */ - writel(cafe->dmaaddr & 0xffffffff, cafe->mmio + 0x44); - if (sizeof(cafe->dmaaddr) > 4) - writel((cafe->dmaaddr >> 16) >> 16, cafe->mmio + 0x48); - else - writel(0, cafe->mmio + 0x48); - cafe_dev_dbg(&cafe->pdev->dev, "Set DMA address to %x (virt %p)\n", - readl(cafe->mmio+0x44), cafe->dmabuf); - - /* Enable NAND IRQ in global IRQ mask register */ - writel(0x80000007, cafe->mmio + 0x300c); - cafe_dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n", - readl(cafe->mmio + 0x3004), readl(cafe->mmio + 0x300c)); -#endif -#if 1 - mtd->writesize=2048; - mtd->oobsize = 0x40; - memset(cafe->dmabuf, 0x5a, 2112); - cafe->nand.cmdfunc(mtd, NAND_CMD_READID, 0, -1); - cafe->nand.read_byte(mtd); - cafe->nand.read_byte(mtd); - cafe->nand.read_byte(mtd); - cafe->nand.read_byte(mtd); - cafe->nand.read_byte(mtd); -#endif -#if 0 - cafe->nand.cmdfunc(mtd, NAND_CMD_READ0, 0, 0); - // nand_wait_ready(mtd); - cafe->nand.read_byte(mtd); - cafe->nand.read_byte(mtd); - cafe->nand.read_byte(mtd); - cafe->nand.read_byte(mtd); -#endif -#if 0 - writel(0x84600070, cafe->mmio); - udelay(10); - cafe_dev_dbg(&cafe->pdev->dev, "Status %x\n", readl(cafe->mmio + 0x30)); -#endif - /* Scan to find existance of the device */ - if (nand_scan_ident(mtd, 1)) { - err = -ENXIO; - goto out_irq; - } - - cafe->ctl2 = 1<<27; /* Reed-Solomon ECC */ - if (mtd->writesize == 2048) - cafe->ctl2 |= 1<<29; /* 2KiB page size */ - - /* Set up ECC according to the type of chip we found */ - if (mtd->writesize == 512 || mtd->writesize == 2048) { - cafe->nand.ecc.mode = NAND_ECC_HW_SYNDROME; - cafe->nand.ecc.size = mtd->writesize; - cafe->nand.ecc.bytes = 14; - cafe->nand.ecc.layout = &cafe_oobinfo_2048; - cafe->nand.bbt_td = &cafe_bbt_main_descr_2048; - cafe->nand.bbt_md = &cafe_bbt_mirror_descr_2048; - cafe->nand.ecc.hwctl = (void *)cafe_nand_bug; - cafe->nand.ecc.calculate = (void *)cafe_nand_bug; - cafe->nand.ecc.correct = (void *)cafe_nand_bug; - cafe->nand.write_page = cafe_nand_write_page; - cafe->nand.ecc.write_page = cafe_nand_write_page_lowlevel; - cafe->nand.ecc.write_oob = cafe_nand_write_oob; - cafe->nand.ecc.read_page = cafe_nand_read_page; - cafe->nand.ecc.read_oob = cafe_nand_read_oob; - - } else { - printk(KERN_WARNING "Unexpected NAND flash writesize %d. Using software ECC\n", - mtd->writesize); - cafe->nand.ecc.mode = NAND_ECC_NONE; - } - - err = nand_scan_tail(mtd); - if (err) - goto out_irq; - - pci_set_drvdata(pdev, mtd); - add_mtd_device(mtd); - goto out; - - out_irq: - /* Disable NAND IRQ in global IRQ mask register */ - writel(~1 & readl(cafe->mmio + 0x300c), cafe->mmio + 0x300c); - free_irq(pdev->irq, mtd); - out_free_dma: - dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr); - out_ior: - pci_iounmap(pdev, cafe->mmio); - out_free_mtd: - kfree(mtd); - out: - return err; -} - -static void __devexit cafe_nand_remove(struct pci_dev *pdev) -{ - struct mtd_info *mtd = pci_get_drvdata(pdev); - struct cafe_priv *cafe = mtd->priv; - - del_mtd_device(mtd); - /* Disable NAND IRQ in global IRQ mask register */ - writel(~1 & readl(cafe->mmio + 0x300c), cafe->mmio + 0x300c); - free_irq(pdev->irq, mtd); - nand_release(mtd); - pci_iounmap(pdev, cafe->mmio); - dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr); - kfree(mtd); -} - -static struct pci_device_id cafe_nand_tbl[] = { - { 0x11ab, 0x4100, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MEMORY_FLASH << 8, 0xFFFF0 } -}; - -MODULE_DEVICE_TABLE(pci, cafe_nand_tbl); - -static struct pci_driver cafe_nand_pci_driver = { - .name = "CAFÉ NAND", - .id_table = cafe_nand_tbl, - .probe = cafe_nand_probe, - .remove = __devexit_p(cafe_nand_remove), -#ifdef CONFIG_PMx - .suspend = cafe_nand_suspend, - .resume = cafe_nand_resume, -#endif -}; - -static int cafe_nand_init(void) -{ - return pci_register_driver(&cafe_nand_pci_driver); -} - -static void cafe_nand_exit(void) -{ - pci_unregister_driver(&cafe_nand_pci_driver); -} -module_init(cafe_nand_init); -module_exit(cafe_nand_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("David Woodhouse "); -MODULE_DESCRIPTION("NAND flash driver for OLPC CAFE chip"); - -/* Correct ECC for 2048 bytes of 0xff: - 41 a0 71 65 54 27 f3 93 ec a9 be ed 0b a1 */ - -/* dwmw2's B-test board, in case of completely screwing it: -Bad eraseblock 2394 at 0x12b40000 -Bad eraseblock 2627 at 0x14860000 -Bad eraseblock 3349 at 0x1a2a0000 -*/ -- cgit v0.10.2 From fbad5696c5c45982d02e05b85922bad6eb6e6349 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sun, 22 Oct 2006 15:09:33 +0100 Subject: =?UTF-8?q?[MTD]=20NAND:=20CAF=C3=89=20NAND=20driver=20cleanup,=20?= =?UTF-8?q?fix=20ECC=20on=20reading=20empty=20flash?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 7cebc10..f7a53f0 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -25,4 +25,5 @@ obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o obj-$(CONFIG_MTD_NAND_AT91) += at91_nand.o -nand-objs = nand_base.o nand_bbt.o +nand-objs := nand_base.o nand_bbt.o +cafe_nand-objs := cafe.o cafe_ecc.o diff --git a/drivers/mtd/nand/cafe.c b/drivers/mtd/nand/cafe.c index 1fe1108..10132ef 100644 --- a/drivers/mtd/nand/cafe.c +++ b/drivers/mtd/nand/cafe.c @@ -1,5 +1,5 @@ /* - * cafe_nand.c + * Driver for One Laptop Per Child ‘CAFÉ’ controller, aka Marvell 88ALP01 * * Copyright © 2006 Red Hat, Inc. * Copyright © 2006 David Woodhouse @@ -30,13 +30,13 @@ #define CAFE_NAND_TIMING3 0x2c #define CAFE_NAND_NONMEM 0x30 #define CAFE_NAND_ECC_RESULT 0x3C +#define CAFE_NAND_DMA_CTRL 0x40 +#define CAFE_NAND_DMA_ADDR0 0x44 +#define CAFE_NAND_DMA_ADDR1 0x48 #define CAFE_NAND_ECC_SYN01 0x50 #define CAFE_NAND_ECC_SYN23 0x54 #define CAFE_NAND_ECC_SYN45 0x58 #define CAFE_NAND_ECC_SYN67 0x5c -#define CAFE_NAND_DMA_CTRL 0x40 -#define CAFE_NAND_DMA_ADDR0 0x44 -#define CAFE_NAND_DMA_ADDR1 0x48 #define CAFE_NAND_READ_DATA 0x1000 #define CAFE_NAND_WRITE_DATA 0x2000 @@ -75,12 +75,14 @@ static int cafe_device_ready(struct mtd_info *mtd) { struct cafe_priv *cafe = mtd->priv; int result = !!(readl(cafe->mmio + CAFE_NAND_STATUS) | 0x40000000); - uint32_t irqs = readl(cafe->mmio + CAFE_NAND_IRQ); + writel(irqs, cafe->mmio+CAFE_NAND_IRQ); + cafe_dev_dbg(&cafe->pdev->dev, "NAND device is%s ready, IRQ %x (%x) (%x,%x)\n", result?"":" not", irqs, readl(cafe->mmio + CAFE_NAND_IRQ), readl(cafe->mmio + 0x3008), readl(cafe->mmio + 0x300c)); + return result; } @@ -93,6 +95,7 @@ static void cafe_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) memcpy(cafe->dmabuf + cafe->datalen, buf, len); else memcpy_toio(cafe->mmio + CAFE_NAND_WRITE_DATA + cafe->datalen, buf, len); + cafe->datalen += len; cafe_dev_dbg(&cafe->pdev->dev, "Copy 0x%x bytes to write buffer. datalen 0x%x\n", @@ -131,14 +134,13 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, int adrbytes = 0; uint32_t ctl1; uint32_t doneint = 0x80000000; - int i; cafe_dev_dbg(&cafe->pdev->dev, "cmdfunc %02x, 0x%x, 0x%x\n", command, column, page_addr); if (command == NAND_CMD_ERASE2 || command == NAND_CMD_PAGEPROG) { /* Second half of a command we already calculated */ - writel(cafe->ctl2 | 0x100 | command, cafe->mmio + 0x04); + writel(cafe->ctl2 | 0x100 | command, cafe->mmio + CAFE_NAND_CTRL2); ctl1 = cafe->ctl1; cafe_dev_dbg(&cafe->pdev->dev, "Continue command, ctl1 %08x, #data %d\n", cafe->ctl1, cafe->nr_data); @@ -158,12 +160,12 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, for small-page chips, to position the buffer correctly? */ if (column != -1) { - writel(column, cafe->mmio + 0x1c); + writel(column, cafe->mmio + CAFE_NAND_ADDR1); adrbytes = 2; if (page_addr != -1) goto write_adr2; } else if (page_addr != -1) { - writel(page_addr & 0xffff, cafe->mmio + 0x1c); + writel(page_addr & 0xffff, cafe->mmio + CAFE_NAND_ADDR1); page_addr >>= 16; write_adr2: writel(page_addr, cafe->mmio+0x20); @@ -212,13 +214,15 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, writel(cafe->ctl2 | 0x100 | NAND_CMD_READSTART, cafe->mmio + CAFE_NAND_CTRL2); do_command: -#if 0 - // ECC on read only works if we ... +#if 1 + /* http://dev.laptop.org/ticket/200 + ECC on read only works if we read precisely 0x80e bytes */ if (cafe->datalen == 2112) cafe->datalen = 2062; #endif cafe_dev_dbg(&cafe->pdev->dev, "dlen %x, ctl1 %x, ctl2 %x\n", cafe->datalen, ctl1, readl(cafe->mmio+CAFE_NAND_CTRL2)); + /* NB: The datasheet lies -- we really should be subtracting 1 here */ writel(cafe->datalen, cafe->mmio + CAFE_NAND_DATA_LEN); writel(0x90000000, cafe->mmio + CAFE_NAND_IRQ); @@ -232,18 +236,16 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, the command. */ doneint = 0x10000000; } - writel(dmactl, cafe->mmio + 0x40); + writel(dmactl, cafe->mmio + CAFE_NAND_DMA_CTRL); } -#if 0 - printk("DMA setup is %x, status %x, ctl1 %x\n", readl(cafe->mmio + 0x40), readl(cafe->mmio + 0x0c), readl(cafe->mmio)); - printk("DMA setup is %x, status %x, ctl1 %x\n", readl(cafe->mmio + 0x40), readl(cafe->mmio + 0x0c), readl(cafe->mmio)); -#endif cafe->datalen = 0; #if 0 + { int i; printk("About to write command %08x\n", ctl1); for (i=0; i< 0x5c; i+=4) printk("Register %x: %08x\n", i, readl(cafe->mmio + i)); + } #endif writel(ctl1, cafe->mmio + CAFE_NAND_CTRL1); /* Apply this short delay always to ensure that we do wait tWB in @@ -299,6 +301,7 @@ static void cafe_select_chip(struct mtd_info *mtd, int chipnr) //struct cafe_priv *cafe = mtd->priv; // cafe_dev_dbg(&cafe->pdev->dev, "select_chip %d\n", chipnr); } + static int cafe_nand_interrupt(int irq, void *id, struct pt_regs *regs) { struct mtd_info *mtd = id; @@ -347,14 +350,34 @@ static int cafe_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, * The hw generator calculates the error syndrome automatically. Therefor * we need a special oob layout and handling. */ + +static unsigned short cafe_empty_syndromes[8] = { 4095, 748, 2629, 2920, 875, 1454, 51, 1456 }; + +static int is_all_ff(unsigned char *buf, int len) +{ + unsigned long *lbuf = (void *)buf; + int i; + + for (i=0; i < (len/sizeof(long)); i++) { + if (lbuf[i] != ~0UL) + return 0; + } + i *= sizeof(long); + for (; i< len; i++) { + if (buf[i] != 0xff) + return 0; + } + return 1; +} + static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf) { struct cafe_priv *cafe = mtd->priv; - dev_dbg(&cafe->pdev->dev, "ECC result %08x SYN1,2 %08x\n", - readl(cafe->mmio + CAFE_NAND_ECC_RESULT), - readl(cafe->mmio + CAFE_NAND_ECC_SYN01)); + cafe_dev_dbg(&cafe->pdev->dev, "ECC result %08x SYN1,2 %08x\n", + readl(cafe->mmio + CAFE_NAND_ECC_RESULT), + readl(cafe->mmio + CAFE_NAND_ECC_SYN01)); chip->read_buf(mtd, buf, mtd->writesize); chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); @@ -369,7 +392,13 @@ static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, syn[i+1] = (tmp >> 16) & 0xfff; } - if ((i = cafe_correct_ecc(buf, syn)) < 0) { + /* FIXME: http://dev.laptop.org/ticket/215 */ + if (!memcmp(syn, cafe_empty_syndromes, sizeof(syn)) + && is_all_ff(chip->oob_poi, 14) + && is_all_ff(buf, mtd->writesize)) { + dev_dbg(&cafe->pdev->dev, "ECC error reported on empty block\n"); + /* It was an empty block. Nothing to fix here except the hardware */ + } else if ((i = cafe_correct_ecc(buf, syn)) < 0) { dev_dbg(&cafe->pdev->dev, "Failed to correct ECC\n"); mtd->ecc_stats.failed++; } else { @@ -389,9 +418,13 @@ static struct nand_ecclayout cafe_oobinfo_2048 = { }; /* Ick. The BBT code really ought to be able to work this bit out - for itself from the above */ -static uint8_t cafe_bbt_pattern[] = {'B', 'b', 't', '0' }; -static uint8_t cafe_mirror_pattern[] = {'1', 't', 'b', 'B' }; + for itself from the above, at least for the 2KiB case */ +static uint8_t cafe_bbt_pattern_2048[] = { 'B', 'b', 't', '0' }; +static uint8_t cafe_mirror_pattern_2048[] = { '1', 't', 'b', 'B' }; + +static uint8_t cafe_bbt_pattern_512[] = { 0xBB }; +static uint8_t cafe_mirror_pattern_512[] = { 0xBC }; + static struct nand_bbt_descr cafe_bbt_main_descr_2048 = { .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE @@ -400,7 +433,7 @@ static struct nand_bbt_descr cafe_bbt_main_descr_2048 = { .len = 4, .veroffs = 18, .maxblocks = 4, - .pattern = cafe_bbt_pattern + .pattern = cafe_bbt_pattern_2048 }; static struct nand_bbt_descr cafe_bbt_mirror_descr_2048 = { @@ -410,7 +443,7 @@ static struct nand_bbt_descr cafe_bbt_mirror_descr_2048 = { .len = 4, .veroffs = 18, .maxblocks = 4, - .pattern = cafe_mirror_pattern + .pattern = cafe_mirror_pattern_2048 }; static struct nand_ecclayout cafe_oobinfo_512 = { @@ -419,6 +452,27 @@ static struct nand_ecclayout cafe_oobinfo_512 = { .oobfree = {{14, 2}} }; +static struct nand_bbt_descr cafe_bbt_main_descr_512 = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 14, + .len = 1, + .veroffs = 15, + .maxblocks = 4, + .pattern = cafe_bbt_pattern_512 +}; + +static struct nand_bbt_descr cafe_bbt_mirror_descr_512 = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 14, + .len = 1, + .veroffs = 15, + .maxblocks = 4, + .pattern = cafe_mirror_pattern_512 +}; + + static void cafe_nand_write_page_lowlevel(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf) { @@ -566,19 +620,21 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, ctrl |= 0x00007000; writel(ctrl | 0x05, cafe->mmio + 0x3004); writel(ctrl | 0x0a, cafe->mmio + 0x3004); - writel(0, cafe->mmio + 0x40); + writel(0, cafe->mmio + CAFE_NAND_DMA_CTRL); writel(0x7006, cafe->mmio + 0x3004); writel(0x700a, cafe->mmio + 0x3004); /* Set up DMA address */ - writel(cafe->dmaaddr & 0xffffffff, cafe->mmio + 0x44); + writel(cafe->dmaaddr & 0xffffffff, cafe->mmio + CAFE_NAND_DMA_ADDR0); if (sizeof(cafe->dmaaddr) > 4) - writel((cafe->dmaaddr >> 16) >> 16, cafe->mmio + 0x48); + /* Shift in two parts to shut the compiler up */ + writel((cafe->dmaaddr >> 16) >> 16, cafe->mmio + CAFE_NAND_DMA_ADDR1); else - writel(0, cafe->mmio + 0x48); + writel(0, cafe->mmio + CAFE_NAND_DMA_ADDR1); + cafe_dev_dbg(&cafe->pdev->dev, "Set DMA address to %x (virt %p)\n", - readl(cafe->mmio+0x44), cafe->dmabuf); + readl(cafe->mmio + CAFE_NAND_DMA_ADDR0), cafe->dmabuf); /* Enable NAND IRQ in global IRQ mask register */ writel(0x80000007, cafe->mmio + 0x300c); @@ -620,27 +676,30 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, cafe->ctl2 |= 1<<29; /* 2KiB page size */ /* Set up ECC according to the type of chip we found */ - if (mtd->writesize == 512 || mtd->writesize == 2048) { - cafe->nand.ecc.mode = NAND_ECC_HW_SYNDROME; - cafe->nand.ecc.size = mtd->writesize; - cafe->nand.ecc.bytes = 14; + if (mtd->writesize == 2048) { cafe->nand.ecc.layout = &cafe_oobinfo_2048; cafe->nand.bbt_td = &cafe_bbt_main_descr_2048; cafe->nand.bbt_md = &cafe_bbt_mirror_descr_2048; - cafe->nand.ecc.hwctl = (void *)cafe_nand_bug; - cafe->nand.ecc.calculate = (void *)cafe_nand_bug; - cafe->nand.ecc.correct = (void *)cafe_nand_bug; - cafe->nand.write_page = cafe_nand_write_page; - cafe->nand.ecc.write_page = cafe_nand_write_page_lowlevel; - cafe->nand.ecc.write_oob = cafe_nand_write_oob; - cafe->nand.ecc.read_page = cafe_nand_read_page; - cafe->nand.ecc.read_oob = cafe_nand_read_oob; - + } else if (mtd->writesize == 512) { + cafe->nand.ecc.layout = &cafe_oobinfo_512; + cafe->nand.bbt_td = &cafe_bbt_main_descr_512; + cafe->nand.bbt_md = &cafe_bbt_mirror_descr_512; } else { - printk(KERN_WARNING "Unexpected NAND flash writesize %d. Using software ECC\n", + printk(KERN_WARNING "Unexpected NAND flash writesize %d. Aborting\n", mtd->writesize); - cafe->nand.ecc.mode = NAND_ECC_NONE; + goto out_irq; } + cafe->nand.ecc.mode = NAND_ECC_HW_SYNDROME; + cafe->nand.ecc.size = mtd->writesize; + cafe->nand.ecc.bytes = 14; + cafe->nand.ecc.hwctl = (void *)cafe_nand_bug; + cafe->nand.ecc.calculate = (void *)cafe_nand_bug; + cafe->nand.ecc.correct = (void *)cafe_nand_bug; + cafe->nand.write_page = cafe_nand_write_page; + cafe->nand.ecc.write_page = cafe_nand_write_page_lowlevel; + cafe->nand.ecc.write_oob = cafe_nand_write_oob; + cafe->nand.ecc.read_page = cafe_nand_read_page; + cafe->nand.ecc.read_oob = cafe_nand_read_oob; err = nand_scan_tail(mtd); if (err) diff --git a/drivers/mtd/nand/cafe_ecc.c b/drivers/mtd/nand/cafe_ecc.c index 4df28a8..c4bec37 100644 --- a/drivers/mtd/nand/cafe_ecc.c +++ b/drivers/mtd/nand/cafe_ecc.c @@ -499,10 +499,11 @@ static void solve_2x3(unsigned short m[2][3], unsigned short *coefs) } static unsigned char gf64_inv[64] = { - 0, 1, 33, 62, 49, 43, 31, 44, 57, 37, 52, 28, 46, 40, 22, 25, + 0, 1, 33, 62, 49, 43, 31, 44, 57, 37, 52, 28, 46, 40, 22, 25, 61, 54, 51, 39, 26, 35, 14, 24, 23, 15, 20, 34, 11, 53, 45, 6, 63, 2, 27, 21, 56, 9, 50, 19, 13, 47, 48, 5, 7, 30, 12, 41, - 42, 4, 38, 18, 10, 29, 17, 60, 36, 8, 59, 58, 55, 16, 3, 32}; + 42, 4, 38, 18, 10, 29, 17, 60, 36, 8, 59, 58, 55, 16, 3, 32 +}; static unsigned short gf4096_inv(unsigned short din) { -- cgit v0.10.2 From 470b0a90d6a21cb72b671215f12ec7ec8a0db2c0 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 23 Oct 2006 14:29:04 +0100 Subject: =?UTF-8?q?[MTD]=20NAND:=20Disable=20ECC=20checking=20on=20CAF?= =?UTF-8?q?=C3=89=20since=20it's=20broken=20for=20now?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/cafe.c b/drivers/mtd/nand/cafe.c index 10132ef..6bcb430 100644 --- a/drivers/mtd/nand/cafe.c +++ b/drivers/mtd/nand/cafe.c @@ -67,6 +67,9 @@ module_param(skipbbt, int, 0644); static int debug = 0; module_param(debug, int, 0644); +static int checkecc = 0; +module_param(checkecc, int, 0644); + /* Hrm. Why isn't this already conditional on something in the struct device? */ #define cafe_dev_dbg(dev, args...) do { if (debug) dev_dbg(dev, ##args); } while(0) @@ -214,7 +217,7 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, writel(cafe->ctl2 | 0x100 | NAND_CMD_READSTART, cafe->mmio + CAFE_NAND_CTRL2); do_command: -#if 1 +#if 0 /* http://dev.laptop.org/ticket/200 ECC on read only works if we read precisely 0x80e bytes */ if (cafe->datalen == 2112) @@ -382,7 +385,7 @@ static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, chip->read_buf(mtd, buf, mtd->writesize); chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - if (readl(cafe->mmio + CAFE_NAND_ECC_RESULT) & (1<<18)) { + if (checkecc && readl(cafe->mmio + CAFE_NAND_ECC_RESULT) & (1<<18)) { unsigned short syn[8]; int i; -- cgit v0.10.2 From ff0dab64b4e9ce3a073365342297e76ddaae9697 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricard=20Wanderl=C3=B6f?= Date: Mon, 23 Oct 2006 09:33:34 +0200 Subject: [MTD] NAND: Fix nand_default_mark_blockbad() when flash-based BBT disabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a flash-based BBT is not used, nand_default_mark_blockbad() is supposed to mark the block bad in the oob. However, it sets the wrong length variable so that no bad block marker is in fact written. This patch attempts to rectify that. (As note, it seems to be that logically, it shouldn't be necessary to set both length variables, as one appears to be for the main buffer, and one for the oob buffer, but this is how it is done in several places, including the code for the mtd character device MEMWRITEOOB and MEMREADOOB ioctls. I'm not sure if this is a temporary solution during some rework of the mtd infrastructure, or whether there is a deeper thought here.) Signed-off-by: Ricard Wanderlöf Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index f23ab2c..8df36e2 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -362,7 +362,7 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) * access */ ofs += mtd->oobsize; - chip->ops.len = 2; + chip->ops.len = chip->ops.ooblen = 2; chip->ops.datbuf = NULL; chip->ops.oobbuf = buf; chip->ops.ooboffs = chip->badblockpos & ~0x01; -- cgit v0.10.2 From 616362de2fe224512fe105aec08f19f5470afb01 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 27 Oct 2006 01:47:34 -0400 Subject: ACPI: make ec_transaction not extern Fix sparse warning: drivers/acpi/ec.c:372:12: warning: function 'ec_transaction' with external linkage has definition Signed-off-by: Randy Dunlap Signed-off-by: Andrew Morton Signed-off-by: Len Brown diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index e6d4b08..7aa6007 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -369,7 +369,7 @@ int ec_write(u8 addr, u8 val) EXPORT_SYMBOL(ec_write); -extern int ec_transaction(u8 command, +int ec_transaction(u8 command, const u8 *wdata, unsigned wdata_len, u8 *rdata, unsigned rdata_len) { -- cgit v0.10.2 From 2f000f5c153e984d5c166e42a9d38113de8693b3 Mon Sep 17 00:00:00 2001 From: "Chen, Justin" Date: Tue, 10 Oct 2006 17:07:00 -0400 Subject: ACPI: optimize pci_rootbridge search acpi_get_pci_rootbridge_handle() walks the ACPI name space searching for seg, bus and the PCI_ROOT_HID_STRING -- returning the handle as soon as if find the match. But the current codes always parses through the whole namespace because the user_function find_pci_rootbridge() returns status=AE_OK when it finds the match. Make the find_pci_rootbridge() return AE_CTRL_TERMINATE when it finds the match. This reduces the ACPI namespace walk for acpi_get_pci_rootbridge_handle(). Signed-off-by: Justin Chen Signed-off-by: Len Brown diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index 10f160d..ba8fe83 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -189,8 +189,12 @@ find_pci_rootbridge(acpi_handle handle, u32 lvl, void *context, void **rv) bus = tmp; if (seg == find->seg && bus == find->bus) + { find->handle = handle; - status = AE_OK; + status = AE_CTRL_TERMINATE; + } + else + status = AE_OK; exit: kfree(buffer.pointer); return status; -- cgit v0.10.2 From 2c8cfdcbeb1ab0ec7bbd5af1be311b55281154c4 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 27 Oct 2006 09:53:08 +0300 Subject: =?UTF-8?q?[MTD]=20NAND:=20Caf=C3=A9=20ECC=20--=20remove=20spuriou?= =?UTF-8?q?s=20BUG=5FON()=20in=20err=5Fpos()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Being a value which isn't in the table is a case we explicitly check for in the caller. Don't BUG_ON() because it does actually happen in practice. Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/cafe_ecc.c b/drivers/mtd/nand/cafe_ecc.c index c4bec37..4621460 100644 --- a/drivers/mtd/nand/cafe_ecc.c +++ b/drivers/mtd/nand/cafe_ecc.c @@ -1045,7 +1045,6 @@ static unsigned short err_pos_lut[4096] = { static unsigned short err_pos(unsigned short din) { BUG_ON(din > 4096); - BUG_ON(err_pos_lut[din] == 0xfff); return err_pos_lut[din]; } static int chk_no_err_only(unsigned short *chk_syndrome_list, unsigned short *err_info) -- cgit v0.10.2 From dcc41bc81c872862652d68af8993b9fa32ce56a4 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 27 Oct 2006 09:55:34 +0300 Subject: =?UTF-8?q?[MTD]=20NAND:=20Reset=20Caf=C3=A9=20controller=20before?= =?UTF-8?q?=20initialising.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes http://dev.laptop.org/ticket/237 Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/cafe.c b/drivers/mtd/nand/cafe.c index 6bcb430..dd274c8 100644 --- a/drivers/mtd/nand/cafe.c +++ b/drivers/mtd/nand/cafe.c @@ -598,6 +598,10 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, cafe->nand.block_bad = cafe_nand_block_bad; } + /* Start off by resetting the NAND controller completely */ + writel(1, cafe->mmio + 0x3034); + writel(0, cafe->mmio + 0x3034); + /* Timings from Marvell's test code (not verified or calculated by us) */ writel(0xffffffff, cafe->mmio + CAFE_NAND_IRQ_MASK); #if 1 -- cgit v0.10.2 From b478c775a0c306c84215a1138e49fab540b94a5d Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 27 Oct 2006 14:50:04 +0300 Subject: =?UTF-8?q?[MTD]=20CAF=C3=89=20NAND:=20Add=20'slowtiming'=20parame?= =?UTF-8?q?ter,=20default=20usedma=20and=20checkecc=20on?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/cafe.c b/drivers/mtd/nand/cafe.c index dd274c8..d894c72 100644 --- a/drivers/mtd/nand/cafe.c +++ b/drivers/mtd/nand/cafe.c @@ -58,7 +58,7 @@ struct cafe_priv { }; -static int usedma = 0; +static int usedma = 1; module_param(usedma, int, 0644); static int skipbbt = 0; @@ -67,9 +67,12 @@ module_param(skipbbt, int, 0644); static int debug = 0; module_param(debug, int, 0644); -static int checkecc = 0; +static int checkecc = 1; module_param(checkecc, int, 0644); +static int slowtiming = 0; +module_param(slowtiming, int, 0644); + /* Hrm. Why isn't this already conditional on something in the struct device? */ #define cafe_dev_dbg(dev, args...) do { if (debug) dev_dbg(dev, ##args); } while(0) @@ -604,15 +607,16 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, /* Timings from Marvell's test code (not verified or calculated by us) */ writel(0xffffffff, cafe->mmio + CAFE_NAND_IRQ_MASK); -#if 1 - writel(0x01010a0a, cafe->mmio + CAFE_NAND_TIMING1); - writel(0x24121212, cafe->mmio + CAFE_NAND_TIMING2); - writel(0x11000000, cafe->mmio + CAFE_NAND_TIMING3); -#else - writel(0xffffffff, cafe->mmio + CAFE_NAND_TIMING1); - writel(0xffffffff, cafe->mmio + CAFE_NAND_TIMING2); - writel(0xffffffff, cafe->mmio + CAFE_NAND_TIMING3); -#endif + + if (!slowtiming) { + writel(0x01010a0a, cafe->mmio + CAFE_NAND_TIMING1); + writel(0x24121212, cafe->mmio + CAFE_NAND_TIMING2); + writel(0x11000000, cafe->mmio + CAFE_NAND_TIMING3); + } else { + writel(0xffffffff, cafe->mmio + CAFE_NAND_TIMING1); + writel(0xffffffff, cafe->mmio + CAFE_NAND_TIMING2); + writel(0xffffffff, cafe->mmio + CAFE_NAND_TIMING3); + } writel(0xffffffff, cafe->mmio + CAFE_NAND_IRQ_MASK); err = request_irq(pdev->irq, &cafe_nand_interrupt, SA_SHIRQ, "CAFE NAND", mtd); if (err) { -- cgit v0.10.2 From 7608194c4ae454fab23b8d940986eeb9c58c3478 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 27 Oct 2006 15:40:51 +0300 Subject: =?UTF-8?q?[MTD]=20NAND:=20Add=20ECC=20debugging=20for=20CAF=C3=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/cafe_ecc.c b/drivers/mtd/nand/cafe_ecc.c index 4621460..2d29265 100644 --- a/drivers/mtd/nand/cafe_ecc.c +++ b/drivers/mtd/nand/cafe_ecc.c @@ -19,6 +19,7 @@ */ #include +#include #include static unsigned short gf4096_mul(unsigned short, unsigned short); @@ -1322,16 +1323,43 @@ void correct_12bit_symbol(unsigned char *buf, unsigned short sym, buf[2+(3*(sym-2))/2] ^= (val >> 8); buf[3+(3*(sym-2))/2] ^= (val & 0xff); } - - } +static int debugecc = 0; +module_param(debugecc, int, 0644); + int cafe_correct_ecc(unsigned char *buf, unsigned short *chk_syndrome_list) { unsigned short err_info[9]; int i; + if (debugecc) { + printk(KERN_WARNING "cafe_correct_ecc invoked. Syndromes %x %x %x %x %x %x %x %x\n", + chk_syndrome_list[0], chk_syndrome_list[1], + chk_syndrome_list[2], chk_syndrome_list[3], + chk_syndrome_list[4], chk_syndrome_list[5], + chk_syndrome_list[6], chk_syndrome_list[7]); + for (i=0; i < 2048; i+=16) { + printk(KERN_WARNING "D %04x: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + i, + buf[i], buf[i+1], buf[i+2], buf[i+3], + buf[i+4], buf[i+5], buf[i+6], buf[i+7], + buf[i+8], buf[i+9], buf[i+10], buf[i+11], + buf[i+12], buf[i+13], buf[i+14], buf[i+15]); + } + for ( ; i < 2112; i+=16) { + printk(KERN_WARNING "O %02x: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + i - 2048, + buf[i], buf[i+1], buf[i+2], buf[i+3], + buf[i+4], buf[i+5], buf[i+6], buf[i+7], + buf[i+8], buf[i+9], buf[i+10], buf[i+11], + buf[i+12], buf[i+13], buf[i+14], buf[i+15]); + } + } + + + if (chk_no_err_only(chk_syndrome_list, err_info) && chk_1_err_only(chk_syndrome_list, err_info) && chk_2_err_only(chk_syndrome_list, err_info) && @@ -1340,8 +1368,13 @@ int cafe_correct_ecc(unsigned char *buf, return -EIO; } - for (i=0; i < err_info[0]; i++) + for (i=0; i < err_info[0]; i++) { + if (debugecc) + printk(KERN_WARNING "Correct symbol %d with 0x%03x\n", + err_info[1+i], err_info[5+i]); + correct_12bit_symbol(buf, err_info[1+i], err_info[5+i]); + } return err_info[0]; } -- cgit v0.10.2 From 63a1423763c6c38eeeaf6dc8cee986514ab67aed Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 27 Oct 2006 22:12:02 +0300 Subject: [MTD] NAND: Remove empty block ECC workaround They fixed the hardware so that ECC doesn't fail on reading an empty block. Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/cafe.c b/drivers/mtd/nand/cafe.c index d894c72..887040c 100644 --- a/drivers/mtd/nand/cafe.c +++ b/drivers/mtd/nand/cafe.c @@ -356,26 +356,6 @@ static int cafe_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, * The hw generator calculates the error syndrome automatically. Therefor * we need a special oob layout and handling. */ - -static unsigned short cafe_empty_syndromes[8] = { 4095, 748, 2629, 2920, 875, 1454, 51, 1456 }; - -static int is_all_ff(unsigned char *buf, int len) -{ - unsigned long *lbuf = (void *)buf; - int i; - - for (i=0; i < (len/sizeof(long)); i++) { - if (lbuf[i] != ~0UL) - return 0; - } - i *= sizeof(long); - for (; i< len; i++) { - if (buf[i] != 0xff) - return 0; - } - return 1; -} - static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf) { @@ -398,13 +378,7 @@ static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, syn[i+1] = (tmp >> 16) & 0xfff; } - /* FIXME: http://dev.laptop.org/ticket/215 */ - if (!memcmp(syn, cafe_empty_syndromes, sizeof(syn)) - && is_all_ff(chip->oob_poi, 14) - && is_all_ff(buf, mtd->writesize)) { - dev_dbg(&cafe->pdev->dev, "ECC error reported on empty block\n"); - /* It was an empty block. Nothing to fix here except the hardware */ - } else if ((i = cafe_correct_ecc(buf, syn)) < 0) { + if ((i = cafe_correct_ecc(buf, syn)) < 0) { dev_dbg(&cafe->pdev->dev, "Failed to correct ECC\n"); mtd->ecc_stats.failed++; } else { -- cgit v0.10.2 From a020727b1628cb4d7b70733222253c7fa3ec6113 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sat, 28 Oct 2006 17:08:38 +0300 Subject: =?UTF-8?q?[MTD]=20NAND:=20Fix=20timing=20calculation=20in=20CAF?= =?UTF-8?q?=C3=89=20debugging=20message?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/cafe.c b/drivers/mtd/nand/cafe.c index 887040c..35a8687 100644 --- a/drivers/mtd/nand/cafe.c +++ b/drivers/mtd/nand/cafe.c @@ -272,7 +272,8 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, cpu_relax(); } writel(doneint, cafe->mmio + CAFE_NAND_IRQ); - cafe_dev_dbg(&cafe->pdev->dev, "Command %x completed after %d usec, irqs %x (%x)\n", command, 50000-c, irqs, readl(cafe->mmio + CAFE_NAND_IRQ)); + cafe_dev_dbg(&cafe->pdev->dev, "Command %x completed after %d usec, irqs %x (%x)\n", + command, 500000-c, irqs, readl(cafe->mmio + CAFE_NAND_IRQ)); } -- cgit v0.10.2 From 8b0dc866dd9b8d10a53cb3537385a51b7ee54b62 Mon Sep 17 00:00:00 2001 From: Kristen Carlson Accardi Date: Mon, 30 Oct 2006 11:18:45 -0800 Subject: ACPI: dock: use mutex instead of spinlock http://bugzilla.kernel.org/show_bug.cgi?id=7303 Use a mutex instead of a spinlock for locking the hotplug list because we need to call into the ACPI subsystem which might sleep. Signed-off-by: Kristen Carlson Accardi Signed-off-by: Len Brown diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 578b99b..c7df2a1 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -44,7 +44,7 @@ struct dock_station { unsigned long last_dock_time; u32 flags; spinlock_t dd_lock; - spinlock_t hp_lock; + struct mutex hp_lock; struct list_head dependent_devices; struct list_head hotplug_devices; }; @@ -114,9 +114,9 @@ static void dock_add_hotplug_device(struct dock_station *ds, struct dock_dependent_device *dd) { - spin_lock(&ds->hp_lock); + mutex_lock(&ds->hp_lock); list_add_tail(&dd->hotplug_list, &ds->hotplug_devices); - spin_unlock(&ds->hp_lock); + mutex_unlock(&ds->hp_lock); } /** @@ -130,9 +130,9 @@ static void dock_del_hotplug_device(struct dock_station *ds, struct dock_dependent_device *dd) { - spin_lock(&ds->hp_lock); + mutex_lock(&ds->hp_lock); list_del(&dd->hotplug_list); - spin_unlock(&ds->hp_lock); + mutex_unlock(&ds->hp_lock); } /** @@ -295,7 +295,7 @@ static void hotplug_dock_devices(struct dock_station *ds, u32 event) { struct dock_dependent_device *dd; - spin_lock(&ds->hp_lock); + mutex_lock(&ds->hp_lock); /* * First call driver specific hotplug functions @@ -317,7 +317,7 @@ static void hotplug_dock_devices(struct dock_station *ds, u32 event) else dock_create_acpi_device(dd->handle); } - spin_unlock(&ds->hp_lock); + mutex_unlock(&ds->hp_lock); } static void dock_event(struct dock_station *ds, u32 event, int num) @@ -625,7 +625,7 @@ static int dock_add(acpi_handle handle) INIT_LIST_HEAD(&dock_station->dependent_devices); INIT_LIST_HEAD(&dock_station->hotplug_devices); spin_lock_init(&dock_station->dd_lock); - spin_lock_init(&dock_station->hp_lock); + mutex_init(&dock_station->hp_lock); ATOMIC_INIT_NOTIFIER_HEAD(&dock_notifier_list); /* Find dependent devices */ -- cgit v0.10.2 From 195a253b6632e2b7e6319f2f67120e708646554e Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 31 Oct 2006 12:30:11 +0800 Subject: =?UTF-8?q?[MTD]=20NAND:=20Use=20register=20#defines=20throughout?= =?UTF-8?q?=20CAF=C3=89=20driver,=20not=20numbers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also use cafe_readl() and cafe_writel() abstraction to make code slightly cleaner -- especially if we want to use it in PIO mode. Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/cafe.c b/drivers/mtd/nand/cafe.c index 35a8687..175cf82 100644 --- a/drivers/mtd/nand/cafe.c +++ b/drivers/mtd/nand/cafe.c @@ -40,6 +40,11 @@ #define CAFE_NAND_READ_DATA 0x1000 #define CAFE_NAND_WRITE_DATA 0x2000 +#define CAFE_GLOBAL_CTRL 0x3004 +#define CAFE_GLOBAL_IRQ 0x3008 +#define CAFE_GLOBAL_IRQ_MASK 0x300c +#define CAFE_NAND_RESET 0x3034 + int cafe_correct_ecc(unsigned char *buf, unsigned short *chk_syndrome_list); @@ -76,18 +81,21 @@ module_param(slowtiming, int, 0644); /* Hrm. Why isn't this already conditional on something in the struct device? */ #define cafe_dev_dbg(dev, args...) do { if (debug) dev_dbg(dev, ##args); } while(0) +/* Make it easier to switch to PIO if we need to */ +#define cafe_readl(cafe, addr) readl((cafe)->mmio + CAFE_##addr) +#define cafe_writel(cafe, datum, addr) writel(datum, (cafe)->mmio + CAFE_##addr) static int cafe_device_ready(struct mtd_info *mtd) { struct cafe_priv *cafe = mtd->priv; - int result = !!(readl(cafe->mmio + CAFE_NAND_STATUS) | 0x40000000); - uint32_t irqs = readl(cafe->mmio + CAFE_NAND_IRQ); + int result = !!(cafe_readl(cafe, NAND_STATUS) | 0x40000000); + uint32_t irqs = cafe_readl(cafe, NAND_IRQ); - writel(irqs, cafe->mmio+CAFE_NAND_IRQ); + cafe_writel(cafe, irqs, NAND_IRQ); cafe_dev_dbg(&cafe->pdev->dev, "NAND device is%s ready, IRQ %x (%x) (%x,%x)\n", - result?"":" not", irqs, readl(cafe->mmio + CAFE_NAND_IRQ), - readl(cafe->mmio + 0x3008), readl(cafe->mmio + 0x300c)); + result?"":" not", irqs, cafe_readl(cafe, NAND_IRQ), + cafe_readl(cafe, GLOBAL_IRQ), cafe_readl(cafe, GLOBAL_IRQ_MASK)); return result; } @@ -146,14 +154,14 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, if (command == NAND_CMD_ERASE2 || command == NAND_CMD_PAGEPROG) { /* Second half of a command we already calculated */ - writel(cafe->ctl2 | 0x100 | command, cafe->mmio + CAFE_NAND_CTRL2); + cafe_writel(cafe, cafe->ctl2 | 0x100 | command, NAND_CTRL2); ctl1 = cafe->ctl1; cafe_dev_dbg(&cafe->pdev->dev, "Continue command, ctl1 %08x, #data %d\n", cafe->ctl1, cafe->nr_data); goto do_command; } /* Reset ECC engine */ - writel(0, cafe->mmio + CAFE_NAND_CTRL2); + cafe_writel(cafe, 0, NAND_CTRL2); /* Emulate NAND_CMD_READOOB on large-page chips */ if (mtd->writesize > 512 && @@ -166,15 +174,15 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, for small-page chips, to position the buffer correctly? */ if (column != -1) { - writel(column, cafe->mmio + CAFE_NAND_ADDR1); + cafe_writel(cafe, column, NAND_ADDR1); adrbytes = 2; if (page_addr != -1) goto write_adr2; } else if (page_addr != -1) { - writel(page_addr & 0xffff, cafe->mmio + CAFE_NAND_ADDR1); + cafe_writel(cafe, page_addr & 0xffff, NAND_ADDR1); page_addr >>= 16; write_adr2: - writel(page_addr, cafe->mmio+0x20); + cafe_writel(cafe, page_addr, NAND_ADDR2); adrbytes += 2; if (mtd->size > mtd->writesize << 16) adrbytes++; @@ -215,9 +223,9 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, } /* RNDOUT and READ0 commands need a following byte */ if (command == NAND_CMD_RNDOUT) - writel(cafe->ctl2 | 0x100 | NAND_CMD_RNDOUTSTART, cafe->mmio + CAFE_NAND_CTRL2); + cafe_writel(cafe, cafe->ctl2 | 0x100 | NAND_CMD_RNDOUTSTART, NAND_CTRL2); else if (command == NAND_CMD_READ0 && mtd->writesize > 512) - writel(cafe->ctl2 | 0x100 | NAND_CMD_READSTART, cafe->mmio + CAFE_NAND_CTRL2); + cafe_writel(cafe, cafe->ctl2 | 0x100 | NAND_CMD_READSTART, NAND_CTRL2); do_command: #if 0 @@ -227,11 +235,11 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, cafe->datalen = 2062; #endif cafe_dev_dbg(&cafe->pdev->dev, "dlen %x, ctl1 %x, ctl2 %x\n", - cafe->datalen, ctl1, readl(cafe->mmio+CAFE_NAND_CTRL2)); + cafe->datalen, ctl1, cafe_readl(cafe, NAND_CTRL2)); /* NB: The datasheet lies -- we really should be subtracting 1 here */ - writel(cafe->datalen, cafe->mmio + CAFE_NAND_DATA_LEN); - writel(0x90000000, cafe->mmio + CAFE_NAND_IRQ); + cafe_writel(cafe, cafe->datalen, NAND_DATA_LEN); + cafe_writel(cafe, 0x90000000, NAND_IRQ); if (usedma && (ctl1 & (3<<25))) { uint32_t dmactl = 0xc0000000 + cafe->datalen; /* If WR or RD bits set, set up DMA */ @@ -242,7 +250,7 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, the command. */ doneint = 0x10000000; } - writel(dmactl, cafe->mmio + CAFE_NAND_DMA_CTRL); + cafe_writel(cafe, dmactl, NAND_DMA_CTRL); } cafe->datalen = 0; @@ -253,7 +261,7 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, printk("Register %x: %08x\n", i, readl(cafe->mmio + i)); } #endif - writel(ctl1, cafe->mmio + CAFE_NAND_CTRL1); + cafe_writel(cafe, ctl1, NAND_CTRL1); /* Apply this short delay always to ensure that we do wait tWB in * any case on any machine. */ ndelay(100); @@ -263,7 +271,7 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, uint32_t irqs; while (c--) { - irqs = readl(cafe->mmio + CAFE_NAND_IRQ); + irqs = cafe_readl(cafe, NAND_IRQ); if (irqs & doneint) break; udelay(1); @@ -271,9 +279,9 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, cafe_dev_dbg(&cafe->pdev->dev, "Wait for ready, IRQ %x\n", irqs); cpu_relax(); } - writel(doneint, cafe->mmio + CAFE_NAND_IRQ); + cafe_writel(cafe, doneint, NAND_IRQ); cafe_dev_dbg(&cafe->pdev->dev, "Command %x completed after %d usec, irqs %x (%x)\n", - command, 500000-c, irqs, readl(cafe->mmio + CAFE_NAND_IRQ)); + command, 500000-c, irqs, cafe_readl(cafe, NAND_IRQ)); } @@ -296,11 +304,11 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, case NAND_CMD_STATUS_ERROR1: case NAND_CMD_STATUS_ERROR2: case NAND_CMD_STATUS_ERROR3: - writel(cafe->ctl2, cafe->mmio + CAFE_NAND_CTRL2); + cafe_writel(cafe, cafe->ctl2, NAND_CTRL2); return; } nand_wait_ready(mtd); - writel(cafe->ctl2, cafe->mmio + CAFE_NAND_CTRL2); + cafe_writel(cafe, cafe->ctl2, NAND_CTRL2); } static void cafe_select_chip(struct mtd_info *mtd, int chipnr) @@ -313,12 +321,12 @@ static int cafe_nand_interrupt(int irq, void *id, struct pt_regs *regs) { struct mtd_info *mtd = id; struct cafe_priv *cafe = mtd->priv; - uint32_t irqs = readl(cafe->mmio + CAFE_NAND_IRQ); - writel(irqs & ~0x90000000, cafe->mmio + CAFE_NAND_IRQ); + uint32_t irqs = cafe_readl(cafe, NAND_IRQ); + cafe_writel(cafe, irqs & ~0x90000000, NAND_IRQ); if (!irqs) return IRQ_NONE; - cafe_dev_dbg(&cafe->pdev->dev, "irq, bits %x (%x)\n", irqs, readl(cafe->mmio + CAFE_NAND_IRQ)); + cafe_dev_dbg(&cafe->pdev->dev, "irq, bits %x (%x)\n", irqs, cafe_readl(cafe, NAND_IRQ)); return IRQ_HANDLED; } @@ -363,18 +371,18 @@ static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, struct cafe_priv *cafe = mtd->priv; cafe_dev_dbg(&cafe->pdev->dev, "ECC result %08x SYN1,2 %08x\n", - readl(cafe->mmio + CAFE_NAND_ECC_RESULT), - readl(cafe->mmio + CAFE_NAND_ECC_SYN01)); + cafe_readl(cafe, NAND_ECC_RESULT), + cafe_readl(cafe, NAND_ECC_SYN01)); chip->read_buf(mtd, buf, mtd->writesize); chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - if (checkecc && readl(cafe->mmio + CAFE_NAND_ECC_RESULT) & (1<<18)) { + if (checkecc && cafe_readl(cafe, NAND_ECC_RESULT) & (1<<18)) { unsigned short syn[8]; int i; for (i=0; i<8; i+=2) { - uint32_t tmp = readl(cafe->mmio + CAFE_NAND_ECC_SYN01 + (i*2)); + uint32_t tmp = cafe_readl(cafe, NAND_ECC_SYN01 + (i*2)); syn[i] = tmp & 0xfff; syn[i+1] = (tmp >> 16) & 0xfff; } @@ -577,22 +585,22 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, } /* Start off by resetting the NAND controller completely */ - writel(1, cafe->mmio + 0x3034); - writel(0, cafe->mmio + 0x3034); + cafe_writel(cafe, 1, NAND_RESET); + cafe_writel(cafe, 0, NAND_RESET); - /* Timings from Marvell's test code (not verified or calculated by us) */ - writel(0xffffffff, cafe->mmio + CAFE_NAND_IRQ_MASK); + cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK); + /* Timings from Marvell's test code (not verified or calculated by us) */ if (!slowtiming) { - writel(0x01010a0a, cafe->mmio + CAFE_NAND_TIMING1); - writel(0x24121212, cafe->mmio + CAFE_NAND_TIMING2); - writel(0x11000000, cafe->mmio + CAFE_NAND_TIMING3); + cafe_writel(cafe, 0x01010a0a, NAND_TIMING1); + cafe_writel(cafe, 0x24121212, NAND_TIMING2); + cafe_writel(cafe, 0x11000000, NAND_TIMING3); } else { - writel(0xffffffff, cafe->mmio + CAFE_NAND_TIMING1); - writel(0xffffffff, cafe->mmio + CAFE_NAND_TIMING2); - writel(0xffffffff, cafe->mmio + CAFE_NAND_TIMING3); + cafe_writel(cafe, 0xffffffff, NAND_TIMING1); + cafe_writel(cafe, 0xffffffff, NAND_TIMING2); + cafe_writel(cafe, 0xffffffff, NAND_TIMING3); } - writel(0xffffffff, cafe->mmio + CAFE_NAND_IRQ_MASK); + cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK); err = request_irq(pdev->irq, &cafe_nand_interrupt, SA_SHIRQ, "CAFE NAND", mtd); if (err) { dev_warn(&pdev->dev, "Could not register IRQ %d\n", pdev->irq); @@ -601,31 +609,31 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, } #if 1 /* Disable master reset, enable NAND clock */ - ctrl = readl(cafe->mmio + 0x3004); + ctrl = cafe_readl(cafe, GLOBAL_CTRL); ctrl &= 0xffffeff0; ctrl |= 0x00007000; - writel(ctrl | 0x05, cafe->mmio + 0x3004); - writel(ctrl | 0x0a, cafe->mmio + 0x3004); - writel(0, cafe->mmio + CAFE_NAND_DMA_CTRL); + cafe_writel(cafe, ctrl | 0x05, GLOBAL_CTRL); + cafe_writel(cafe, ctrl | 0x0a, GLOBAL_CTRL); + cafe_writel(cafe, 0, NAND_DMA_CTRL); - writel(0x7006, cafe->mmio + 0x3004); - writel(0x700a, cafe->mmio + 0x3004); + cafe_writel(cafe, 0x7006, GLOBAL_CTRL); + cafe_writel(cafe, 0x700a, GLOBAL_CTRL); /* Set up DMA address */ - writel(cafe->dmaaddr & 0xffffffff, cafe->mmio + CAFE_NAND_DMA_ADDR0); + cafe_writel(cafe, cafe->dmaaddr & 0xffffffff, NAND_DMA_ADDR0); if (sizeof(cafe->dmaaddr) > 4) /* Shift in two parts to shut the compiler up */ - writel((cafe->dmaaddr >> 16) >> 16, cafe->mmio + CAFE_NAND_DMA_ADDR1); + cafe_writel(cafe, (cafe->dmaaddr >> 16) >> 16, NAND_DMA_ADDR1); else - writel(0, cafe->mmio + CAFE_NAND_DMA_ADDR1); + cafe_writel(cafe, 0, NAND_DMA_ADDR1); cafe_dev_dbg(&cafe->pdev->dev, "Set DMA address to %x (virt %p)\n", - readl(cafe->mmio + CAFE_NAND_DMA_ADDR0), cafe->dmabuf); + cafe_readl(cafe, NAND_DMA_ADDR0), cafe->dmabuf); /* Enable NAND IRQ in global IRQ mask register */ - writel(0x80000007, cafe->mmio + 0x300c); + cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK); cafe_dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n", - readl(cafe->mmio + 0x3004), readl(cafe->mmio + 0x300c)); + cafe_readl(cafe, GLOBAL_CTRL), cafe_readl(cafe, GLOBAL_IRQ_MASK)); #endif #if 1 mtd->writesize=2048; @@ -649,7 +657,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, #if 0 writel(0x84600070, cafe->mmio); udelay(10); - cafe_dev_dbg(&cafe->pdev->dev, "Status %x\n", readl(cafe->mmio + 0x30)); + cafe_dev_dbg(&cafe->pdev->dev, "Status %x\n", cafe_readl(cafe, NAND_NONMEM)); #endif /* Scan to find existance of the device */ if (nand_scan_ident(mtd, 1)) { @@ -697,7 +705,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, out_irq: /* Disable NAND IRQ in global IRQ mask register */ - writel(~1 & readl(cafe->mmio + 0x300c), cafe->mmio + 0x300c); + cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK); free_irq(pdev->irq, mtd); out_free_dma: dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr); @@ -716,7 +724,7 @@ static void __devexit cafe_nand_remove(struct pci_dev *pdev) del_mtd_device(mtd); /* Disable NAND IRQ in global IRQ mask register */ - writel(~1 & readl(cafe->mmio + 0x300c), cafe->mmio + 0x300c); + cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK); free_irq(pdev->irq, mtd); nand_release(mtd); pci_iounmap(pdev, cafe->mmio); -- cgit v0.10.2 From be8444bdf34f7ba21e2364ca296c68e81033e3b2 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 31 Oct 2006 12:36:04 +0800 Subject: =?UTF-8?q?[MTD]=20NAND:=20Add=20register=20debugging=20spew=20opt?= =?UTF-8?q?ion=20to=20CAF=C3=89=20driver?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/cafe.c b/drivers/mtd/nand/cafe.c index 175cf82..c5d03b0 100644 --- a/drivers/mtd/nand/cafe.c +++ b/drivers/mtd/nand/cafe.c @@ -72,6 +72,9 @@ module_param(skipbbt, int, 0644); static int debug = 0; module_param(debug, int, 0644); +static int regdebug = 0; +module_param(regdebug, int, 0644); + static int checkecc = 1; module_param(checkecc, int, 0644); @@ -228,12 +231,6 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, cafe_writel(cafe, cafe->ctl2 | 0x100 | NAND_CMD_READSTART, NAND_CTRL2); do_command: -#if 0 - /* http://dev.laptop.org/ticket/200 - ECC on read only works if we read precisely 0x80e bytes */ - if (cafe->datalen == 2112) - cafe->datalen = 2062; -#endif cafe_dev_dbg(&cafe->pdev->dev, "dlen %x, ctl1 %x, ctl2 %x\n", cafe->datalen, ctl1, cafe_readl(cafe, NAND_CTRL2)); @@ -254,13 +251,13 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, } cafe->datalen = 0; -#if 0 - { int i; - printk("About to write command %08x\n", ctl1); - for (i=0; i< 0x5c; i+=4) - printk("Register %x: %08x\n", i, readl(cafe->mmio + i)); + if (unlikely(regdebug)) { + int i; + printk("About to write command %08x to register 0\n", ctl1); + for (i=4; i< 0x5c; i+=4) + printk("Register %x: %08x\n", i, readl(cafe->mmio + i)); } -#endif + cafe_writel(cafe, ctl1, NAND_CTRL1); /* Apply this short delay always to ensure that we do wait tWB in * any case on any machine. */ @@ -388,7 +385,10 @@ static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, } if ((i = cafe_correct_ecc(buf, syn)) < 0) { - dev_dbg(&cafe->pdev->dev, "Failed to correct ECC\n"); + dev_dbg(&cafe->pdev->dev, "Failed to correct ECC at %08x\n", + cafe_readl(cafe, NAND_ADDR2) * 2048); + for (i=0; i< 0x5c; i+=4) + printk("Register %x: %08x\n", i, readl(cafe->mmio + i)); mtd->ecc_stats.failed++; } else { dev_dbg(&cafe->pdev->dev, "Corrected %d symbol errors\n", i); -- cgit v0.10.2 From cad40654c312fc51bdb520e9be91e29a9742bbcb Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 1 Nov 2006 08:19:20 +0800 Subject: =?UTF-8?q?[MTD]=20NAND:=20Fix=20ECC=20settings=20in=20CAF=C3=89?= =?UTF-8?q?=20controller=20driver.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We were resetting cafe->ctl2 to zero after an erase (and also during a write, but it was correctly reset after that). This meant that ECC reads after an erase were failing. Doh. Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/cafe.c b/drivers/mtd/nand/cafe.c index c5d03b0..fad304b 100644 --- a/drivers/mtd/nand/cafe.c +++ b/drivers/mtd/nand/cafe.c @@ -159,6 +159,7 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, /* Second half of a command we already calculated */ cafe_writel(cafe, cafe->ctl2 | 0x100 | command, NAND_CTRL2); ctl1 = cafe->ctl1; + cafe->ctl2 &= ~(1<<30); cafe_dev_dbg(&cafe->pdev->dev, "Continue command, ctl1 %08x, #data %d\n", cafe->ctl1, cafe->nr_data); goto do_command; @@ -219,7 +220,6 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, /* Ignore the first command of a pair; the hardware deals with them both at once, later */ cafe->ctl1 = ctl1; - cafe->ctl2 = 0; cafe_dev_dbg(&cafe->pdev->dev, "Setup for delayed command, ctl1 %08x, dlen %x\n", cafe->ctl1, cafe->datalen); return; @@ -281,9 +281,7 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, command, 500000-c, irqs, cafe_readl(cafe, NAND_IRQ)); } - - cafe->ctl2 &= ~(1<<8); - cafe->ctl2 &= ~(1<<30); + WARN_ON(cafe->ctl2 & (1<<30)); switch (command) { @@ -471,9 +469,7 @@ static void cafe_nand_write_page_lowlevel(struct mtd_info *mtd, chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); /* Set up ECC autogeneration */ - cafe->ctl2 |= (1<<27) | (1<<30); - if (mtd->writesize == 2048) - cafe->ctl2 |= (1<<29); + cafe->ctl2 |= (1<<30); } static int cafe_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, -- cgit v0.10.2 From 9185cfa92507d07ac787bc73d06c42222eec7239 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 1 Nov 2006 13:23:14 +0100 Subject: ACPI: S4: Use "platform" rather than "shutdown" mode by default It has been reported that on some systems the functionality after a resume from disk is limited if the system is simply powered off during the suspend instead of using the ACPI S4 suspend (aka platform mode). Unfortunately the default is currently to power off the system during the suspend so the users of these systems experience problems after the resume if they don't switch to the platform mode explicitly. This patch makes swsusp use the platform mode by default to avoid such situations. Signed-off-by: Rafael J. Wysocki Acked-by: Stefan Seyfried Acked-by: Pavel Machek Signed-off-by: Len Brown diff --git a/kernel/power/disk.c b/kernel/power/disk.c index d3a158a..ae6bbc9 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c @@ -44,9 +44,11 @@ static void power_down(suspend_disk_method_t mode) switch(mode) { case PM_DISK_PLATFORM: - kernel_shutdown_prepare(SYSTEM_SUSPEND_DISK); - error = pm_ops->enter(PM_SUSPEND_DISK); - break; + if (pm_ops && pm_ops->enter) { + kernel_shutdown_prepare(SYSTEM_SUSPEND_DISK); + error = pm_ops->enter(PM_SUSPEND_DISK); + break; + } case PM_DISK_SHUTDOWN: kernel_power_off(); break; diff --git a/kernel/power/main.c b/kernel/power/main.c index 873228c..1210961 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -27,7 +27,7 @@ DECLARE_MUTEX(pm_sem); struct pm_ops *pm_ops; -suspend_disk_method_t pm_disk_mode = PM_DISK_SHUTDOWN; +suspend_disk_method_t pm_disk_mode = PM_DISK_PLATFORM; /** * pm_set_ops - Set the global power method table. -- cgit v0.10.2 From 28af24bb8470c7d0573b703a2955548b73a6c066 Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Fri, 3 Nov 2006 15:13:27 -0500 Subject: [AGPGART] Fix up misprogrammed bridges with incorrect AGPv2 rates. Some dumb bridges are programmed to disobey the AGP2 spec. This is likely a BIOS misprogramming rather than poweron default, or it would be a lot more common. AGPv2 spec 6.1.9 states: "The RATE field indicates the data transfer rates supported by this device. A.G.P. devices must report all that apply." Fix them up as best we can. This will prevent errors like.. agpgart: Found an AGP 3.5 compliant device at 0000:00:00.0. agpgart: req mode 1f000201 bridge_agpstat 1f000a14 vga_agpstat 2f000217. agpgart: Device is in legacy mode, falling back to 2.x agpgart: Putting AGP V2 device at 0000:00:00.0 into 0x mode agpgart: Putting AGP V2 device at 0000:01:00.0 into 0x mode agpgart: Putting AGP V2 device at 0000:01:00.1 into 0x mode https://bugs.freedesktop.org/show_bug.cgi?id=8816 Signed-off-by: Dave Jones diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c index c392001..ca4629f 100644 --- a/drivers/char/agp/generic.c +++ b/drivers/char/agp/generic.c @@ -419,6 +419,31 @@ static void agp_v2_parse_one(u32 *requested_mode, u32 *bridge_agpstat, u32 *vga_ *requested_mode &= ~AGP2_RESERVED_MASK; } + /* + * Some dumb bridges are programmed to disobey the AGP2 spec. + * This is likely a BIOS misprogramming rather than poweron default, or + * it would be a lot more common. + * https://bugs.freedesktop.org/show_bug.cgi?id=8816 + * AGPv2 spec 6.1.9 states: + * The RATE field indicates the data transfer rates supported by this + * device. A.G.P. devices must report all that apply. + * Fix them up as best we can. + */ + switch (*bridge_agpstat & 7) { + case 4: + *bridge_agpstat |= (AGPSTAT2_2X | AGPSTAT2_1X); + printk(KERN_INFO PFX "BIOS bug. AGP bridge claims to only support x4 rate" + "Fixing up support for x2 & x1\n"); + break; + case 2: + *bridge_agpstat |= AGPSTAT2_1X; + printk(KERN_INFO PFX "BIOS bug. AGP bridge claims to only support x2 rate" + "Fixing up support for x1\n"); + break; + default: + break; + } + /* Check the speed bits make sense. Only one should be set. */ tmp = *requested_mode & 7; switch (tmp) { -- cgit v0.10.2 From 6b15484ccb91e85100cf164067bf3bc6c5038726 Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Thu, 2 Nov 2006 13:13:22 +0100 Subject: ACPI: Get rid of 'unused variable' warning in acpi_ev_global_lock_handler() Fix this warning : drivers/acpi/events/evmisc.c: In function `acpi_ev_global_lock_handler': drivers/acpi/events/evmisc.c:334: warning: unused variable `status' Signed-off-by: Jesper Juhl Signed-off-by: Len Brown diff --git a/drivers/acpi/events/evmisc.c b/drivers/acpi/events/evmisc.c index ee2a10b..bf63edc 100644 --- a/drivers/acpi/events/evmisc.c +++ b/drivers/acpi/events/evmisc.c @@ -331,7 +331,6 @@ static void ACPI_SYSTEM_XFACE acpi_ev_global_lock_thread(void *context) static u32 acpi_ev_global_lock_handler(void *context) { u8 acquired = FALSE; - acpi_status status; /* * Attempt to get the lock -- cgit v0.10.2 From b7b09b1cdf4de7e28424250972d4a5526e5bdfb9 Mon Sep 17 00:00:00 2001 From: Satoru Takeuchi Date: Thu, 2 Nov 2006 19:08:57 +0900 Subject: ACPI: update comment Fixing wrong description for acpi_gpe_sleep_prepare(). acpi_gpe_sleep_prepare() had only used on power off and was changed to also used on entering some sleep state. However its description isn't changed yet. Signed-off-by: Satoru Takeuchi Signed-off-by: Len Brown diff --git a/drivers/acpi/sleep/wakeup.c b/drivers/acpi/sleep/wakeup.c index af1dbab..fab8f26 100644 --- a/drivers/acpi/sleep/wakeup.c +++ b/drivers/acpi/sleep/wakeup.c @@ -183,11 +183,11 @@ late_initcall(acpi_wakeup_device_init); #endif /* - * Disable all wakeup GPEs before power off. - * + * Disable all wakeup GPEs before entering requested sleep state. + * @sleep_state: ACPI state * Since acpi_enter_sleep_state() will disable all * RUNTIME GPEs, we simply mark all GPES that - * are not enabled for wakeup from S5 as RUNTIME. + * are not enabled for wakeup from requested state as RUNTIME. */ void acpi_gpe_sleep_prepare(u32 sleep_state) { -- cgit v0.10.2 From e08f5f5bb5dfaaa28d69ffe37eb774533297657f Mon Sep 17 00:00:00 2001 From: Gautham R Shenoy Date: Thu, 26 Oct 2006 16:20:58 +0530 Subject: [CPUFREQ] Fix coding style issues in cpufreq. Clean up cpufreq subsystem to fix coding style issues and to improve the readability. Signed-off-by: Gautham R Shenoy Signed-off-by: Dave Jones diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 56c433e..0c18ac2 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -29,7 +29,8 @@ #include #include -#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_CORE, "cpufreq-core", msg) +#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_CORE, \ + "cpufreq-core", msg) /** * The "cpufreq driver" - the arch- or hardware-dependent low @@ -41,7 +42,8 @@ static struct cpufreq_policy *cpufreq_cpu_data[NR_CPUS]; static DEFINE_SPINLOCK(cpufreq_driver_lock); /* internal prototypes */ -static int __cpufreq_governor(struct cpufreq_policy *policy, unsigned int event); +static int __cpufreq_governor(struct cpufreq_policy *policy, + unsigned int event); static void handle_update(void *data); /** @@ -151,7 +153,8 @@ static void cpufreq_debug_disable_ratelimit(void) spin_unlock_irqrestore(&disable_ratelimit_lock, flags); } -void cpufreq_debug_printk(unsigned int type, const char *prefix, const char *fmt, ...) +void cpufreq_debug_printk(unsigned int type, const char *prefix, + const char *fmt, ...) { char s[256]; va_list args; @@ -161,7 +164,8 @@ void cpufreq_debug_printk(unsigned int type, const char *prefix, const char *fmt WARN_ON(!prefix); if (type & debug) { spin_lock_irqsave(&disable_ratelimit_lock, flags); - if (!disable_ratelimit && debug_ratelimit && !printk_ratelimit()) { + if (!disable_ratelimit && debug_ratelimit + && !printk_ratelimit()) { spin_unlock_irqrestore(&disable_ratelimit_lock, flags); return; } @@ -182,10 +186,12 @@ EXPORT_SYMBOL(cpufreq_debug_printk); module_param(debug, uint, 0644); -MODULE_PARM_DESC(debug, "CPUfreq debugging: add 1 to debug core, 2 to debug drivers, and 4 to debug governors."); +MODULE_PARM_DESC(debug, "CPUfreq debugging: add 1 to debug core," + " 2 to debug drivers, and 4 to debug governors."); module_param(debug_ratelimit, uint, 0644); -MODULE_PARM_DESC(debug_ratelimit, "CPUfreq debugging: set to 0 to disable ratelimiting."); +MODULE_PARM_DESC(debug_ratelimit, "CPUfreq debugging:" + " set to 0 to disable ratelimiting."); #else /* !CONFIG_CPU_FREQ_DEBUG */ @@ -219,17 +225,23 @@ static void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci) if (!l_p_j_ref_freq) { l_p_j_ref = loops_per_jiffy; l_p_j_ref_freq = ci->old; - dprintk("saving %lu as reference value for loops_per_jiffy; freq is %u kHz\n", l_p_j_ref, l_p_j_ref_freq); + dprintk("saving %lu as reference value for loops_per_jiffy;" + "freq is %u kHz\n", l_p_j_ref, l_p_j_ref_freq); } if ((val == CPUFREQ_PRECHANGE && ci->old < ci->new) || (val == CPUFREQ_POSTCHANGE && ci->old > ci->new) || (val == CPUFREQ_RESUMECHANGE || val == CPUFREQ_SUSPENDCHANGE)) { - loops_per_jiffy = cpufreq_scale(l_p_j_ref, l_p_j_ref_freq, ci->new); - dprintk("scaling loops_per_jiffy to %lu for frequency %u kHz\n", loops_per_jiffy, ci->new); + loops_per_jiffy = cpufreq_scale(l_p_j_ref, l_p_j_ref_freq, + ci->new); + dprintk("scaling loops_per_jiffy to %lu" + "for frequency %u kHz\n", loops_per_jiffy, ci->new); } } #else -static inline void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci) { return; } +static inline void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci) +{ + return; +} #endif @@ -316,7 +328,8 @@ static int cpufreq_parse_governor (char *str_governor, unsigned int *policy, if (!strnicmp(str_governor, "performance", CPUFREQ_NAME_LEN)) { *policy = CPUFREQ_POLICY_PERFORMANCE; err = 0; - } else if (!strnicmp(str_governor, "powersave", CPUFREQ_NAME_LEN)) { + } else if (!strnicmp(str_governor, "powersave", + CPUFREQ_NAME_LEN)) { *policy = CPUFREQ_POLICY_POWERSAVE; err = 0; } @@ -328,7 +341,8 @@ static int cpufreq_parse_governor (char *str_governor, unsigned int *policy, t = __find_governor(str_governor); if (t == NULL) { - char *name = kasprintf(GFP_KERNEL, "cpufreq_%s", str_governor); + char *name = kasprintf(GFP_KERNEL, "cpufreq_%s", + str_governor); if (name) { int ret; @@ -361,7 +375,8 @@ extern struct sysdev_class cpu_sysdev_class; /** - * cpufreq_per_cpu_attr_read() / show_##file_name() - print out cpufreq information + * cpufreq_per_cpu_attr_read() / show_##file_name() - + * print out cpufreq information * * Write out information from cpufreq_driver->policy[cpu]; object must be * "unsigned int". @@ -380,7 +395,8 @@ show_one(scaling_min_freq, min); show_one(scaling_max_freq, max); show_one(scaling_cur_freq, cur); -static int __cpufreq_set_policy(struct cpufreq_policy *data, struct cpufreq_policy *policy); +static int __cpufreq_set_policy(struct cpufreq_policy *data, + struct cpufreq_policy *policy); /** * cpufreq_per_cpu_attr_write() / store_##file_name() - sysfs write access @@ -416,7 +432,8 @@ store_one(scaling_max_freq,max); /** * show_cpuinfo_cur_freq - current CPU frequency as detected by hardware */ -static ssize_t show_cpuinfo_cur_freq (struct cpufreq_policy * policy, char *buf) +static ssize_t show_cpuinfo_cur_freq (struct cpufreq_policy * policy, + char *buf) { unsigned int cur_freq = cpufreq_get(policy->cpu); if (!cur_freq) @@ -428,7 +445,8 @@ static ssize_t show_cpuinfo_cur_freq (struct cpufreq_policy * policy, char *buf) /** * show_scaling_governor - show the current policy for the specified CPU */ -static ssize_t show_scaling_governor (struct cpufreq_policy * policy, char *buf) +static ssize_t show_scaling_governor (struct cpufreq_policy * policy, + char *buf) { if(policy->policy == CPUFREQ_POLICY_POWERSAVE) return sprintf(buf, "powersave\n"); @@ -458,7 +476,8 @@ static ssize_t store_scaling_governor (struct cpufreq_policy * policy, if (ret != 1) return -EINVAL; - if (cpufreq_parse_governor(str_governor, &new_policy.policy, &new_policy.governor)) + if (cpufreq_parse_governor(str_governor, &new_policy.policy, + &new_policy.governor)) return -EINVAL; lock_cpu_hotplug(); @@ -474,7 +493,10 @@ static ssize_t store_scaling_governor (struct cpufreq_policy * policy, unlock_cpu_hotplug(); - return ret ? ret : count; + if (ret) + return ret; + else + return count; } /** @@ -488,7 +510,7 @@ static ssize_t show_scaling_driver (struct cpufreq_policy * policy, char *buf) /** * show_scaling_available_governors - show the available CPUfreq governors */ -static ssize_t show_scaling_available_governors (struct cpufreq_policy * policy, +static ssize_t show_scaling_available_governors (struct cpufreq_policy *policy, char *buf) { ssize_t i = 0; @@ -574,7 +596,11 @@ static ssize_t show(struct kobject * kobj, struct attribute * attr ,char * buf) policy = cpufreq_cpu_get(policy->cpu); if (!policy) return -EINVAL; - ret = fattr->show ? fattr->show(policy,buf) : -EIO; + if (fattr->show) + ret = fattr->show(policy, buf); + else + ret = -EIO; + cpufreq_cpu_put(policy); return ret; } @@ -588,7 +614,11 @@ static ssize_t store(struct kobject * kobj, struct attribute * attr, policy = cpufreq_cpu_get(policy->cpu); if (!policy) return -EINVAL; - ret = fattr->store ? fattr->store(policy,buf,count) : -EIO; + if (fattr->store) + ret = fattr->store(policy, buf, count); + else + ret = -EIO; + cpufreq_cpu_put(policy); return ret; } @@ -911,7 +941,8 @@ static void handle_update(void *data) * We adjust to current frequency first, and need to clean up later. So either call * to cpufreq_update_policy() or schedule handle_update()). */ -static void cpufreq_out_of_sync(unsigned int cpu, unsigned int old_freq, unsigned int new_freq) +static void cpufreq_out_of_sync(unsigned int cpu, unsigned int old_freq, + unsigned int new_freq) { struct cpufreq_freqs freqs; @@ -936,16 +967,16 @@ static void cpufreq_out_of_sync(unsigned int cpu, unsigned int old_freq, unsigne unsigned int cpufreq_quick_get(unsigned int cpu) { struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); - unsigned int ret = 0; + unsigned int ret_freq = 0; if (policy) { mutex_lock(&policy->lock); - ret = policy->cur; + ret_freq = policy->cur; mutex_unlock(&policy->lock); cpufreq_cpu_put(policy); } - return (ret); + return (ret_freq); } EXPORT_SYMBOL(cpufreq_quick_get); @@ -959,7 +990,7 @@ EXPORT_SYMBOL(cpufreq_quick_get); unsigned int cpufreq_get(unsigned int cpu) { struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); - unsigned int ret = 0; + unsigned int ret_freq = 0; if (!policy) return 0; @@ -969,12 +1000,14 @@ unsigned int cpufreq_get(unsigned int cpu) mutex_lock(&policy->lock); - ret = cpufreq_driver->get(cpu); + ret_freq = cpufreq_driver->get(cpu); - if (ret && policy->cur && !(cpufreq_driver->flags & CPUFREQ_CONST_LOOPS)) { - /* verify no discrepancy between actual and saved value exists */ - if (unlikely(ret != policy->cur)) { - cpufreq_out_of_sync(cpu, policy->cur, ret); + if (ret_freq && policy->cur && + !(cpufreq_driver->flags & CPUFREQ_CONST_LOOPS)) { + /* verify no discrepancy between actual and + saved value exists */ + if (unlikely(ret_freq != policy->cur)) { + cpufreq_out_of_sync(cpu, policy->cur, ret_freq); schedule_work(&policy->update); } } @@ -984,7 +1017,7 @@ unsigned int cpufreq_get(unsigned int cpu) out: cpufreq_cpu_put(policy); - return (ret); + return (ret_freq); } EXPORT_SYMBOL(cpufreq_get); @@ -996,7 +1029,7 @@ EXPORT_SYMBOL(cpufreq_get); static int cpufreq_suspend(struct sys_device * sysdev, pm_message_t pmsg) { int cpu = sysdev->id; - unsigned int ret = 0; + int ret = 0; unsigned int cur_freq = 0; struct cpufreq_policy *cpu_policy; @@ -1078,7 +1111,7 @@ out: static int cpufreq_resume(struct sys_device * sysdev) { int cpu = sysdev->id; - unsigned int ret = 0; + int ret = 0; struct cpufreq_policy *cpu_policy; dprintk("resuming cpu %u\n", cpu); @@ -1299,17 +1332,20 @@ EXPORT_SYMBOL_GPL(cpufreq_driver_getavg); * when "event" is CPUFREQ_GOV_LIMITS */ -static int __cpufreq_governor(struct cpufreq_policy *policy, unsigned int event) +static int __cpufreq_governor(struct cpufreq_policy *policy, + unsigned int event) { int ret; if (!try_module_get(policy->governor->owner)) return -EINVAL; - dprintk("__cpufreq_governor for CPU %u, event %u\n", policy->cpu, event); + dprintk("__cpufreq_governor for CPU %u, event %u\n", + policy->cpu, event); ret = policy->governor->governor(policy, event); - /* we keep one module reference alive for each CPU governed by this CPU */ + /* we keep one module reference alive for + each CPU governed by this CPU */ if ((event != CPUFREQ_GOV_START) || ret) module_put(policy->governor->owner); if ((event == CPUFREQ_GOV_STOP) && !ret) @@ -1385,9 +1421,12 @@ EXPORT_SYMBOL(cpufreq_get_policy); /* + * data : current policy. + * policy : policy to be set. * Locking: Must be called with the lock_cpu_hotplug() lock held */ -static int __cpufreq_set_policy(struct cpufreq_policy *data, struct cpufreq_policy *policy) +static int __cpufreq_set_policy(struct cpufreq_policy *data, + struct cpufreq_policy *policy) { int ret = 0; @@ -1395,7 +1434,8 @@ static int __cpufreq_set_policy(struct cpufreq_policy *data, struct cpufreq_poli dprintk("setting new policy for CPU %u: %u - %u kHz\n", policy->cpu, policy->min, policy->max); - memcpy(&policy->cpuinfo, &data->cpuinfo, sizeof(struct cpufreq_cpuinfo)); + memcpy(&policy->cpuinfo, &data->cpuinfo, + sizeof(struct cpufreq_cpuinfo)); if (policy->min > data->min && policy->min > policy->max) { ret = -EINVAL; @@ -1428,7 +1468,8 @@ static int __cpufreq_set_policy(struct cpufreq_policy *data, struct cpufreq_poli data->min = policy->min; data->max = policy->max; - dprintk("new min and max freqs are %u - %u kHz\n", data->min, data->max); + dprintk("new min and max freqs are %u - %u kHz\n", + data->min, data->max); if (cpufreq_driver->setpolicy) { data->policy = policy->policy; @@ -1449,10 +1490,12 @@ static int __cpufreq_set_policy(struct cpufreq_policy *data, struct cpufreq_poli data->governor = policy->governor; if (__cpufreq_governor(data, CPUFREQ_GOV_START)) { /* new governor failed, so re-start old one */ - dprintk("starting governor %s failed\n", data->governor->name); + dprintk("starting governor %s failed\n", + data->governor->name); if (old_gov) { data->governor = old_gov; - __cpufreq_governor(data, CPUFREQ_GOV_START); + __cpufreq_governor(data, + CPUFREQ_GOV_START); } ret = -EINVAL; goto error_out; @@ -1542,7 +1585,8 @@ int cpufreq_update_policy(unsigned int cpu) data->cur = policy.cur; } else { if (data->cur != policy.cur) - cpufreq_out_of_sync(cpu, data->cur, policy.cur); + cpufreq_out_of_sync(cpu, data->cur, + policy.cur); } } @@ -1646,8 +1690,10 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data) /* if all ->init() calls failed, unregister */ if (ret) { - dprintk("no CPU initialized for driver %s\n", driver_data->name); - sysdev_driver_unregister(&cpu_sysdev_class, &cpufreq_sysdev_driver); + dprintk("no CPU initialized for driver %s\n", + driver_data->name); + sysdev_driver_unregister(&cpu_sysdev_class, + &cpufreq_sysdev_driver); spin_lock_irqsave(&cpufreq_driver_lock, flags); cpufreq_driver = NULL; diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c index 8fe13ec..29905b4 100644 --- a/drivers/cpufreq/cpufreq_conservative.c +++ b/drivers/cpufreq/cpufreq_conservative.c @@ -44,15 +44,17 @@ * latency of the processor. The governor will work on any processor with * transition latency <= 10mS, using appropriate sampling * rate. - * For CPUs with transition latency > 10mS (mostly drivers with CPUFREQ_ETERNAL) - * this governor will not work. + * For CPUs with transition latency > 10mS (mostly drivers + * with CPUFREQ_ETERNAL), this governor will not work. * All times here are in uS. */ static unsigned int def_sampling_rate; #define MIN_SAMPLING_RATE_RATIO (2) /* for correct statistics, we need at least 10 ticks between each measure */ -#define MIN_STAT_SAMPLING_RATE (MIN_SAMPLING_RATE_RATIO * jiffies_to_usecs(10)) -#define MIN_SAMPLING_RATE (def_sampling_rate / MIN_SAMPLING_RATE_RATIO) +#define MIN_STAT_SAMPLING_RATE \ + (MIN_SAMPLING_RATE_RATIO * jiffies_to_usecs(10)) +#define MIN_SAMPLING_RATE \ + (def_sampling_rate / MIN_SAMPLING_RATE_RATIO) #define MAX_SAMPLING_RATE (500 * def_sampling_rate) #define DEF_SAMPLING_RATE_LATENCY_MULTIPLIER (1000) #define DEF_SAMPLING_DOWN_FACTOR (1) @@ -103,11 +105,16 @@ static struct dbs_tuners dbs_tuners_ins = { static inline unsigned int get_cpu_idle_time(unsigned int cpu) { - return kstat_cpu(cpu).cpustat.idle + + unsigned int add_nice = 0, ret; + + if (dbs_tuners_ins.ignore_nice) + add_nice = kstat_cpu(cpu).cpustat.nice; + + ret = kstat_cpu(cpu).cpustat.idle + kstat_cpu(cpu).cpustat.iowait + - ( dbs_tuners_ins.ignore_nice ? - kstat_cpu(cpu).cpustat.nice : - 0); + add_nice; + + return ret; } /************************** sysfs interface ************************/ diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index cbde076..048ec8b 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -41,8 +41,10 @@ static unsigned int def_sampling_rate; #define MIN_SAMPLING_RATE_RATIO (2) /* for correct statistics, we need at least 10 ticks between each measure */ -#define MIN_STAT_SAMPLING_RATE (MIN_SAMPLING_RATE_RATIO * jiffies_to_usecs(10)) -#define MIN_SAMPLING_RATE (def_sampling_rate / MIN_SAMPLING_RATE_RATIO) +#define MIN_STAT_SAMPLING_RATE \ + (MIN_SAMPLING_RATE_RATIO * jiffies_to_usecs(10)) +#define MIN_SAMPLING_RATE \ + (def_sampling_rate / MIN_SAMPLING_RATE_RATIO) #define MAX_SAMPLING_RATE (500 * def_sampling_rate) #define DEF_SAMPLING_RATE_LATENCY_MULTIPLIER (1000) #define TRANSITION_LATENCY_LIMIT (10 * 1000) @@ -202,7 +204,8 @@ static ssize_t store_sampling_rate(struct cpufreq_policy *unused, ret = sscanf(buf, "%u", &input); mutex_lock(&dbs_mutex); - if (ret != 1 || input > MAX_SAMPLING_RATE || input < MIN_SAMPLING_RATE) { + if (ret != 1 || input > MAX_SAMPLING_RATE + || input < MIN_SAMPLING_RATE) { mutex_unlock(&dbs_mutex); return -EINVAL; } @@ -496,7 +499,8 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, if (dbs_enable == 1) { kondemand_wq = create_workqueue("kondemand"); if (!kondemand_wq) { - printk(KERN_ERR "Creation of kondemand failed\n"); + printk(KERN_ERR + "Creation of kondemand failed\n"); dbs_enable--; mutex_unlock(&dbs_mutex); return -ENOSPC; diff --git a/drivers/cpufreq/cpufreq_performance.c b/drivers/cpufreq/cpufreq_performance.c index de91e33..e8e1451 100644 --- a/drivers/cpufreq/cpufreq_performance.c +++ b/drivers/cpufreq/cpufreq_performance.c @@ -15,7 +15,8 @@ #include #include -#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_GOVERNOR, "performance", msg) +#define dprintk(msg...) \ + cpufreq_debug_printk(CPUFREQ_DEBUG_GOVERNOR, "performance", msg) static int cpufreq_governor_performance(struct cpufreq_policy *policy, @@ -24,8 +25,10 @@ static int cpufreq_governor_performance(struct cpufreq_policy *policy, switch (event) { case CPUFREQ_GOV_START: case CPUFREQ_GOV_LIMITS: - dprintk("setting to %u kHz because of event %u\n", policy->max, event); - __cpufreq_driver_target(policy, policy->max, CPUFREQ_RELATION_H); + dprintk("setting to %u kHz because of event %u\n", + policy->max, event); + __cpufreq_driver_target(policy, policy->max, + CPUFREQ_RELATION_H); break; default: break; diff --git a/drivers/cpufreq/cpufreq_powersave.c b/drivers/cpufreq/cpufreq_powersave.c index 0a25960..13fe06b 100644 --- a/drivers/cpufreq/cpufreq_powersave.c +++ b/drivers/cpufreq/cpufreq_powersave.c @@ -15,7 +15,8 @@ #include #include -#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_GOVERNOR, "powersave", msg) +#define dprintk(msg...) \ + cpufreq_debug_printk(CPUFREQ_DEBUG_GOVERNOR, "powersave", msg) static int cpufreq_governor_powersave(struct cpufreq_policy *policy, unsigned int event) @@ -23,8 +24,10 @@ static int cpufreq_governor_powersave(struct cpufreq_policy *policy, switch (event) { case CPUFREQ_GOV_START: case CPUFREQ_GOV_LIMITS: - dprintk("setting to %u kHz because of event %u\n", policy->min, event); - __cpufreq_driver_target(policy, policy->min, CPUFREQ_RELATION_L); + dprintk("setting to %u kHz because of event %u\n", + policy->min, event); + __cpufreq_driver_target(policy, policy->min, + CPUFREQ_RELATION_L); break; default: break; diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c index c2ecc59..6742b1a 100644 --- a/drivers/cpufreq/cpufreq_stats.c +++ b/drivers/cpufreq/cpufreq_stats.c @@ -351,8 +351,8 @@ __init cpufreq_stats_init(void) register_hotcpu_notifier(&cpufreq_stat_cpu_notifier); for_each_online_cpu(cpu) { - cpufreq_stat_cpu_callback(&cpufreq_stat_cpu_notifier, CPU_ONLINE, - (void *)(long)cpu); + cpufreq_stat_cpu_callback(&cpufreq_stat_cpu_notifier, + CPU_ONLINE, (void *)(long)cpu); } return 0; } @@ -368,14 +368,15 @@ __exit cpufreq_stats_exit(void) unregister_hotcpu_notifier(&cpufreq_stat_cpu_notifier); lock_cpu_hotplug(); for_each_online_cpu(cpu) { - cpufreq_stat_cpu_callback(&cpufreq_stat_cpu_notifier, CPU_DEAD, - (void *)(long)cpu); + cpufreq_stat_cpu_callback(&cpufreq_stat_cpu_notifier, + CPU_DEAD, (void *)(long)cpu); } unlock_cpu_hotplug(); } MODULE_AUTHOR ("Zou Nan hai "); -MODULE_DESCRIPTION ("'cpufreq_stats' - A driver to export cpufreq stats through sysfs filesystem"); +MODULE_DESCRIPTION ("'cpufreq_stats' - A driver to export cpufreq stats" + "through sysfs filesystem"); MODULE_LICENSE ("GPL"); module_init(cpufreq_stats_init); diff --git a/drivers/cpufreq/freq_table.c b/drivers/cpufreq/freq_table.c index 551f4cc..e749092 100644 --- a/drivers/cpufreq/freq_table.c +++ b/drivers/cpufreq/freq_table.c @@ -9,7 +9,8 @@ #include #include -#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_CORE, "freq-table", msg) +#define dprintk(msg...) \ + cpufreq_debug_printk(CPUFREQ_DEBUG_CORE, "freq-table", msg) /********************************************************************* * FREQUENCY TABLE HELPERS * @@ -29,7 +30,8 @@ int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy, continue; } - dprintk("table entry %u: %u kHz, %u index\n", i, freq, table[i].index); + dprintk("table entry %u: %u kHz, %u index\n", + i, freq, table[i].index); if (freq < min_freq) min_freq = freq; if (freq > max_freq) @@ -54,13 +56,14 @@ int cpufreq_frequency_table_verify(struct cpufreq_policy *policy, unsigned int i; unsigned int count = 0; - dprintk("request for verification of policy (%u - %u kHz) for cpu %u\n", policy->min, policy->max, policy->cpu); + dprintk("request for verification of policy (%u - %u kHz) for cpu %u\n", + policy->min, policy->max, policy->cpu); if (!cpu_online(policy->cpu)) return -EINVAL; - cpufreq_verify_within_limits(policy, - policy->cpuinfo.min_freq, policy->cpuinfo.max_freq); + cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, + policy->cpuinfo.max_freq); for (i=0; (table[i].frequency != CPUFREQ_TABLE_END); i++) { unsigned int freq = table[i].frequency; @@ -75,10 +78,11 @@ int cpufreq_frequency_table_verify(struct cpufreq_policy *policy, if (!count) policy->max = next_larger; - cpufreq_verify_within_limits(policy, - policy->cpuinfo.min_freq, policy->cpuinfo.max_freq); + cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, + policy->cpuinfo.max_freq); - dprintk("verification lead to (%u - %u kHz) for cpu %u\n", policy->min, policy->max, policy->cpu); + dprintk("verification lead to (%u - %u kHz) for cpu %u\n", + policy->min, policy->max, policy->cpu); return 0; } @@ -101,7 +105,8 @@ int cpufreq_frequency_table_target(struct cpufreq_policy *policy, }; unsigned int i; - dprintk("request for target %u kHz (relation: %u) for cpu %u\n", target_freq, relation, policy->cpu); + dprintk("request for target %u kHz (relation: %u) for cpu %u\n", + target_freq, relation, policy->cpu); switch (relation) { case CPUFREQ_RELATION_H: @@ -192,7 +197,10 @@ static ssize_t show_available_freqs (struct cpufreq_policy *policy, char *buf) } struct freq_attr cpufreq_freq_attr_scaling_available_freqs = { - .attr = { .name = "scaling_available_frequencies", .mode = 0444, .owner=THIS_MODULE }, + .attr = { .name = "scaling_available_frequencies", + .mode = 0444, + .owner=THIS_MODULE + }, .show = show_available_freqs, }; EXPORT_SYMBOL_GPL(cpufreq_freq_attr_scaling_available_freqs); -- cgit v0.10.2 From 4e74663c5d7eefc1f953b9b0bdacab09917b4eac Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Tue, 31 Oct 2006 12:44:08 -0500 Subject: [CPUFREQ] p4-clockmod: add more CPUs Several more Intel CPUs are now capable using the p4-clockmod cpufreq driver. As it is of limited use most of the time, print a big bold warning if a better cpufreq driver might be available. Signed-off-by: Dominik Brodowski Signed-off-by: Dave Jones diff --git a/arch/i386/kernel/cpu/cpufreq/p4-clockmod.c b/arch/i386/kernel/cpu/cpufreq/p4-clockmod.c index 304d2ea..bec5017 100644 --- a/arch/i386/kernel/cpu/cpufreq/p4-clockmod.c +++ b/arch/i386/kernel/cpu/cpufreq/p4-clockmod.c @@ -163,29 +163,27 @@ static int cpufreq_p4_verify(struct cpufreq_policy *policy) static unsigned int cpufreq_p4_get_frequency(struct cpuinfo_x86 *c) { - if ((c->x86 == 0x06) && (c->x86_model == 0x09)) { - /* Pentium M (Banias) */ - printk(KERN_WARNING PFX "Warning: Pentium M detected. " - "The speedstep_centrino module offers voltage scaling" - " in addition of frequency scaling. You should use " - "that instead of p4-clockmod, if possible.\n"); - return speedstep_get_processor_frequency(SPEEDSTEP_PROCESSOR_PM); - } - - if ((c->x86 == 0x06) && (c->x86_model == 0x0D)) { - /* Pentium M (Dothan) */ - printk(KERN_WARNING PFX "Warning: Pentium M detected. " - "The speedstep_centrino module offers voltage scaling" - " in addition of frequency scaling. You should use " - "that instead of p4-clockmod, if possible.\n"); - /* on P-4s, the TSC runs with constant frequency independent whether - * throttling is active or not. */ - p4clockmod_driver.flags |= CPUFREQ_CONST_LOOPS; - return speedstep_get_processor_frequency(SPEEDSTEP_PROCESSOR_PM); + if (c->x86 == 0x06) { + if (cpu_has(c, X86_FEATURE_EST)) + printk(KERN_WARNING PFX "Warning: EST-capable CPU detected. " + "The acpi-cpufreq module offers voltage scaling" + " in addition of frequency scaling. You should use " + "that instead of p4-clockmod, if possible.\n"); + switch (c->x86_model) { + case 0x0E: /* Core */ + case 0x0F: /* Core Duo */ + p4clockmod_driver.flags |= CPUFREQ_CONST_LOOPS; + return speedstep_get_processor_frequency(SPEEDSTEP_PROCESSOR_PCORE); + case 0x0D: /* Pentium M (Dothan) */ + p4clockmod_driver.flags |= CPUFREQ_CONST_LOOPS; + /* fall through */ + case 0x09: /* Pentium M (Banias) */ + return speedstep_get_processor_frequency(SPEEDSTEP_PROCESSOR_PM); + } } if (c->x86 != 0xF) { - printk(KERN_WARNING PFX "Unknown p4-clockmod-capable CPU. Please send an e-mail to \n"); + printk(KERN_WARNING PFX "Unknown p4-clockmod-capable CPU. Please send an e-mail to \n"); return 0; } diff --git a/arch/i386/kernel/cpu/cpufreq/speedstep-lib.c b/arch/i386/kernel/cpu/cpufreq/speedstep-lib.c index 4f46cac..6623a56 100644 --- a/arch/i386/kernel/cpu/cpufreq/speedstep-lib.c +++ b/arch/i386/kernel/cpu/cpufreq/speedstep-lib.c @@ -123,6 +123,36 @@ static unsigned int pentiumM_get_frequency(void) return (msr_tmp * 100 * 1000); } +static unsigned int pentium_core_get_frequency(void) +{ + u32 fsb = 0; + u32 msr_lo, msr_tmp; + + rdmsr(MSR_FSB_FREQ, msr_lo, msr_tmp); + /* see table B-2 of 24547212.pdf */ + switch (msr_lo & 0x07) { + case 5: + fsb = 400; + break; + case 1: + fsb = 533; + break; + case 3: + fsb = 667; + break; + default: + printk(KERN_ERR "PCORE - MSR_FSB_FREQ undefined value"); + } + + rdmsr(MSR_IA32_EBL_CR_POWERON, msr_lo, msr_tmp); + dprintk("PCORE - MSR_IA32_EBL_CR_POWERON: 0x%x 0x%x\n", msr_lo, msr_tmp); + + msr_tmp = (msr_lo >> 22) & 0x1f; + dprintk("bits 22-26 are 0x%x, speed is %u\n", msr_tmp, (msr_tmp * fsb * 1000)); + + return (msr_tmp * fsb * 1000); +} + static unsigned int pentium4_get_frequency(void) { @@ -174,6 +204,8 @@ static unsigned int pentium4_get_frequency(void) unsigned int speedstep_get_processor_frequency(unsigned int processor) { switch (processor) { + case SPEEDSTEP_PROCESSOR_PCORE: + return pentium_core_get_frequency(); case SPEEDSTEP_PROCESSOR_PM: return pentiumM_get_frequency(); case SPEEDSTEP_PROCESSOR_P4D: diff --git a/arch/i386/kernel/cpu/cpufreq/speedstep-lib.h b/arch/i386/kernel/cpu/cpufreq/speedstep-lib.h index b735429..b11bcc6 100644 --- a/arch/i386/kernel/cpu/cpufreq/speedstep-lib.h +++ b/arch/i386/kernel/cpu/cpufreq/speedstep-lib.h @@ -22,6 +22,7 @@ * the speedstep_get_processor_frequency() call. */ #define SPEEDSTEP_PROCESSOR_PM 0xFFFFFF03 /* Pentium M */ #define SPEEDSTEP_PROCESSOR_P4D 0xFFFFFF04 /* desktop P4 */ +#define SPEEDSTEP_PROCESSOR_PCORE 0xFFFFFF05 /* Core */ /* speedstep states -- only two of them */ diff --git a/include/asm-i386/msr.h b/include/asm-i386/msr.h index 0aa15fc..8c31887 100644 --- a/include/asm-i386/msr.h +++ b/include/asm-i386/msr.h @@ -95,6 +95,8 @@ static inline void wrmsrl (unsigned long msr, unsigned long long val) #define MSR_P6_PERFCTR0 0xc1 #define MSR_P6_PERFCTR1 0xc2 +#define MSR_FSB_FREQ 0xcd + #define MSR_IA32_BBL_CR_CTL 0x119 -- cgit v0.10.2 From d7a1944e8da5e91859b98259189aaaa4d8b7fa07 Mon Sep 17 00:00:00 2001 From: Gary Hade Date: Mon, 6 Nov 2006 15:39:23 -0800 Subject: [CPUFREQ] speedstep-centrino should ignore upper performance control bits On some systems such as the IBM x3650 there are bits set in the upper half of the control values provided by the _PSS object. These bits are only relevant for cpufreq drivers that use IO ports which are not currently supported by the speedstep-centrino driver. The current MSR oriented code assumes that upper bits are not set and thus fails to work correctly when they are. e.g. the control and status value equality check fails even though the ACPI spec allows the inequality. Signed-off-by: Gary Hade Signed-off-by: Dave Jones diff --git a/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c b/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c index d2d9caf..e3fa03a 100644 --- a/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c +++ b/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c @@ -463,6 +463,10 @@ static int centrino_cpu_init_acpi(struct cpufreq_policy *policy) } for (i=0; istate_count; i++) { + /* clear high bits (set by some BIOSes) that are non-relevant and + problematic for this driver's MSR only frequency transition code */ + p->states[i].control &= 0xffff; + if (p->states[i].control != p->states[i].status) { dprintk("Different control (%llu) and status values (%llu)\n", p->states[i].control, p->states[i].status); -- cgit v0.10.2 From caede347c3578b9dca49f53eab781fcaaacd3234 Mon Sep 17 00:00:00 2001 From: "akpm@osdl.org" Date: Wed, 8 Nov 2006 01:09:25 -0800 Subject: [CPUFREQ] Fix build failure on x86-64 arch/x86_64/kernel/cpufreq/../../../i386/kernel/cpu/cpufreq/speedstep-lib.c:131: error: 'MSR_FSB_FREQ' undeclared (first use in this function) Signed-off-by: Andrew Morton Signed-off-by: Dave Jones diff --git a/arch/i386/kernel/cpu/cpufreq/speedstep-lib.c b/arch/i386/kernel/cpu/cpufreq/speedstep-lib.c index 6623a56..a709f6d 100644 --- a/arch/i386/kernel/cpu/cpufreq/speedstep-lib.c +++ b/arch/i386/kernel/cpu/cpufreq/speedstep-lib.c @@ -123,6 +123,7 @@ static unsigned int pentiumM_get_frequency(void) return (msr_tmp * 100 * 1000); } +#ifdef CONFIG_X86_32 static unsigned int pentium_core_get_frequency(void) { u32 fsb = 0; @@ -152,7 +153,7 @@ static unsigned int pentium_core_get_frequency(void) return (msr_tmp * fsb * 1000); } - +#endif static unsigned int pentium4_get_frequency(void) { @@ -204,8 +205,10 @@ static unsigned int pentium4_get_frequency(void) unsigned int speedstep_get_processor_frequency(unsigned int processor) { switch (processor) { +#ifdef CONFIG_X86_32 case SPEEDSTEP_PROCESSOR_PCORE: return pentium_core_get_frequency(); +#endif case SPEEDSTEP_PROCESSOR_PM: return pentiumM_get_frequency(); case SPEEDSTEP_PROCESSOR_P4D: -- cgit v0.10.2 From 6fdc2d07501f1fff1637f3c8efdc860e7fd28eb0 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Thu, 9 Nov 2006 01:04:35 +0300 Subject: [CPUFREQ] gx-suspmod: fix "&& 0xff" typo Signed-off-by: Alexey Dobriyan Signed-off-by: Dave Jones diff --git a/arch/i386/kernel/cpu/cpufreq/gx-suspmod.c b/arch/i386/kernel/cpu/cpufreq/gx-suspmod.c index 92afa3b..0b9cc8a 100644 --- a/arch/i386/kernel/cpu/cpufreq/gx-suspmod.c +++ b/arch/i386/kernel/cpu/cpufreq/gx-suspmod.c @@ -473,7 +473,7 @@ static int __init cpufreq_gx_init(void) pci_read_config_byte(params->cs55x0, PCI_MODON, &(params->on_duration)); pci_read_config_byte(params->cs55x0, PCI_MODOFF, &(params->off_duration)); pci_read_config_dword(params->cs55x0, PCI_CLASS_REVISION, &class_rev); - params->pci_rev = class_rev && 0xff; + params->pci_rev = class_rev & 0xff; if ((ret = cpufreq_register_driver(&gx_suspmod_driver))) { kfree(params); -- cgit v0.10.2 From 474a14df3fce60bebde64d25c6adbdab7d30594a Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Wed, 8 Nov 2006 19:22:45 -0500 Subject: Revert "[CPUFREQ] speedstep-centrino should ignore upper performance control bits" This reverts commit d7a1944e8da5e91859b98259189aaaa4d8b7fa07. diff --git a/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c b/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c index e3fa03a..d2d9caf 100644 --- a/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c +++ b/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c @@ -463,10 +463,6 @@ static int centrino_cpu_init_acpi(struct cpufreq_policy *policy) } for (i=0; istate_count; i++) { - /* clear high bits (set by some BIOSes) that are non-relevant and - problematic for this driver's MSR only frequency transition code */ - p->states[i].control &= 0xffff; - if (p->states[i].control != p->states[i].status) { dprintk("Different control (%llu) and status values (%llu)\n", p->states[i].control, p->states[i].status); -- cgit v0.10.2 From c0968f0ea21d10b6720246e1e96bd6a7a161964d Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 9 Nov 2006 00:40:13 -0500 Subject: ACPI: button: register with input layer In addition to signalling button/lid events through /proc/acpi/event, create separate input devices and report KEY_POWER, KEY_SLEEP and SW_LID through input layer. Also remove unnecessary casts and variable initializations, clean up formatting. Sleep button may autorepeat but userspace will have to filter duplicate sleep requests anyway (and discard unprocessed events right after wakeup). Unlike /proc/acpi/event interface input device corresponding to LID switch reports true lid state instead of just a counter. SW_LID is active when lid is closed. The driver now depends on CONFIG_INPUT. Signed-off-by: Dmitry Torokhov Signed-off-by: Len Brown diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 0f9d4be..0ed8012 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -97,6 +97,7 @@ config ACPI_BATTERY config ACPI_BUTTON tristate "Button" + depends on INPUT default y help This driver handles events on the power, sleep and lid buttons. diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index 5ef885e..ac86058 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -62,7 +63,7 @@ #define _COMPONENT ACPI_BUTTON_COMPONENT ACPI_MODULE_NAME("acpi_button") - MODULE_AUTHOR("Paul Diefenbaugh"); +MODULE_AUTHOR("Paul Diefenbaugh"); MODULE_DESCRIPTION(ACPI_BUTTON_DRIVER_NAME); MODULE_LICENSE("GPL"); @@ -78,12 +79,14 @@ static struct acpi_driver acpi_button_driver = { .ops = { .add = acpi_button_add, .remove = acpi_button_remove, - }, + }, }; struct acpi_button { struct acpi_device *device; /* Fixed button kludge */ - u8 type; + unsigned int type; + struct input_dev *input; + char phys[32]; /* for input device */ unsigned long pushed; }; @@ -109,8 +112,7 @@ static struct proc_dir_entry *acpi_button_dir; static int acpi_button_info_seq_show(struct seq_file *seq, void *offset) { - struct acpi_button *button = (struct acpi_button *)seq->private; - + struct acpi_button *button = seq->private; if (!button || !button->device) return 0; @@ -128,22 +130,17 @@ static int acpi_button_info_open_fs(struct inode *inode, struct file *file) static int acpi_button_state_seq_show(struct seq_file *seq, void *offset) { - struct acpi_button *button = (struct acpi_button *)seq->private; + struct acpi_button *button = seq->private; acpi_status status; unsigned long state; - if (!button || !button->device) return 0; status = acpi_evaluate_integer(button->device->handle, "_LID", NULL, &state); - if (ACPI_FAILURE(status)) { - seq_printf(seq, "state: unsupported\n"); - } else { - seq_printf(seq, "state: %s\n", - (state ? "open" : "closed")); - } - + seq_printf(seq, "state: %s\n", + ACPI_FAILURE(status) ? "unsupported" : + (state ? "open" : "closed")); return 0; } @@ -159,8 +156,7 @@ static struct proc_dir_entry *acpi_lid_dir; static int acpi_button_add_fs(struct acpi_device *device) { struct proc_dir_entry *entry = NULL; - struct acpi_button *button = NULL; - + struct acpi_button *button; if (!device || !acpi_driver_data(device)) return -EINVAL; @@ -228,10 +224,8 @@ static int acpi_button_add_fs(struct acpi_device *device) static int acpi_button_remove_fs(struct acpi_device *device) { - struct acpi_button *button = NULL; - + struct acpi_button *button = acpi_driver_data(device); - button = acpi_driver_data(device); if (acpi_device_dir(device)) { if (button->type == ACPI_BUTTON_TYPE_LID) remove_proc_entry(ACPI_BUTTON_FILE_STATE, @@ -253,14 +247,34 @@ static int acpi_button_remove_fs(struct acpi_device *device) static void acpi_button_notify(acpi_handle handle, u32 event, void *data) { - struct acpi_button *button = (struct acpi_button *)data; - + struct acpi_button *button = data; + struct input_dev *input; if (!button || !button->device) return; switch (event) { case ACPI_BUTTON_NOTIFY_STATUS: + input = button->input; + + if (button->type == ACPI_BUTTON_TYPE_LID) { + struct acpi_handle *handle = button->device->handle; + unsigned long state; + + if (!ACPI_FAILURE(acpi_evaluate_integer(handle, "_LID", + NULL, &state))) + input_report_switch(input, SW_LID, !state); + + } else { + int keycode = test_bit(KEY_SLEEP, input->keybit) ? + KEY_SLEEP : KEY_POWER; + + input_report_key(input, keycode, 1); + input_sync(input); + input_report_key(input, keycode, 0); + } + input_sync(input); + acpi_bus_generate_event(button->device, event, ++button->pushed); break; @@ -275,8 +289,7 @@ static void acpi_button_notify(acpi_handle handle, u32 event, void *data) static acpi_status acpi_button_notify_fixed(void *data) { - struct acpi_button *button = (struct acpi_button *)data; - + struct acpi_button *button = data; if (!button) return AE_BAD_PARAMETER; @@ -286,24 +299,75 @@ static acpi_status acpi_button_notify_fixed(void *data) return AE_OK; } -static int acpi_button_add(struct acpi_device *device) +static int acpi_button_install_notify_handlers(struct acpi_button *button) { - int result = 0; - acpi_status status = AE_OK; - struct acpi_button *button = NULL; + acpi_status status; + switch (button->type) { + case ACPI_BUTTON_TYPE_POWERF: + status = + acpi_install_fixed_event_handler(ACPI_EVENT_POWER_BUTTON, + acpi_button_notify_fixed, + button); + break; + case ACPI_BUTTON_TYPE_SLEEPF: + status = + acpi_install_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON, + acpi_button_notify_fixed, + button); + break; + default: + status = acpi_install_notify_handler(button->device->handle, + ACPI_DEVICE_NOTIFY, + acpi_button_notify, + button); + break; + } + + return ACPI_FAILURE(status) ? -ENODEV : 0; +} + +static void acpi_button_remove_notify_handlers(struct acpi_button *button) +{ + switch (button->type) { + case ACPI_BUTTON_TYPE_POWERF: + acpi_remove_fixed_event_handler(ACPI_EVENT_POWER_BUTTON, + acpi_button_notify_fixed); + break; + case ACPI_BUTTON_TYPE_SLEEPF: + acpi_remove_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON, + acpi_button_notify_fixed); + break; + default: + acpi_remove_notify_handler(button->device->handle, + ACPI_DEVICE_NOTIFY, + acpi_button_notify); + break; + } +} + +static int acpi_button_add(struct acpi_device *device) +{ + int error; + struct acpi_button *button; + struct input_dev *input; if (!device) return -EINVAL; - button = kmalloc(sizeof(struct acpi_button), GFP_KERNEL); + button = kzalloc(sizeof(struct acpi_button), GFP_KERNEL); if (!button) return -ENOMEM; - memset(button, 0, sizeof(struct acpi_button)); button->device = device; acpi_driver_data(device) = button; + button->input = input = input_allocate_device(); + if (!input) { + error = -ENOMEM; + goto err_free_button; + } + /* * Determine the button type (via hid), as fixed-feature buttons * need to be handled a bit differently than generic-space. @@ -338,39 +402,48 @@ static int acpi_button_add(struct acpi_device *device) } else { printk(KERN_ERR PREFIX "Unsupported hid [%s]\n", acpi_device_hid(device)); - result = -ENODEV; - goto end; + error = -ENODEV; + goto err_free_input; } - result = acpi_button_add_fs(device); - if (result) - goto end; + error = acpi_button_add_fs(device); + if (error) + goto err_free_input; + + error = acpi_button_install_notify_handlers(button); + if (error) + goto err_remove_fs; + + snprintf(button->phys, sizeof(button->phys), + "%s/button/input0", acpi_device_hid(device)); + + input->name = acpi_device_name(device); + input->phys = button->phys; + input->id.bustype = BUS_HOST; + input->id.product = button->type; switch (button->type) { + case ACPI_BUTTON_TYPE_POWER: case ACPI_BUTTON_TYPE_POWERF: - status = - acpi_install_fixed_event_handler(ACPI_EVENT_POWER_BUTTON, - acpi_button_notify_fixed, - button); + input->evbit[0] = BIT(EV_KEY); + set_bit(KEY_POWER, input->keybit); break; + + case ACPI_BUTTON_TYPE_SLEEP: case ACPI_BUTTON_TYPE_SLEEPF: - status = - acpi_install_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON, - acpi_button_notify_fixed, - button); + input->evbit[0] = BIT(EV_KEY); + set_bit(KEY_SLEEP, input->keybit); break; - default: - status = acpi_install_notify_handler(device->handle, - ACPI_DEVICE_NOTIFY, - acpi_button_notify, - button); + + case ACPI_BUTTON_TYPE_LID: + input->evbit[0] = BIT(EV_SW); + set_bit(SW_LID, input->swbit); break; } - if (ACPI_FAILURE(status)) { - result = -ENODEV; - goto end; - } + error = input_register_device(input); + if (error) + goto err_remove_handlers; if (device->wakeup.flags.valid) { /* Button's GPE is run-wake GPE */ @@ -385,47 +458,31 @@ static int acpi_button_add(struct acpi_device *device) printk(KERN_INFO PREFIX "%s [%s]\n", acpi_device_name(device), acpi_device_bid(device)); - end: - if (result) { - acpi_button_remove_fs(device); - kfree(button); - } + return 0; - return result; + err_remove_handlers: + acpi_button_remove_notify_handlers(button); + err_remove_fs: + acpi_button_remove_fs(device); + err_free_input: + input_free_device(input); + err_free_button: + kfree(button); + return error; } static int acpi_button_remove(struct acpi_device *device, int type) { - acpi_status status = 0; - struct acpi_button *button = NULL; - + struct acpi_button *button; if (!device || !acpi_driver_data(device)) return -EINVAL; button = acpi_driver_data(device); - /* Unregister for device notifications. */ - switch (button->type) { - case ACPI_BUTTON_TYPE_POWERF: - status = - acpi_remove_fixed_event_handler(ACPI_EVENT_POWER_BUTTON, - acpi_button_notify_fixed); - break; - case ACPI_BUTTON_TYPE_SLEEPF: - status = - acpi_remove_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON, - acpi_button_notify_fixed); - break; - default: - status = acpi_remove_notify_handler(device->handle, - ACPI_DEVICE_NOTIFY, - acpi_button_notify); - break; - } - + acpi_button_remove_notify_handlers(button); acpi_button_remove_fs(device); - + input_unregister_device(button->input); kfree(button); return 0; @@ -433,8 +490,7 @@ static int acpi_button_remove(struct acpi_device *device, int type) static int __init acpi_button_init(void) { - int result = 0; - + int result; acpi_button_dir = proc_mkdir(ACPI_BUTTON_CLASS, acpi_root_dir); if (!acpi_button_dir) @@ -451,7 +507,6 @@ static int __init acpi_button_init(void) static void __exit acpi_button_exit(void) { - acpi_bus_unregister_driver(&acpi_button_driver); if (acpi_power_dir) @@ -461,8 +516,6 @@ static void __exit acpi_button_exit(void) if (acpi_lid_dir) remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir); remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir); - - return; } module_init(acpi_button_init); -- cgit v0.10.2 From 7d63c6759188b9b35c789159f6e02cd02d49ec7d Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Wed, 8 Nov 2006 13:18:29 -0200 Subject: ACPI: ibm-acpi: new ibm-acpi maintainer I will be taking care of ibm-acpi maintenance for now on, with Borislav's blessing. Many thanks to Borislav Deianov for writing this driver and for the many years he took care of it: his efforts made our ThinkPads much nicer devices to run Linux on, and are very much appreciated. Signed-off-by: Henrique de Moraes Holschuh Cc: Borislav Deianov Signed-off-by: Len Brown diff --git a/MAINTAINERS b/MAINTAINERS index d708702..bd4d5df 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1369,6 +1369,15 @@ W: http://www.ia64-linux.org/ T: git kernel.org:/pub/scm/linux/kernel/git/aegl/linux-2.6.git S: Maintained +IBM ACPI EXTRAS DRIVER +P: Henrique de Moraes Holschuh +M: ibm-acpi@hmh.eng.br +L: ibm-acpi-devel@lists.sourceforge.net +W: http://ibm-acpi.sourceforge.net +W: http://thinkwiki.org/wiki/Ibm-acpi +T: git repo.or.cz/linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git +S: Maintained + SN-IA64 (Itanium) SUB-PLATFORM P: Jes Sorensen M: jes@sgi.com -- cgit v0.10.2 From 2c22120fbd017d78ad2b6825ba573db3ef539bca Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Thu, 16 Nov 2006 11:23:48 +0900 Subject: MTD: OneNAND: interrupt based wait support We can use the two methods to wait. 1. polling: read interrupt status register 2. interrupt: use kernel ineterrupt mechanism To use interrupt method, you first connect onenand interrupt pin to your platform and configure interrupt properly Signed-off-by: Kyungmin Park diff --git a/drivers/mtd/onenand/generic.c b/drivers/mtd/onenand/generic.c index af06a80..cdf80c6 100644 --- a/drivers/mtd/onenand/generic.c +++ b/drivers/mtd/onenand/generic.c @@ -63,6 +63,7 @@ static int __devinit generic_onenand_probe(struct device *dev) } info->onenand.mmcontrol = pdata->mmcontrol; + info->onenand.irq = platform_get_irq(pdev, 0); info->mtd.name = pdev->dev.bus_id; info->mtd.priv = &info->onenand; diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 8ed68b2..aea13a3 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -339,6 +340,111 @@ static int onenand_wait(struct mtd_info *mtd, int state) return 0; } +/* + * onenand_interrupt - [DEFAULT] onenand interrupt handler + * @param irq onenand interrupt number + * @param dev_id interrupt data + * + * complete the work + */ +static irqreturn_t onenand_interrupt(int irq, void *data) +{ + struct onenand_chip *this = (struct onenand_chip *) data; + + /* To handle shared interrupt */ + if (!this->complete.done) + complete(&this->complete); + + return IRQ_HANDLED; +} + +/* + * onenand_interrupt_wait - [DEFAULT] wait until the command is done + * @param mtd MTD device structure + * @param state state to select the max. timeout value + * + * Wait for command done. + */ +static int onenand_interrupt_wait(struct mtd_info *mtd, int state) +{ + struct onenand_chip *this = mtd->priv; + + /* To prevent soft lockup */ + touch_softlockup_watchdog(); + + wait_for_completion(&this->complete); + + return onenand_wait(mtd, state); +} + +/* + * onenand_try_interrupt_wait - [DEFAULT] try interrupt wait + * @param mtd MTD device structure + * @param state state to select the max. timeout value + * + * Try interrupt based wait (It is used one-time) + */ +static int onenand_try_interrupt_wait(struct mtd_info *mtd, int state) +{ + struct onenand_chip *this = mtd->priv; + unsigned long remain, timeout; + + /* We use interrupt wait first */ + this->wait = onenand_interrupt_wait; + + /* To prevent soft lockup */ + touch_softlockup_watchdog(); + + timeout = msecs_to_jiffies(100); + remain = wait_for_completion_timeout(&this->complete, timeout); + if (!remain) { + printk(KERN_INFO "OneNAND: There's no interrupt. " + "We use the normal wait\n"); + + /* Release the irq */ + free_irq(this->irq, this); + + this->wait = onenand_wait; + } + + return onenand_wait(mtd, state); +} + +/* + * onenand_setup_wait - [OneNAND Interface] setup onenand wait method + * @param mtd MTD device structure + * + * There's two method to wait onenand work + * 1. polling - read interrupt status register + * 2. interrupt - use the kernel interrupt method + */ +static void onenand_setup_wait(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + int syscfg; + + init_completion(&this->complete); + + if (this->irq <= 0) { + this->wait = onenand_wait; + return; + } + + if (request_irq(this->irq, &onenand_interrupt, + IRQF_SHARED, "onenand", this)) { + /* If we can't get irq, use the normal wait */ + this->wait = onenand_wait; + return; + } + + /* Enable interrupt */ + syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1); + syscfg |= ONENAND_SYS_CFG1_IOBE; + this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1); + + this->wait = onenand_try_interrupt_wait; +} + /** * onenand_bufferram_offset - [DEFAULT] BufferRAM offset * @param mtd MTD data structure @@ -1129,7 +1235,6 @@ static void onenand_sync(struct mtd_info *mtd) onenand_release_device(mtd); } - /** * onenand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad * @param mtd MTD device structure @@ -1846,7 +1951,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) if (!this->command) this->command = onenand_command; if (!this->wait) - this->wait = onenand_wait; + onenand_setup_wait(mtd); if (!this->read_bufferram) this->read_bufferram = onenand_read_bufferram; diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h index 6f045b5..df963f1 100644 --- a/include/linux/mtd/onenand.h +++ b/include/linux/mtd/onenand.h @@ -13,6 +13,7 @@ #define __LINUX_MTD_ONENAND_H #include +#include #include #include @@ -120,6 +121,9 @@ struct onenand_chip { int (*block_markbad)(struct mtd_info *mtd, loff_t ofs); int (*scan_bbt)(struct mtd_info *mtd); + struct completion complete; + int irq; + spinlock_t chip_lock; wait_queue_head_t wq; onenand_state_t state; -- cgit v0.10.2 From 08f782b60a633cbd926ef5e49de303a752390719 Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Thu, 16 Nov 2006 11:29:39 +0900 Subject: [MTD] OneNAND: lock support Now you can use mtd lock inferface on OneNAND The idea is from Nemakal, Vijaya, thanks Signed-off-by: Kyungmin Park diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index aea13a3..bef4f26 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -1301,32 +1301,38 @@ static int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs) } /** - * onenand_unlock - [MTD Interface] Unlock block(s) + * onenand_do_lock_cmd - [OneNAND Interface] Lock or unlock block(s) * @param mtd MTD device structure * @param ofs offset relative to mtd start - * @param len number of bytes to unlock + * @param len number of bytes to lock or unlock * - * Unlock one or more blocks + * Lock or unlock one or more blocks */ -static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) +static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int cmd) { struct onenand_chip *this = mtd->priv; int start, end, block, value, status; + int wp_status_mask; start = ofs >> this->erase_shift; end = len >> this->erase_shift; + if (cmd == ONENAND_CMD_LOCK) + wp_status_mask = ONENAND_WP_LS; + else + wp_status_mask = ONENAND_WP_US; + /* Continuous lock scheme */ if (this->options & ONENAND_HAS_CONT_LOCK) { /* Set start block address */ this->write_word(start, this->base + ONENAND_REG_START_BLOCK_ADDRESS); /* Set end block address */ this->write_word(start + end - 1, this->base + ONENAND_REG_END_BLOCK_ADDRESS); - /* Write unlock command */ - this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0); + /* Write lock command */ + this->command(mtd, cmd, 0, 0); /* There's no return value */ - this->wait(mtd, FL_UNLOCKING); + this->wait(mtd, FL_LOCKING); /* Sanity check */ while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS) @@ -1335,7 +1341,7 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) /* Check lock status */ status = this->read_word(this->base + ONENAND_REG_WP_STATUS); - if (!(status & ONENAND_WP_US)) + if (!(status & wp_status_mask)) printk(KERN_ERR "wp status = 0x%x\n", status); return 0; @@ -1351,11 +1357,11 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); /* Set start block address */ this->write_word(block, this->base + ONENAND_REG_START_BLOCK_ADDRESS); - /* Write unlock command */ - this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0); + /* Write lock command */ + this->command(mtd, cmd, 0, 0); /* There's no return value */ - this->wait(mtd, FL_UNLOCKING); + this->wait(mtd, FL_LOCKING); /* Sanity check */ while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS) @@ -1364,7 +1370,7 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) /* Check lock status */ status = this->read_word(this->base + ONENAND_REG_WP_STATUS); - if (!(status & ONENAND_WP_US)) + if (!(status & wp_status_mask)) printk(KERN_ERR "block = %d, wp status = 0x%x\n", block, status); } @@ -1372,6 +1378,33 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) } /** + * onenand_lock - [MTD Interface] Lock block(s) + * @param mtd MTD device structure + * @param ofs offset relative to mtd start + * @param len number of bytes to unlock + * + * Lock one or more blocks + */ +static int onenand_lock(struct mtd_info *mtd, loff_t ofs, size_t len) +{ + return onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_LOCK); +} + + +/** + * onenand_unlock - [MTD Interface] Unlock block(s) + * @param mtd MTD device structure + * @param ofs offset relative to mtd start + * @param len number of bytes to unlock + * + * Unlock one or more blocks + */ +static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) +{ + return onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK); +} + +/** * onenand_check_lock_status - [OneNAND Interface] Check lock status * @param this onenand chip data structure * @@ -1415,7 +1448,7 @@ static int onenand_unlock_all(struct mtd_info *mtd) this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0); /* There's no return value */ - this->wait(mtd, FL_UNLOCKING); + this->wait(mtd, FL_LOCKING); /* Sanity check */ while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS) @@ -1439,7 +1472,7 @@ static int onenand_unlock_all(struct mtd_info *mtd) return 0; } - mtd->unlock(mtd, 0x0, this->chipsize); + onenand_unlock(mtd, 0x0, this->chipsize); return 0; } @@ -2027,7 +2060,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) mtd->lock_user_prot_reg = onenand_lock_user_prot_reg; #endif mtd->sync = onenand_sync; - mtd->lock = NULL; + mtd->lock = onenand_lock; mtd->unlock = onenand_unlock; mtd->suspend = onenand_suspend; mtd->resume = onenand_resume; diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h index df963f1..62ca0f4 100644 --- a/include/linux/mtd/onenand.h +++ b/include/linux/mtd/onenand.h @@ -34,7 +34,6 @@ typedef enum { FL_WRITING, FL_ERASING, FL_SYNCING, - FL_UNLOCKING, FL_LOCKING, FL_RESETING, FL_OTPING, -- cgit v0.10.2 From f4f91ac3c833abbd7181ff2122c6b48a653b4e55 Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Thu, 16 Nov 2006 12:03:56 +0900 Subject: [MTD] OneNAND: Single bit error detection Idea from Jarkko Lavinen Signed-off-by: Jarkko Lavinen Signed-off-by: Kyungmin Park diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index bef4f26..fc84ddc 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -331,9 +331,12 @@ static int onenand_wait(struct mtd_info *mtd, int state) if (interrupt & ONENAND_INT_READ) { ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); - if (ecc & ONENAND_ECC_2BIT_ALL) { + if (ecc) { DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: ECC error = 0x%04x\n", ecc); - return -EBADMSG; + if (ecc & ONENAND_ECC_2BIT_ALL) + mtd->ecc_stats.failed++; + else if (ecc & ONENAND_ECC_1BIT_ALL) + mtd->ecc_stats.corrected++; } } @@ -715,6 +718,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { struct onenand_chip *this = mtd->priv; + struct mtd_ecc_stats stats; int read = 0, column; int thislen; int ret = 0; @@ -733,6 +737,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, /* TODO handling oob */ + stats = mtd->ecc_stats; while (read < len) { thislen = min_t(int, mtd->writesize, len - read); @@ -774,7 +779,11 @@ out: * retlen == desired len and result == -EBADMSG */ *retlen = read; - return ret; + + if (mtd->ecc_stats.failed - stats.failed) + return -EBADMSG; + + return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; } /** @@ -1390,7 +1399,6 @@ static int onenand_lock(struct mtd_info *mtd, loff_t ofs, size_t len) return onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_LOCK); } - /** * onenand_unlock - [MTD Interface] Unlock block(s) * @param mtd MTD device structure @@ -1900,7 +1908,7 @@ static int onenand_probe(struct mtd_info *mtd) /* Read manufacturer and device IDs from Register */ maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID); dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID); - ver_id= this->read_word(this->base + ONENAND_REG_VERSION_ID); + ver_id = this->read_word(this->base + ONENAND_REG_VERSION_ID); /* Check OneNAND device */ if (maf_id != bram_maf_id || dev_id != bram_dev_id) diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c index 1b00dac..c8067b8 100644 --- a/drivers/mtd/onenand/onenand_bbt.c +++ b/drivers/mtd/onenand/onenand_bbt.c @@ -100,6 +100,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr bbm->bbt[i >> 3] |= 0x03 << (i & 0x6); printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n", i >> 1, (unsigned int) from); + mtd->ecc_stats.badblocks++; break; } } diff --git a/include/linux/mtd/onenand_regs.h b/include/linux/mtd/onenand_regs.h index 9e409fe..e31c8f5 100644 --- a/include/linux/mtd/onenand_regs.h +++ b/include/linux/mtd/onenand_regs.h @@ -179,6 +179,7 @@ * ECC Status Reigser FF00h (R) */ #define ONENAND_ECC_1BIT (1 << 0) +#define ONENAND_ECC_1BIT_ALL (0x5555) #define ONENAND_ECC_2BIT (1 << 1) #define ONENAND_ECC_2BIT_ALL (0xAAAA) -- cgit v0.10.2 From 1605cd3d9c5001790c2e36979cf1eff1f222fbc5 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Wed, 22 Nov 2006 05:38:11 +0100 Subject: [MTD] [NAND] rtc_from4.c: use lib/bitrev.c This patch converts drivers/mtd/nand/rtc_from4.c to use the new lib/bitrev.c Signed-off-by: Adrian Bunk Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index b4b1656..c600868 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -90,6 +90,7 @@ config MTD_NAND_RTC_FROM4 depends on MTD_NAND && SH_SOLUTION_ENGINE select REED_SOLOMON select REED_SOLOMON_DEC8 + select BITREVERSE help This enables the driver for the Renesas Technology AG-AND flash interface board (FROM_BOARD4) diff --git a/drivers/mtd/nand/rtc_from4.c b/drivers/mtd/nand/rtc_from4.c index f8c4964..4a83a7d 100644 --- a/drivers/mtd/nand/rtc_from4.c +++ b/drivers/mtd/nand/rtc_from4.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -152,47 +153,6 @@ static struct nand_ecclayout rtc_from4_nand_oobinfo = { .oobfree = {{32, 32}} }; -/* Aargh. I missed the reversed bit order, when I - * was talking to Renesas about the FPGA. - * - * The table is used for bit reordering and inversion - * of the ecc byte which we get from the FPGA - */ -static uint8_t revbits[256] = { - 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, - 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, - 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, - 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, - 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, - 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, - 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, - 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, - 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, - 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, - 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, - 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, - 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, - 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, - 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, - 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, - 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, - 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, - 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, - 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, - 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, - 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, - 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, - 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, - 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, - 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, - 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, - 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, - 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, - 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, - 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, - 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff, -}; - #endif /* @@ -397,7 +357,7 @@ static int rtc_from4_correct_data(struct mtd_info *mtd, const u_char *buf, u_cha /* Read the syndrom pattern from the FPGA and correct the bitorder */ rs_ecc = (volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECC); for (i = 0; i < 8; i++) { - ecc[i] = revbits[(*rs_ecc) & 0xFF]; + ecc[i] = byte_rev_table[(*rs_ecc) & 0xFF]; rs_ecc++; } -- cgit v0.10.2 From ddacff1f20fc5c96cc73e2975258e05d298c97cc Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Sat, 25 Nov 2006 20:15:41 +0100 Subject: [MTD] make drivers/mtd/cmdlinepart.c:mtdpart_setup() static This patch makes the needlessly global mtdpart_setup() static. Signed-off-by: Adrian Bunk Signed-off-by: David Woodhouse diff --git a/drivers/mtd/cmdlinepart.c b/drivers/mtd/cmdlinepart.c index a7a7bfe..3402ce4 100644 --- a/drivers/mtd/cmdlinepart.c +++ b/drivers/mtd/cmdlinepart.c @@ -346,7 +346,7 @@ static int parse_cmdline_partitions(struct mtd_info *master, * * This function needs to be visible for bootloaders. */ -int mtdpart_setup(char *s) +static int mtdpart_setup(char *s) { cmdline = s; return 1; -- cgit v0.10.2 From 4010db56c8fe5bbb8e223bf9c9c36d41e9ad4f79 Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Fri, 10 Nov 2006 12:19:32 +0100 Subject: [MTD] [NAND] Fix endianess bug in ndfc.c The writel() call accidentally clears all bits in the NDFC_CCR register (endianess problem). Now __raw_writel() is used instead. Tested on Bamboo with NAND on chip select 0 and chip select 1. Signed-off-by: Stefan Roese Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c index 039c759..fd7a8d5 100644 --- a/drivers/mtd/nand/ndfc.c +++ b/drivers/mtd/nand/ndfc.c @@ -56,7 +56,7 @@ static void ndfc_select_chip(struct mtd_info *mtd, int chip) ccr |= NDFC_CCR_BS(chip + pchip->chip_offset); } else ccr |= NDFC_CCR_RESET_CE; - writel(ccr, ndfc->ndfcbase + NDFC_CCR); + __raw_writel(ccr, ndfc->ndfcbase + NDFC_CCR); } static void ndfc_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) -- cgit v0.10.2 From 90afffc8bd79d130b58acd18f972ce4e00b8e20f Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Mon, 6 Nov 2006 16:33:57 -0700 Subject: [MTD] [MAPS] Support for BIOS flash chips on the nvidia ck804 southbridge Add support for accessing BIOS flash chips connected to the NVIDIA ck804 southbridge. Signed-off-by: Ryan Jackson Signed-off-by: David Woodhouse diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 7514a9b..fa74668 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -193,6 +193,15 @@ config MTD_ESB2ROM BE VERY CAREFUL. +config MTD_CK804XROM + tristate "BIOS flash chip on Nvidia CK804" + depends on X86 && MTD_JEDECPROBE + help + Support for treating the BIOS flash chip on nvidia motherboards + as an MTD device - with this you can reprogram your BIOS. + + BE VERY CAREFUL. + config MTD_SCB2_FLASH tristate "BIOS flash chip on Intel SCB2 boards" depends on X86 && MTD_JEDECPROBE diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 9061432..3450521 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_MTD_L440GX) += l440gx.o obj-$(CONFIG_MTD_AMD76XROM) += amd76xrom.o obj-$(CONFIG_MTD_ESB2ROM) += esb2rom.o obj-$(CONFIG_MTD_ICHXROM) += ichxrom.o +obj-$(CONFIG_MTD_CK804XROM) += ck804xrom.o obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o obj-$(CONFIG_MTD_LUBBOCK) += lubbock-flash.o obj-$(CONFIG_MTD_MAINSTONE) += mainstone-flash.o diff --git a/drivers/mtd/maps/ck804xrom.c b/drivers/mtd/maps/ck804xrom.c new file mode 100644 index 0000000..c222b88 --- /dev/null +++ b/drivers/mtd/maps/ck804xrom.c @@ -0,0 +1,356 @@ +/* + * ck804xrom.c + * + * Normal mappings of chips in physical memory + * + * Dave Olsen + * Ryan Jackson + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define MOD_NAME KBUILD_BASENAME + +#define ADDRESS_NAME_LEN 18 + +#define ROM_PROBE_STEP_SIZE (64*1024) + +struct ck804xrom_window { + void __iomem *virt; + unsigned long phys; + unsigned long size; + struct list_head maps; + struct resource rsrc; + struct pci_dev *pdev; +}; + +struct ck804xrom_map_info { + struct list_head list; + struct map_info map; + struct mtd_info *mtd; + struct resource rsrc; + char map_name[sizeof(MOD_NAME) + 2 + ADDRESS_NAME_LEN]; +}; + + +/* The 2 bits controlling the window size are often set to allow reading + * the BIOS, but too small to allow writing, since the lock registers are + * 4MiB lower in the address space than the data. + * + * This is intended to prevent flashing the bios, perhaps accidentally. + * + * This parameter allows the normal driver to override the BIOS settings. + * + * The bits are 6 and 7. If both bits are set, it is a 5MiB window. + * If only the 7 Bit is set, it is a 4MiB window. Otherwise, a + * 64KiB window. + * + */ +static uint win_size_bits = 0; +module_param(win_size_bits, uint, 0); +MODULE_PARM_DESC(win_size_bits, "ROM window size bits override for 0x88 byte, normally set by BIOS."); + +static struct ck804xrom_window ck804xrom_window = { + .maps = LIST_HEAD_INIT(ck804xrom_window.maps), +}; + +static void ck804xrom_cleanup(struct ck804xrom_window *window) +{ + struct ck804xrom_map_info *map, *scratch; + u8 byte; + + if (window->pdev) { + /* Disable writes through the rom window */ + pci_read_config_byte(window->pdev, 0x6d, &byte); + pci_write_config_byte(window->pdev, 0x6d, byte & ~1); + } + + /* Free all of the mtd devices */ + list_for_each_entry_safe(map, scratch, &window->maps, list) { + if (map->rsrc.parent) + release_resource(&map->rsrc); + + del_mtd_device(map->mtd); + map_destroy(map->mtd); + list_del(&map->list); + kfree(map); + } + if (window->rsrc.parent) + release_resource(&window->rsrc); + + if (window->virt) { + iounmap(window->virt); + window->virt = NULL; + window->phys = 0; + window->size = 0; + } + pci_dev_put(window->pdev); +} + + +static int __devinit ck804xrom_init_one (struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL }; + u8 byte; + struct ck804xrom_window *window = &ck804xrom_window; + struct ck804xrom_map_info *map = NULL; + unsigned long map_top; + + /* Remember the pci dev I find the window in */ + window->pdev = pci_dev_get(pdev); + + /* Enable the selected rom window. This is often incorrectly + * set up by the BIOS, and the 4MiB offset for the lock registers + * requires the full 5MiB of window space. + * + * This 'write, then read' approach leaves the bits for + * other uses of the hardware info. + */ + pci_read_config_byte(pdev, 0x88, &byte); + pci_write_config_byte(pdev, 0x88, byte | win_size_bits ); + + + /* Assume the rom window is properly setup, and find it's size */ + pci_read_config_byte(pdev, 0x88, &byte); + + if ((byte & ((1<<7)|(1<<6))) == ((1<<7)|(1<<6))) + window->phys = 0xffb00000; /* 5MiB */ + else if ((byte & (1<<7)) == (1<<7)) + window->phys = 0xffc00000; /* 4MiB */ + else + window->phys = 0xffff0000; /* 64KiB */ + + window->size = 0xffffffffUL - window->phys + 1UL; + + /* + * Try to reserve the window mem region. If this fails then + * it is likely due to a fragment of the window being + * "reserved" by the BIOS. In the case that the + * request_mem_region() fails then once the rom size is + * discovered we will try to reserve the unreserved fragment. + */ + window->rsrc.name = MOD_NAME; + window->rsrc.start = window->phys; + window->rsrc.end = window->phys + window->size - 1; + window->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; + if (request_resource(&iomem_resource, &window->rsrc)) { + window->rsrc.parent = NULL; + printk(KERN_ERR MOD_NAME + " %s(): Unable to register resource" + " 0x%.016llx-0x%.016llx - kernel bug?\n", + __func__, + (unsigned long long)window->rsrc.start, + (unsigned long long)window->rsrc.end); + } + + + /* Enable writes through the rom window */ + pci_read_config_byte(pdev, 0x6d, &byte); + pci_write_config_byte(pdev, 0x6d, byte | 1); + + /* FIXME handle registers 0x80 - 0x8C the bios region locks */ + + /* For write accesses caches are useless */ + window->virt = ioremap_nocache(window->phys, window->size); + if (!window->virt) { + printk(KERN_ERR MOD_NAME ": ioremap(%08lx, %08lx) failed\n", + window->phys, window->size); + goto out; + } + + /* Get the first address to look for a rom chip at */ + map_top = window->phys; +#if 1 + /* The probe sequence run over the firmware hub lock + * registers sets them to 0x7 (no access). + * Probe at most the last 4MiB of the address space. + */ + if (map_top < 0xffc00000) + map_top = 0xffc00000; +#endif + /* Loop through and look for rom chips. Since we don't know the + * starting address for each chip, probe every ROM_PROBE_STEP_SIZE + * bytes from the starting address of the window. + */ + while((map_top - 1) < 0xffffffffUL) { + struct cfi_private *cfi; + unsigned long offset; + int i; + + if (!map) + map = kmalloc(sizeof(*map), GFP_KERNEL); + + if (!map) { + printk(KERN_ERR MOD_NAME ": kmalloc failed"); + goto out; + } + memset(map, 0, sizeof(*map)); + INIT_LIST_HEAD(&map->list); + map->map.name = map->map_name; + map->map.phys = map_top; + offset = map_top - window->phys; + map->map.virt = (void __iomem *) + (((unsigned long)(window->virt)) + offset); + map->map.size = 0xffffffffUL - map_top + 1UL; + /* Set the name of the map to the address I am trying */ + sprintf(map->map_name, "%s @%08lx", + MOD_NAME, map->map.phys); + + /* There is no generic VPP support */ + for(map->map.bankwidth = 32; map->map.bankwidth; + map->map.bankwidth >>= 1) + { + char **probe_type; + /* Skip bankwidths that are not supported */ + if (!map_bankwidth_supported(map->map.bankwidth)) + continue; + + /* Setup the map methods */ + simple_map_init(&map->map); + + /* Try all of the probe methods */ + probe_type = rom_probe_types; + for(; *probe_type; probe_type++) { + map->mtd = do_map_probe(*probe_type, &map->map); + if (map->mtd) + goto found; + } + } + map_top += ROM_PROBE_STEP_SIZE; + continue; + found: + /* Trim the size if we are larger than the map */ + if (map->mtd->size > map->map.size) { + printk(KERN_WARNING MOD_NAME + " rom(%u) larger than window(%lu). fixing...\n", + map->mtd->size, map->map.size); + map->mtd->size = map->map.size; + } + if (window->rsrc.parent) { + /* + * Registering the MTD device in iomem may not be possible + * if there is a BIOS "reserved" and BUSY range. If this + * fails then continue anyway. + */ + map->rsrc.name = map->map_name; + map->rsrc.start = map->map.phys; + map->rsrc.end = map->map.phys + map->mtd->size - 1; + map->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; + if (request_resource(&window->rsrc, &map->rsrc)) { + printk(KERN_ERR MOD_NAME + ": cannot reserve MTD resource\n"); + map->rsrc.parent = NULL; + } + } + + /* Make the whole region visible in the map */ + map->map.virt = window->virt; + map->map.phys = window->phys; + cfi = map->map.fldrv_priv; + for(i = 0; i < cfi->numchips; i++) + cfi->chips[i].start += offset; + + /* Now that the mtd devices is complete claim and export it */ + map->mtd->owner = THIS_MODULE; + if (add_mtd_device(map->mtd)) { + map_destroy(map->mtd); + map->mtd = NULL; + goto out; + } + + + /* Calculate the new value of map_top */ + map_top += map->mtd->size; + + /* File away the map structure */ + list_add(&map->list, &window->maps); + map = NULL; + } + + out: + /* Free any left over map structures */ + if (map) + kfree(map); + + /* See if I have any map structures */ + if (list_empty(&window->maps)) { + ck804xrom_cleanup(window); + return -ENODEV; + } + return 0; +} + + +static void __devexit ck804xrom_remove_one (struct pci_dev *pdev) +{ + struct ck804xrom_window *window = &ck804xrom_window; + + ck804xrom_cleanup(window); +} + +static struct pci_device_id ck804xrom_pci_tbl[] = { + { PCI_VENDOR_ID_NVIDIA, 0x0051, + PCI_ANY_ID, PCI_ANY_ID, }, /* nvidia ck804 */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, ck804xrom_pci_tbl); + +#if 0 +static struct pci_driver ck804xrom_driver = { + .name = MOD_NAME, + .id_table = ck804xrom_pci_tbl, + .probe = ck804xrom_init_one, + .remove = ck804xrom_remove_one, +}; +#endif + +static int __init init_ck804xrom(void) +{ + struct pci_dev *pdev; + struct pci_device_id *id; + int retVal; + pdev = NULL; + + for(id = ck804xrom_pci_tbl; id->vendor; id++) { + pdev = pci_find_device(id->vendor, id->device, NULL); + if (pdev) + break; + } + if (pdev) { + retVal = ck804xrom_init_one(pdev, &ck804xrom_pci_tbl[0]); + pci_dev_put(pdev); + return retVal; + } + return -ENXIO; +#if 0 + return pci_module_init(&ck804xrom_driver); +#endif +} + +static void __exit cleanup_ck804xrom(void) +{ + ck804xrom_remove_one(ck804xrom_window.pdev); +} + +module_init(init_ck804xrom); +module_exit(cleanup_ck804xrom); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Eric Biederman , Dave Olsen "); +MODULE_DESCRIPTION("MTD map driver for BIOS chips on the Nvidia ck804 southbridge"); + -- cgit v0.10.2 From 191876729901d0c8dab8a331f9a1e4b73a56457b Mon Sep 17 00:00:00 2001 From: Richard Purdie Date: Fri, 27 Oct 2006 09:09:33 +0100 Subject: [MTD] Allow variable block sizes in mtd_blkdevs Currently, mtd_blkdevs enforces a block size of 512, even if the drivers can seemingly request a different size. This patch fixes mtd_blkdevs so block sizes other than 512 work correctly. Signed-off-by: Richard Purdie Signed-off-by: David Woodhouse diff --git a/drivers/mtd/ftl.c b/drivers/mtd/ftl.c index 8a878b3..da39355 100644 --- a/drivers/mtd/ftl.c +++ b/drivers/mtd/ftl.c @@ -1054,7 +1054,7 @@ static void ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) le32_to_cpu(partition->header.FormattedSize) >> 10); #endif partition->mbd.size = le32_to_cpu(partition->header.FormattedSize) >> 9; - partition->mbd.blksize = SECTOR_SIZE; + partition->mbd.tr = tr; partition->mbd.devnum = -1; if (!add_mtd_blktrans_dev((void *)partition)) @@ -1076,6 +1076,7 @@ struct mtd_blktrans_ops ftl_tr = { .name = "ftl", .major = FTL_MAJOR, .part_bits = PART_BITS, + .blksize = SECTOR_SIZE, .readsect = ftl_readsect, .writesect = ftl_writesect, .getgeo = ftl_getgeo, diff --git a/drivers/mtd/inftlcore.c b/drivers/mtd/inftlcore.c index 4116535..a1b2de6 100644 --- a/drivers/mtd/inftlcore.c +++ b/drivers/mtd/inftlcore.c @@ -77,7 +77,7 @@ static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) inftl->mbd.mtd = mtd; inftl->mbd.devnum = -1; - inftl->mbd.blksize = 512; + inftl->mbd.tr = tr; if (INFTL_mount(inftl) < 0) { @@ -945,6 +945,7 @@ static struct mtd_blktrans_ops inftl_tr = { .name = "inftl", .major = INFTL_MAJOR, .part_bits = INFTL_PARTN_BITS, + .blksize = 512, .getgeo = inftl_getgeo, .readsect = inftl_readblock, .writesect = inftl_writeblock, diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index 178b53b..b5d62cb 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -42,19 +42,20 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr, unsigned long block, nsect; char *buf; - block = req->sector; - nsect = req->current_nr_sectors; + block = req->sector << 9 >> tr->blkshift; + nsect = req->current_nr_sectors << 9 >> tr->blkshift; + buf = req->buffer; if (!blk_fs_request(req)) return 0; - if (block + nsect > get_capacity(req->rq_disk)) + if (req->sector + req->current_nr_sectors > get_capacity(req->rq_disk)) return 0; switch(rq_data_dir(req)) { case READ: - for (; nsect > 0; nsect--, block++, buf += 512) + for (; nsect > 0; nsect--, block++, buf += tr->blksize) if (tr->readsect(dev, block, buf)) return 0; return 1; @@ -63,7 +64,7 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr, if (!tr->writesect) return 0; - for (; nsect > 0; nsect--, block++, buf += 512) + for (; nsect > 0; nsect--, block++, buf += tr->blksize) if (tr->writesect(dev, block, buf)) return 0; return 1; @@ -297,7 +298,7 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) /* 2.5 has capacity in units of 512 bytes while still having BLOCK_SIZE_BITS set to 10. Just to keep us amused. */ - set_capacity(gd, (new->size * new->blksize) >> 9); + set_capacity(gd, (new->size * tr->blksize) >> 9); gd->private_data = new; new->blkcore_priv = gd; @@ -401,6 +402,8 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr) } tr->blkcore_priv->rq->queuedata = tr; + blk_queue_hardsect_size(tr->blkcore_priv->rq, tr->blksize); + tr->blkshift = ffs(tr->blksize) - 1; ret = kernel_thread(mtd_blktrans_thread, tr, CLONE_KERNEL); if (ret < 0) { diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c index 04ed3469..a052648 100644 --- a/drivers/mtd/mtdblock.c +++ b/drivers/mtd/mtdblock.c @@ -348,7 +348,7 @@ static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) dev->mtd = mtd; dev->devnum = mtd->index; - dev->blksize = 512; + dev->size = mtd->size >> 9; dev->tr = tr; @@ -368,6 +368,7 @@ static struct mtd_blktrans_ops mtdblock_tr = { .name = "mtdblock", .major = 31, .part_bits = 0, + .blksize = 512, .open = mtdblock_open, .flush = mtdblock_flush, .release = mtdblock_release, diff --git a/drivers/mtd/mtdblock_ro.c b/drivers/mtd/mtdblock_ro.c index 29563ed..642ccc6 100644 --- a/drivers/mtd/mtdblock_ro.c +++ b/drivers/mtd/mtdblock_ro.c @@ -42,7 +42,7 @@ static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) dev->mtd = mtd; dev->devnum = mtd->index; - dev->blksize = 512; + dev->size = mtd->size >> 9; dev->tr = tr; dev->readonly = 1; @@ -60,6 +60,7 @@ static struct mtd_blktrans_ops mtdblock_tr = { .name = "mtdblock", .major = 31, .part_bits = 0, + .blksize = 512, .readsect = mtdblock_readsect, .writesect = mtdblock_writesect, .add_mtd = mtdblock_add_mtd, diff --git a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c index b5a5f8d..d974ada 100644 --- a/drivers/mtd/nftlcore.c +++ b/drivers/mtd/nftlcore.c @@ -67,7 +67,7 @@ static void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) nftl->mbd.mtd = mtd; nftl->mbd.devnum = -1; - nftl->mbd.blksize = 512; + nftl->mbd.tr = tr; if (NFTL_mount(nftl) < 0) { @@ -797,6 +797,7 @@ static struct mtd_blktrans_ops nftl_tr = { .name = "nftl", .major = NFTL_MAJOR, .part_bits = NFTL_PARTN_BITS, + .blksize = 512, .getgeo = nftl_getgeo, .readsect = nftl_readblock, #ifdef CONFIG_NFTL_RW diff --git a/drivers/mtd/rfd_ftl.c b/drivers/mtd/rfd_ftl.c index fa4362f..d60cc66 100644 --- a/drivers/mtd/rfd_ftl.c +++ b/drivers/mtd/rfd_ftl.c @@ -787,7 +787,6 @@ static void rfd_ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) if (scan_header(part) == 0) { part->mbd.size = part->sector_count; - part->mbd.blksize = SECTOR_SIZE; part->mbd.tr = tr; part->mbd.devnum = -1; if (!(mtd->flags & MTD_WRITEABLE)) @@ -829,6 +828,8 @@ struct mtd_blktrans_ops rfd_ftl_tr = { .name = "rfd", .major = RFD_FTL_MAJOR, .part_bits = PART_BITS, + .blksize = SECTOR_SIZE, + .readsect = rfd_ftl_readsect, .writesect = rfd_ftl_writesect, .getgeo = rfd_ftl_getgeo, diff --git a/include/linux/mtd/blktrans.h b/include/linux/mtd/blktrans.h index 72fc68c..9a6e2f9 100644 --- a/include/linux/mtd/blktrans.h +++ b/include/linux/mtd/blktrans.h @@ -24,7 +24,6 @@ struct mtd_blktrans_dev { struct mtd_info *mtd; struct mutex lock; int devnum; - int blksize; unsigned long size; int readonly; void *blkcore_priv; /* gendisk in 2.5, devfs_handle in 2.4 */ @@ -36,6 +35,8 @@ struct mtd_blktrans_ops { char *name; int major; int part_bits; + int blksize; + int blkshift; /* Access functions */ int (*readsect)(struct mtd_blktrans_dev *dev, -- cgit v0.10.2 From 7014568bad55c20b7ee4f439d78c9e875912d51f Mon Sep 17 00:00:00 2001 From: Vitaly Wool Date: Fri, 3 Nov 2006 18:20:38 +0300 Subject: [MTD] [NAND] remove len/ooblen confusion. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As was discussed between Ricard Wanderlöf, David Woodhouse, Artem Bityutskiy and me, the current API for reading/writing OOB is confusing. The thing that introduces confusion is the need to specify ops.len together with ops.ooblen for reads/writes that concern only OOB not data area. So, ops.len is overloaded: when ops.datbuf != NULL it serves to specify the length of the data read, and when ops.datbuf == NULL, it serves to specify the full OOB read length. The patch inlined below is the slightly updated version of the previous patch serving the same purpose, but with the new Artem's comments taken into account. Artem, BTW, thanks a lot for your valuable input! Signed-off-by: Vitaly Wool Signed-off-by: David Woodhouse diff --git a/drivers/mtd/inftlcore.c b/drivers/mtd/inftlcore.c index a1b2de6..d2f54c0 100644 --- a/drivers/mtd/inftlcore.c +++ b/drivers/mtd/inftlcore.c @@ -163,10 +163,9 @@ int inftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len, ops.ooblen = len; ops.oobbuf = buf; ops.datbuf = NULL; - ops.len = len; res = mtd->read_oob(mtd, offs & ~(mtd->writesize - 1), &ops); - *retlen = ops.retlen; + *retlen = ops.oobretlen; return res; } @@ -184,10 +183,9 @@ int inftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len, ops.ooblen = len; ops.oobbuf = buf; ops.datbuf = NULL; - ops.len = len; res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops); - *retlen = ops.retlen; + *retlen = ops.oobretlen; return res; } diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 866c8e0..07618f5 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -499,13 +499,12 @@ static int mtd_ioctl(struct inode *inode, struct file *file, if (ret) return ret; - ops.len = buf.length; ops.ooblen = buf.length; ops.ooboffs = buf.start & (mtd->oobsize - 1); ops.datbuf = NULL; ops.mode = MTD_OOB_PLACE; - if (ops.ooboffs && ops.len > (mtd->oobsize - ops.ooboffs)) + if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs)) return -EINVAL; ops.oobbuf = kmalloc(buf.length, GFP_KERNEL); @@ -520,7 +519,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, buf.start &= ~(mtd->oobsize - 1); ret = mtd->write_oob(mtd, buf.start, &ops); - if (copy_to_user(argp + sizeof(uint32_t), &ops.retlen, + if (copy_to_user(argp + sizeof(uint32_t), &ops.oobretlen, sizeof(uint32_t))) ret = -EFAULT; @@ -548,7 +547,6 @@ static int mtd_ioctl(struct inode *inode, struct file *file, if (ret) return ret; - ops.len = buf.length; ops.ooblen = buf.length; ops.ooboffs = buf.start & (mtd->oobsize - 1); ops.datbuf = NULL; @@ -564,10 +562,10 @@ static int mtd_ioctl(struct inode *inode, struct file *file, buf.start &= ~(mtd->oobsize - 1); ret = mtd->read_oob(mtd, buf.start, &ops); - if (put_user(ops.retlen, (uint32_t __user *)argp)) + if (put_user(ops.oobretlen, (uint32_t __user *)argp)) ret = -EFAULT; - else if (ops.retlen && copy_to_user(buf.ptr, ops.oobbuf, - ops.retlen)) + else if (ops.oobretlen && copy_to_user(buf.ptr, ops.oobbuf, + ops.oobretlen)) ret = -EFAULT; kfree(ops.oobbuf); diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index 1fea631..cf927a8 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -247,7 +247,7 @@ concat_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) struct mtd_oob_ops devops = *ops; int i, err, ret = 0; - ops->retlen = 0; + ops->retlen = ops->oobretlen = 0; for (i = 0; i < concat->num_subdev; i++) { struct mtd_info *subdev = concat->subdev[i]; @@ -263,6 +263,7 @@ concat_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) err = subdev->read_oob(subdev, from, &devops); ops->retlen += devops.retlen; + ops->oobretlen += devops.oobretlen; /* Save information about bitflips! */ if (unlikely(err)) { @@ -278,14 +279,18 @@ concat_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) return err; } - devops.len = ops->len - ops->retlen; - if (!devops.len) - return ret; - - if (devops.datbuf) + if (devops.datbuf) { + devops.len = ops->len - ops->retlen; + if (!devops.len) + return ret; devops.datbuf += devops.retlen; - if (devops.oobbuf) - devops.oobbuf += devops.ooblen; + } + if (devops.oobbuf) { + devops.ooblen = ops->ooblen - ops->oobretlen; + if (!devops.ooblen) + return ret; + devops.oobbuf += ops->oobretlen; + } from = 0; } @@ -321,14 +326,18 @@ concat_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) if (err) return err; - devops.len = ops->len - ops->retlen; - if (!devops.len) - return 0; - - if (devops.datbuf) + if (devops.datbuf) { + devops.len = ops->len - ops->retlen; + if (!devops.len) + return 0; devops.datbuf += devops.retlen; - if (devops.oobbuf) - devops.oobbuf += devops.ooblen; + } + if (devops.oobbuf) { + devops.ooblen = ops->ooblen - ops->oobretlen; + if (!devops.ooblen) + return 0; + devops.oobbuf += devops.oobretlen; + } to = 0; } return -EINVAL; diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 06a9303..a20f75f 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -94,7 +94,7 @@ static int part_read_oob(struct mtd_info *mtd, loff_t from, if (from >= mtd->size) return -EINVAL; - if (from + ops->len > mtd->size) + if (ops->datbuf && from + ops->len > mtd->size) return -EINVAL; res = part->master->read_oob(part->master, from + part->offset, ops); @@ -161,7 +161,7 @@ static int part_write_oob(struct mtd_info *mtd, loff_t to, if (to >= mtd->size) return -EINVAL; - if (to + ops->len > mtd->size) + if (ops->datbuf && to + ops->len > mtd->size) return -EINVAL; return part->master->write_oob(part->master, to + part->offset, ops); } diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 8df36e2..5dcb2e0 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -897,12 +897,11 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, * @chip: nand chip structure * @oob: oob destination address * @ops: oob ops structure + * @len: size of oob to transfer */ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob, - struct mtd_oob_ops *ops) + struct mtd_oob_ops *ops, size_t len) { - size_t len = ops->ooblen; - switch(ops->mode) { case MTD_OOB_PLACE: @@ -960,6 +959,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, int sndcmd = 1; int ret = 0; uint32_t readlen = ops->len; + uint32_t oobreadlen = ops->ooblen; uint8_t *bufpoi, *oob, *buf; stats = mtd->ecc_stats; @@ -1006,10 +1006,17 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, if (unlikely(oob)) { /* Raw mode does data:oob:data:oob */ - if (ops->mode != MTD_OOB_RAW) - oob = nand_transfer_oob(chip, oob, ops); - else - buf = nand_transfer_oob(chip, buf, ops); + if (ops->mode != MTD_OOB_RAW) { + int toread = min(oobreadlen, + chip->ecc.layout->oobavail); + if (toread) { + oob = nand_transfer_oob(chip, + oob, ops, toread); + oobreadlen -= toread; + } + } else + buf = nand_transfer_oob(chip, + buf, ops, mtd->oobsize); } if (!(chip->options & NAND_NO_READRDY)) { @@ -1056,6 +1063,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, } ops->retlen = ops->len - (size_t) readlen; + if (oob) + ops->oobretlen = ops->ooblen - oobreadlen; if (ret) return ret; @@ -1256,12 +1265,18 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, int page, realpage, chipnr, sndcmd = 1; struct nand_chip *chip = mtd->priv; int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; - int readlen = ops->len; + int readlen = ops->ooblen; + int len; uint8_t *buf = ops->oobbuf; DEBUG(MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08Lx, len = %i\n", (unsigned long long)from, readlen); + if (ops->mode == MTD_OOB_RAW) + len = mtd->oobsize; + else + len = chip->ecc.layout->oobavail; + chipnr = (int)(from >> chip->chip_shift); chip->select_chip(mtd, chipnr); @@ -1271,7 +1286,9 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, while(1) { sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd); - buf = nand_transfer_oob(chip, buf, ops); + + len = min(len, readlen); + buf = nand_transfer_oob(chip, buf, ops, len); if (!(chip->options & NAND_NO_READRDY)) { /* @@ -1286,7 +1303,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, nand_wait_ready(mtd); } - readlen -= ops->ooblen; + readlen -= len; if (!readlen) break; @@ -1308,7 +1325,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, sndcmd = 1; } - ops->retlen = ops->len; + ops->oobretlen = ops->ooblen; return 0; } @@ -1329,7 +1346,7 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from, ops->retlen = 0; /* Do not allow reads past end of device */ - if ((from + ops->len) > mtd->size) { + if (ops->datbuf && (from + ops->len) > mtd->size) { DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " "Attempt read beyond end of device\n"); return -EINVAL; @@ -1654,6 +1671,8 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, } ops->retlen = ops->len - writelen; + if (unlikely(oob)) + ops->oobretlen = ops->ooblen; return ret; } @@ -1709,10 +1728,10 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, struct nand_chip *chip = mtd->priv; DEBUG(MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", - (unsigned int)to, (int)ops->len); + (unsigned int)to, (int)ops->ooblen); /* Do not allow write past end of page */ - if ((ops->ooboffs + ops->len) > mtd->oobsize) { + if ((ops->ooboffs + ops->ooblen) > mtd->oobsize) { DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: " "Attempt to write past end of page\n"); return -EINVAL; @@ -1748,7 +1767,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, if (status) return status; - ops->retlen = ops->len; + ops->oobretlen = ops->ooblen; return 0; } @@ -1768,7 +1787,7 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to, ops->retlen = 0; /* Do not allow writes past end of device */ - if ((to + ops->len) > mtd->size) { + if (ops->datbuf && (to + ops->len) > mtd->size) { DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " "Attempt read beyond end of device\n"); return -EINVAL; diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 9402653..4e74fe9 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -333,7 +333,6 @@ static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd, struct mtd_oob_ops ops; int j, ret; - ops.len = mtd->oobsize; ops.ooblen = mtd->oobsize; ops.oobbuf = buf; ops.ooboffs = 0; @@ -676,10 +675,10 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, "bad block table\n"); } /* Read oob data */ - ops.len = (len >> this->page_shift) * mtd->oobsize; + ops.ooblen = (len >> this->page_shift) * mtd->oobsize; ops.oobbuf = &buf[len]; res = mtd->read_oob(mtd, to + mtd->writesize, &ops); - if (res < 0 || ops.retlen != ops.len) + if (res < 0 || ops.oobretlen != ops.ooblen) goto outerr; /* Calc the byte offset in the buffer */ diff --git a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c index d974ada..f4d3854 100644 --- a/drivers/mtd/nftlcore.c +++ b/drivers/mtd/nftlcore.c @@ -147,10 +147,9 @@ int nftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len, ops.ooblen = len; ops.oobbuf = buf; ops.datbuf = NULL; - ops.len = len; res = mtd->read_oob(mtd, offs & ~(mtd->writesize - 1), &ops); - *retlen = ops.retlen; + *retlen = ops.oobretlen; return res; } @@ -168,10 +167,9 @@ int nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len, ops.ooblen = len; ops.oobbuf = buf; ops.datbuf = NULL; - ops.len = len; res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops); - *retlen = ops.retlen; + *retlen = ops.oobretlen; return res; } diff --git a/drivers/mtd/ssfdc.c b/drivers/mtd/ssfdc.c index 79d3bb6..e834cc1 100644 --- a/drivers/mtd/ssfdc.c +++ b/drivers/mtd/ssfdc.c @@ -172,13 +172,12 @@ static int read_raw_oob(struct mtd_info *mtd, loff_t offs, uint8_t *buf) ops.mode = MTD_OOB_RAW; ops.ooboffs = 0; - ops.ooblen = mtd->oobsize; - ops.len = OOB_SIZE; + ops.ooblen = OOB_SIZE; ops.oobbuf = buf; ops.datbuf = NULL; ret = mtd->read_oob(mtd, offs, &ops); - if (ret < 0 || ops.retlen != OOB_SIZE) + if (ret < 0 || ops.oobretlen != OOB_SIZE) return -1; return 0; diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c index b9b7007..dcb18e9 100644 --- a/fs/jffs2/wbuf.c +++ b/fs/jffs2/wbuf.c @@ -968,8 +968,7 @@ int jffs2_check_oob_empty(struct jffs2_sb_info *c, int oobsize = c->mtd->oobsize; struct mtd_oob_ops ops; - ops.len = NR_OOB_SCAN_PAGES * oobsize; - ops.ooblen = oobsize; + ops.ooblen = NR_OOB_SCAN_PAGES * oobsize; ops.oobbuf = c->oobbuf; ops.ooboffs = 0; ops.datbuf = NULL; @@ -982,10 +981,10 @@ int jffs2_check_oob_empty(struct jffs2_sb_info *c, return ret; } - if (ops.retlen < ops.len) { + if (ops.oobretlen < ops.ooblen) { D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB " "returned short read (%zd bytes not %d) for block " - "at %08x\n", ops.retlen, ops.len, jeb->offset)); + "at %08x\n", ops.oobretlen, ops.ooblen, jeb->offset)); return -EIO; } @@ -1004,7 +1003,7 @@ int jffs2_check_oob_empty(struct jffs2_sb_info *c, } /* we know, we are aligned :) */ - for (page = oobsize; page < ops.len; page += sizeof(long)) { + for (page = oobsize; page < ops.ooblen; page += sizeof(long)) { long dat = *(long *)(&ops.oobbuf[page]); if(dat != -1) return 1; @@ -1032,7 +1031,6 @@ int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, return 2; } - ops.len = oobsize; ops.ooblen = oobsize; ops.oobbuf = c->oobbuf; ops.ooboffs = 0; @@ -1047,10 +1045,10 @@ int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, return ret; } - if (ops.retlen < ops.len) { + if (ops.oobretlen < ops.ooblen) { D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): " "Read OOB return short read (%zd bytes not %d) " - "for block at %08x\n", ops.retlen, ops.len, + "for block at %08x\n", ops.oobretlen, ops.ooblen, jeb->offset)); return -EIO; } @@ -1089,8 +1087,7 @@ int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, n.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER); n.totlen = cpu_to_je32(8); - ops.len = c->fsdata_len; - ops.ooblen = c->fsdata_len;; + ops.ooblen = c->fsdata_len; ops.oobbuf = (uint8_t *)&n; ops.ooboffs = c->fsdata_pos; ops.datbuf = NULL; @@ -1104,10 +1101,10 @@ int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, jeb->offset, ret)); return ret; } - if (ops.retlen != ops.len) { + if (ops.oobretlen != ops.ooblen) { D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): " "Short write for block at %08x: %zd not %d\n", - jeb->offset, ops.retlen, ops.len)); + jeb->offset, ops.oobretlen, ops.ooblen)); return -EIO; } return 0; diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 94a443d..4fc391e 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -75,15 +75,12 @@ typedef enum { * struct mtd_oob_ops - oob operation operands * @mode: operation mode * - * @len: number of bytes to write/read. When a data buffer is given - * (datbuf != NULL) this is the number of data bytes. When - * no data buffer is available this is the number of oob bytes. + * @len: number of data bytes to write/read * - * @retlen: number of bytes written/read. When a data buffer is given - * (datbuf != NULL) this is the number of data bytes. When - * no data buffer is available this is the number of oob bytes. + * @retlen: number of data bytes written/read * - * @ooblen: number of oob bytes per page + * @ooblen: number of oob bytes to write/read + * @oobretlen: number of oob bytes written/read * @ooboffs: offset of oob data in the oob area (only relevant when * mode = MTD_OOB_PLACE) * @datbuf: data buffer - if NULL only oob data are read/written @@ -94,6 +91,7 @@ struct mtd_oob_ops { size_t len; size_t retlen; size_t ooblen; + size_t oobretlen; uint32_t ooboffs; uint8_t *datbuf; uint8_t *oobbuf; -- cgit v0.10.2 From 998a43e72d20afa7566dad66fd866fe939a89c09 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 28 Nov 2006 23:40:46 +0000 Subject: [MTD] Fix printk format warning in physmap. (resources again) Fix printk format warning: drivers/mtd/maps/physmap.c:93: warning: long long unsigned int format, long unsigned int arg (arg 2) Signed-off-by: Randy Dunlap Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse diff --git a/drivers/mtd/maps/physmap.c b/drivers/mtd/maps/physmap.c index d171776..bb67c50 100644 --- a/drivers/mtd/maps/physmap.c +++ b/drivers/mtd/maps/physmap.c @@ -89,7 +89,7 @@ static int physmap_flash_probe(struct platform_device *dev) return -ENODEV; printk(KERN_NOTICE "physmap platform flash device: %.8llx at %.8llx\n", - (unsigned long long)dev->resource->end - dev->resource->start + 1, + (unsigned long long)(dev->resource->end - dev->resource->start + 1), (unsigned long long)dev->resource->start); info = kmalloc(sizeof(struct physmap_flash_info), GFP_KERNEL); -- cgit v0.10.2 From 95b93a0cd46682c6d9e8eea803fda510cb6b863a Mon Sep 17 00:00:00 2001 From: Burman Yan Date: Wed, 15 Nov 2006 21:10:29 +0200 Subject: [MTD] replace kmalloc+memset with kzalloc Signed-off-by: Yan Burman Signed-off-by: David Woodhouse diff --git a/arch/ppc/platforms/ev64360.c b/arch/ppc/platforms/ev64360.c index 90ed375..f87e06f 100644 --- a/arch/ppc/platforms/ev64360.c +++ b/arch/ppc/platforms/ev64360.c @@ -358,13 +358,12 @@ ev64360_setup_mtd(void) ptbl_entries = 3; - if ((ptbl = kmalloc(ptbl_entries * sizeof(struct mtd_partition), + if ((ptbl = kzalloc(ptbl_entries * sizeof(struct mtd_partition), GFP_KERNEL)) == NULL) { printk(KERN_WARNING "Can't alloc MTD partition table\n"); return -ENOMEM; } - memset(ptbl, 0, ptbl_entries * sizeof(struct mtd_partition)); ptbl[0].name = "reserved"; ptbl[0].offset = 0; diff --git a/drivers/mtd/afs.c b/drivers/mtd/afs.c index 6a45be0..52d51eb 100644 --- a/drivers/mtd/afs.c +++ b/drivers/mtd/afs.c @@ -207,11 +207,10 @@ static int parse_afs_partitions(struct mtd_info *mtd, if (!sz) return ret; - parts = kmalloc(sz, GFP_KERNEL); + parts = kzalloc(sz, GFP_KERNEL); if (!parts) return -ENOMEM; - memset(parts, 0, sz); str = (char *)(parts + idx); /* diff --git a/drivers/mtd/chips/amd_flash.c b/drivers/mtd/chips/amd_flash.c index 16eaca6..e7999f1 100644 --- a/drivers/mtd/chips/amd_flash.c +++ b/drivers/mtd/chips/amd_flash.c @@ -643,13 +643,12 @@ static struct mtd_info *amd_flash_probe(struct map_info *map) int reg_idx; int offset; - mtd = (struct mtd_info*)kmalloc(sizeof(*mtd), GFP_KERNEL); + mtd = kzalloc(sizeof(*mtd), GFP_KERNEL); if (!mtd) { printk(KERN_WARNING "%s: kmalloc failed for info structure\n", map->name); return NULL; } - memset(mtd, 0, sizeof(*mtd)); mtd->priv = map; memset(&temp, 0, sizeof(temp)); diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index e249736..11de545 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -337,12 +337,11 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary) struct mtd_info *mtd; int i; - mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); + mtd = kzalloc(sizeof(*mtd), GFP_KERNEL); if (!mtd) { printk(KERN_ERR "Failed to allocate memory for MTD device\n"); return NULL; } - memset(mtd, 0, sizeof(*mtd)); mtd->priv = map; mtd->type = MTD_NORFLASH; diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index ca0882b..e3acd39 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -257,12 +257,11 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) struct mtd_info *mtd; int i; - mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); + mtd = kzalloc(sizeof(*mtd), GFP_KERNEL); if (!mtd) { printk(KERN_WARNING "Failed to allocate memory for MTD device\n"); return NULL; } - memset(mtd, 0, sizeof(*mtd)); mtd->priv = map; mtd->type = MTD_NORFLASH; diff --git a/drivers/mtd/chips/cfi_cmdset_0020.c b/drivers/mtd/chips/cfi_cmdset_0020.c index fae70a5..d56849f 100644 --- a/drivers/mtd/chips/cfi_cmdset_0020.c +++ b/drivers/mtd/chips/cfi_cmdset_0020.c @@ -172,7 +172,7 @@ static struct mtd_info *cfi_staa_setup(struct map_info *map) int i,j; unsigned long devsize = (1<cfiq->DevSize) * cfi->interleave; - mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); + mtd = kzalloc(sizeof(*mtd), GFP_KERNEL); //printk(KERN_DEBUG "number of CFI chips: %d\n", cfi->numchips); if (!mtd) { @@ -181,7 +181,6 @@ static struct mtd_info *cfi_staa_setup(struct map_info *map) return NULL; } - memset(mtd, 0, sizeof(*mtd)); mtd->priv = map; mtd->type = MTD_NORFLASH; mtd->size = devsize * cfi->numchips; diff --git a/drivers/mtd/chips/gen_probe.c b/drivers/mtd/chips/gen_probe.c index cdb0f59..77843d5 100644 --- a/drivers/mtd/chips/gen_probe.c +++ b/drivers/mtd/chips/gen_probe.c @@ -113,13 +113,12 @@ static struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chi } mapsize = (max_chips + BITS_PER_LONG-1) / BITS_PER_LONG; - chip_map = kmalloc(mapsize, GFP_KERNEL); + chip_map = kzalloc(mapsize, GFP_KERNEL); if (!chip_map) { printk(KERN_WARNING "%s: kmalloc failed for CFI chip map\n", map->name); kfree(cfi.cfiq); return NULL; } - memset (chip_map, 0, mapsize); set_bit(0, chip_map); /* Mark first chip valid */ diff --git a/drivers/mtd/chips/jedec.c b/drivers/mtd/chips/jedec.c index 2c3f019..14e57b2 100644 --- a/drivers/mtd/chips/jedec.c +++ b/drivers/mtd/chips/jedec.c @@ -116,11 +116,10 @@ static struct mtd_info *jedec_probe(struct map_info *map) char Part[200]; memset(&priv,0,sizeof(priv)); - MTD = kmalloc(sizeof(struct mtd_info) + sizeof(struct jedec_private), GFP_KERNEL); + MTD = kzalloc(sizeof(struct mtd_info) + sizeof(struct jedec_private), GFP_KERNEL); if (!MTD) return NULL; - memset(MTD, 0, sizeof(struct mtd_info) + sizeof(struct jedec_private)); priv = (struct jedec_private *)&MTD[1]; my_bank_size = map->size; diff --git a/drivers/mtd/chips/map_absent.c b/drivers/mtd/chips/map_absent.c index ac01a94..fc478c0 100644 --- a/drivers/mtd/chips/map_absent.c +++ b/drivers/mtd/chips/map_absent.c @@ -47,13 +47,11 @@ static struct mtd_info *map_absent_probe(struct map_info *map) { struct mtd_info *mtd; - mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); + mtd = kzalloc(sizeof(*mtd), GFP_KERNEL); if (!mtd) { return NULL; } - memset(mtd, 0, sizeof(*mtd)); - map->fldrv = &map_absent_chipdrv; mtd->priv = map; mtd->name = map->name; diff --git a/drivers/mtd/chips/map_ram.c b/drivers/mtd/chips/map_ram.c index 3a66680..5cb6d52 100644 --- a/drivers/mtd/chips/map_ram.c +++ b/drivers/mtd/chips/map_ram.c @@ -55,12 +55,10 @@ static struct mtd_info *map_ram_probe(struct map_info *map) #endif /* OK. It seems to be RAM. */ - mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); + mtd = kzalloc(sizeof(*mtd), GFP_KERNEL); if (!mtd) return NULL; - memset(mtd, 0, sizeof(*mtd)); - map->fldrv = &mapram_chipdrv; mtd->priv = map; mtd->name = map->name; diff --git a/drivers/mtd/chips/map_rom.c b/drivers/mtd/chips/map_rom.c index 1b328b1..cb27f85 100644 --- a/drivers/mtd/chips/map_rom.c +++ b/drivers/mtd/chips/map_rom.c @@ -31,12 +31,10 @@ static struct mtd_info *map_rom_probe(struct map_info *map) { struct mtd_info *mtd; - mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); + mtd = kzalloc(sizeof(*mtd), GFP_KERNEL); if (!mtd) return NULL; - memset(mtd, 0, sizeof(*mtd)); - map->fldrv = &maprom_chipdrv; mtd->priv = map; mtd->name = map->name; diff --git a/drivers/mtd/chips/sharp.c b/drivers/mtd/chips/sharp.c index 967abbe..c9cd3d2 100644 --- a/drivers/mtd/chips/sharp.c +++ b/drivers/mtd/chips/sharp.c @@ -112,18 +112,16 @@ static struct mtd_info *sharp_probe(struct map_info *map) struct sharp_info *sharp = NULL; int width; - mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); + mtd = kzalloc(sizeof(*mtd), GFP_KERNEL); if(!mtd) return NULL; - sharp = kmalloc(sizeof(*sharp), GFP_KERNEL); + sharp = kzalloc(sizeof(*sharp), GFP_KERNEL); if(!sharp) { kfree(mtd); return NULL; } - memset(mtd, 0, sizeof(*mtd)); - width = sharp_probe_map(map,mtd); if(!width){ kfree(mtd); @@ -143,7 +141,6 @@ static struct mtd_info *sharp_probe(struct map_info *map) mtd->writesize = 1; mtd->name = map->name; - memset(sharp, 0, sizeof(*sharp)); sharp->chipshift = 23; sharp->numchips = 1; sharp->chips[0].start = 0; diff --git a/drivers/mtd/cmdlinepart.c b/drivers/mtd/cmdlinepart.c index 3402ce4..23fab14 100644 --- a/drivers/mtd/cmdlinepart.c +++ b/drivers/mtd/cmdlinepart.c @@ -163,13 +163,12 @@ static struct mtd_partition * newpart(char *s, *num_parts = this_part + 1; alloc_size = *num_parts * sizeof(struct mtd_partition) + extra_mem_size; - parts = kmalloc(alloc_size, GFP_KERNEL); + parts = kzalloc(alloc_size, GFP_KERNEL); if (!parts) { printk(KERN_ERR ERRP "out of memory\n"); return NULL; } - memset(parts, 0, alloc_size); extra_mem = (unsigned char *)(parts + *num_parts); } /* enter this partition (offset will be calculated later if it is zero at this point) */ diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index 401c6a2..6d917a4 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -295,10 +295,9 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size) if (!devname) return NULL; - dev = kmalloc(sizeof(struct block2mtd_dev), GFP_KERNEL); + dev = kzalloc(sizeof(struct block2mtd_dev), GFP_KERNEL); if (!dev) return NULL; - memset(dev, 0, sizeof(*dev)); /* Get a handle on the device */ bdev = open_bdev_excl(devname, O_RDWR, NULL); diff --git a/drivers/mtd/devices/ms02-nv.c b/drivers/mtd/devices/ms02-nv.c index 08dfb89..9cff119 100644 --- a/drivers/mtd/devices/ms02-nv.c +++ b/drivers/mtd/devices/ms02-nv.c @@ -131,11 +131,10 @@ static int __init ms02nv_init_one(ulong addr) int ret = -ENODEV; /* The module decodes 8MiB of address space. */ - mod_res = kmalloc(sizeof(*mod_res), GFP_KERNEL); + mod_res = kzalloc(sizeof(*mod_res), GFP_KERNEL); if (!mod_res) return -ENOMEM; - memset(mod_res, 0, sizeof(*mod_res)); mod_res->name = ms02nv_name; mod_res->start = addr; mod_res->end = addr + MS02NV_SLOT_SIZE - 1; @@ -153,24 +152,21 @@ static int __init ms02nv_init_one(ulong addr) } ret = -ENOMEM; - mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); + mtd = kzalloc(sizeof(*mtd), GFP_KERNEL); if (!mtd) goto err_out_mod_res_rel; - memset(mtd, 0, sizeof(*mtd)); - mp = kmalloc(sizeof(*mp), GFP_KERNEL); + mp = kzalloc(sizeof(*mp), GFP_KERNEL); if (!mp) goto err_out_mtd; - memset(mp, 0, sizeof(*mp)); mtd->priv = mp; mp->resource.module = mod_res; /* Firmware's diagnostic NVRAM area. */ - diag_res = kmalloc(sizeof(*diag_res), GFP_KERNEL); + diag_res = kzalloc(sizeof(*diag_res), GFP_KERNEL); if (!diag_res) goto err_out_mp; - memset(diag_res, 0, sizeof(*diag_res)); diag_res->name = ms02nv_res_diag_ram; diag_res->start = addr; diag_res->end = addr + MS02NV_RAM - 1; @@ -180,11 +176,10 @@ static int __init ms02nv_init_one(ulong addr) mp->resource.diag_ram = diag_res; /* User-available general-purpose NVRAM area. */ - user_res = kmalloc(sizeof(*user_res), GFP_KERNEL); + user_res = kzalloc(sizeof(*user_res), GFP_KERNEL); if (!user_res) goto err_out_diag_res; - memset(user_res, 0, sizeof(*user_res)); user_res->name = ms02nv_res_user_ram; user_res->start = addr + MS02NV_RAM; user_res->end = addr + size - 1; @@ -194,11 +189,10 @@ static int __init ms02nv_init_one(ulong addr) mp->resource.user_ram = user_res; /* Control and status register. */ - csr_res = kmalloc(sizeof(*csr_res), GFP_KERNEL); + csr_res = kzalloc(sizeof(*csr_res), GFP_KERNEL); if (!csr_res) goto err_out_user_res; - memset(csr_res, 0, sizeof(*csr_res)); csr_res->name = ms02nv_res_csr; csr_res->start = addr + MS02NV_CSR; csr_res->end = addr + MS02NV_CSR + 3; diff --git a/drivers/mtd/devices/phram.c b/drivers/mtd/devices/phram.c index 6c7337f..56cc1ca 100644 --- a/drivers/mtd/devices/phram.c +++ b/drivers/mtd/devices/phram.c @@ -126,12 +126,10 @@ static int register_device(char *name, unsigned long start, unsigned long len) struct phram_mtd_list *new; int ret = -ENOMEM; - new = kmalloc(sizeof(*new), GFP_KERNEL); + new = kzalloc(sizeof(*new), GFP_KERNEL); if (!new) goto out0; - memset(new, 0, sizeof(*new)); - ret = -EIO; new->mtd.priv = ioremap(start, len); if (!new->mtd.priv) { diff --git a/drivers/mtd/devices/slram.c b/drivers/mtd/devices/slram.c index 542a0c0..5f49248 100644 --- a/drivers/mtd/devices/slram.c +++ b/drivers/mtd/devices/slram.c @@ -168,19 +168,16 @@ static int register_device(char *name, unsigned long start, unsigned long length E("slram: Cannot allocate new MTD device.\n"); return(-ENOMEM); } - (*curmtd)->mtdinfo = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); + (*curmtd)->mtdinfo = kzalloc(sizeof(struct mtd_info), GFP_KERNEL); (*curmtd)->next = NULL; if ((*curmtd)->mtdinfo) { - memset((char *)(*curmtd)->mtdinfo, 0, sizeof(struct mtd_info)); (*curmtd)->mtdinfo->priv = - kmalloc(sizeof(slram_priv_t), GFP_KERNEL); + kzalloc(sizeof(slram_priv_t), GFP_KERNEL); if (!(*curmtd)->mtdinfo->priv) { kfree((*curmtd)->mtdinfo); (*curmtd)->mtdinfo = NULL; - } else { - memset((*curmtd)->mtdinfo->priv,0,sizeof(slram_priv_t)); } } diff --git a/drivers/mtd/ftl.c b/drivers/mtd/ftl.c index da39355..24235d4 100644 --- a/drivers/mtd/ftl.c +++ b/drivers/mtd/ftl.c @@ -1033,7 +1033,7 @@ static void ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) { partition_t *partition; - partition = kmalloc(sizeof(partition_t), GFP_KERNEL); + partition = kzalloc(sizeof(partition_t), GFP_KERNEL); if (!partition) { printk(KERN_WARNING "No memory to scan for FTL on %s\n", @@ -1041,8 +1041,6 @@ static void ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) return; } - memset(partition, 0, sizeof(partition_t)); - partition->mbd.mtd = mtd; if ((scan_header(partition) == 0) && diff --git a/drivers/mtd/inftlcore.c b/drivers/mtd/inftlcore.c index d2f54c0..b0e3965 100644 --- a/drivers/mtd/inftlcore.c +++ b/drivers/mtd/inftlcore.c @@ -67,13 +67,12 @@ static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) DEBUG(MTD_DEBUG_LEVEL3, "INFTL: add_mtd for %s\n", mtd->name); - inftl = kmalloc(sizeof(*inftl), GFP_KERNEL); + inftl = kzalloc(sizeof(*inftl), GFP_KERNEL); if (!inftl) { printk(KERN_WARNING "INFTL: Out of memory for data structures\n"); return; } - memset(inftl, 0, sizeof(*inftl)); inftl->mbd.mtd = mtd; inftl->mbd.devnum = -1; diff --git a/drivers/mtd/maps/ceiva.c b/drivers/mtd/maps/ceiva.c index 0402c21..629e6e2 100644 --- a/drivers/mtd/maps/ceiva.c +++ b/drivers/mtd/maps/ceiva.c @@ -122,10 +122,9 @@ static int __init clps_setup_mtd(struct clps_info *clps, int nr, struct mtd_info /* * Allocate the map_info structs in one go. */ - maps = kmalloc(sizeof(struct map_info) * nr, GFP_KERNEL); + maps = kzalloc(sizeof(struct map_info) * nr, GFP_KERNEL); if (!maps) return -ENOMEM; - memset(maps, 0, sizeof(struct map_info) * nr); /* * Claim and then map the memory regions. */ diff --git a/drivers/mtd/maps/integrator-flash.c b/drivers/mtd/maps/integrator-flash.c index c8db01b..6946d80 100644 --- a/drivers/mtd/maps/integrator-flash.c +++ b/drivers/mtd/maps/integrator-flash.c @@ -75,14 +75,12 @@ static int armflash_probe(struct platform_device *dev) int err; void __iomem *base; - info = kmalloc(sizeof(struct armflash_info), GFP_KERNEL); + info = kzalloc(sizeof(struct armflash_info), GFP_KERNEL); if (!info) { err = -ENOMEM; goto out; } - memset(info, 0, sizeof(struct armflash_info)); - info->plat = plat; if (plat && plat->init) { err = plat->init(); diff --git a/drivers/mtd/maps/omap_nor.c b/drivers/mtd/maps/omap_nor.c index 418afff..e8d9ae5 100644 --- a/drivers/mtd/maps/omap_nor.c +++ b/drivers/mtd/maps/omap_nor.c @@ -78,12 +78,10 @@ static int __devinit omapflash_probe(struct platform_device *pdev) struct resource *res = pdev->resource; unsigned long size = res->end - res->start + 1; - info = kmalloc(sizeof(struct omapflash_info), GFP_KERNEL); + info = kzalloc(sizeof(struct omapflash_info), GFP_KERNEL); if (!info) return -ENOMEM; - memset(info, 0, sizeof(struct omapflash_info)); - if (!request_mem_region(res->start, size, "flash")) { err = -EBUSY; goto out_free_info; diff --git a/drivers/mtd/maps/pcmciamtd.c b/drivers/mtd/maps/pcmciamtd.c index 995347b..eaeb56a 100644 --- a/drivers/mtd/maps/pcmciamtd.c +++ b/drivers/mtd/maps/pcmciamtd.c @@ -735,11 +735,10 @@ static int pcmciamtd_probe(struct pcmcia_device *link) struct pcmciamtd_dev *dev; /* Create new memory card device */ - dev = kmalloc(sizeof(*dev), GFP_KERNEL); + dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) return -ENOMEM; DEBUG(1, "dev=0x%p", dev); - memset(dev, 0, sizeof(*dev)); dev->p_dev = link; link->priv = dev; diff --git a/drivers/mtd/maps/physmap.c b/drivers/mtd/maps/physmap.c index bb67c50..28c5ffd 100644 --- a/drivers/mtd/maps/physmap.c +++ b/drivers/mtd/maps/physmap.c @@ -92,12 +92,11 @@ static int physmap_flash_probe(struct platform_device *dev) (unsigned long long)(dev->resource->end - dev->resource->start + 1), (unsigned long long)dev->resource->start); - info = kmalloc(sizeof(struct physmap_flash_info), GFP_KERNEL); + info = kzalloc(sizeof(struct physmap_flash_info), GFP_KERNEL); if (info == NULL) { err = -ENOMEM; goto err_out; } - memset(info, 0, sizeof(*info)); platform_set_drvdata(dev, info); diff --git a/drivers/mtd/maps/plat-ram.c b/drivers/mtd/maps/plat-ram.c index 5d3c754..2b6504e 100644 --- a/drivers/mtd/maps/plat-ram.c +++ b/drivers/mtd/maps/plat-ram.c @@ -147,14 +147,13 @@ static int platram_probe(struct platform_device *pdev) pdata = pdev->dev.platform_data; - info = kmalloc(sizeof(*info), GFP_KERNEL); + info = kzalloc(sizeof(*info), GFP_KERNEL); if (info == NULL) { dev_err(&pdev->dev, "no memory for flash info\n"); err = -ENOMEM; goto exit_error; } - memset(info, 0, sizeof(*info)); platform_set_drvdata(pdev, info); info->dev = &pdev->dev; diff --git a/drivers/mtd/maps/sa1100-flash.c b/drivers/mtd/maps/sa1100-flash.c index 950bf1c..f904e6b 100644 --- a/drivers/mtd/maps/sa1100-flash.c +++ b/drivers/mtd/maps/sa1100-flash.c @@ -273,14 +273,12 @@ sa1100_setup_mtd(struct platform_device *pdev, struct flash_platform_data *plat) /* * Allocate the map_info structs in one go. */ - info = kmalloc(size, GFP_KERNEL); + info = kzalloc(size, GFP_KERNEL); if (!info) { ret = -ENOMEM; goto out; } - memset(info, 0, size); - if (plat->init) { ret = plat->init(); if (ret) diff --git a/drivers/mtd/maps/tqm834x.c b/drivers/mtd/maps/tqm834x.c index 58e5912..9adc970 100644 --- a/drivers/mtd/maps/tqm834x.c +++ b/drivers/mtd/maps/tqm834x.c @@ -132,20 +132,16 @@ static int __init init_tqm834x_mtd(void) pr_debug("%s: chip probing count %d\n", __FUNCTION__, idx); - map_banks[idx] = - (struct map_info *)kmalloc(sizeof(struct map_info), - GFP_KERNEL); + map_banks[idx] = kzalloc(sizeof(struct map_info), GFP_KERNEL); if (map_banks[idx] == NULL) { ret = -ENOMEM; goto error_mem; } - memset((void *)map_banks[idx], 0, sizeof(struct map_info)); - map_banks[idx]->name = (char *)kmalloc(16, GFP_KERNEL); + map_banks[idx]->name = kzalloc(16, GFP_KERNEL); if (map_banks[idx]->name == NULL) { ret = -ENOMEM; goto error_mem; } - memset((void *)map_banks[idx]->name, 0, 16); sprintf(map_banks[idx]->name, "TQM834x-%d", idx); map_banks[idx]->size = flash_size; diff --git a/drivers/mtd/maps/tqm8xxl.c b/drivers/mtd/maps/tqm8xxl.c index 19578ba..37e4ded 100644 --- a/drivers/mtd/maps/tqm8xxl.c +++ b/drivers/mtd/maps/tqm8xxl.c @@ -134,14 +134,13 @@ int __init init_tqm_mtd(void) printk(KERN_INFO "%s: chip probing count %d\n", __FUNCTION__, idx); - map_banks[idx] = (struct map_info *)kmalloc(sizeof(struct map_info), GFP_KERNEL); + map_banks[idx] = kzalloc(sizeof(struct map_info), GFP_KERNEL); if(map_banks[idx] == NULL) { ret = -ENOMEM; /* FIXME: What if some MTD devices were probed already? */ goto error_mem; } - memset((void *)map_banks[idx], 0, sizeof(struct map_info)); map_banks[idx]->name = (char *)kmalloc(16, GFP_KERNEL); if (!map_banks[idx]->name) { diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index b5d62cb..b879a66 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -373,12 +373,10 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr) if (!blktrans_notifier.list.next) register_mtd_user(&blktrans_notifier); - tr->blkcore_priv = kmalloc(sizeof(*tr->blkcore_priv), GFP_KERNEL); + tr->blkcore_priv = kzalloc(sizeof(*tr->blkcore_priv), GFP_KERNEL); if (!tr->blkcore_priv) return -ENOMEM; - memset(tr->blkcore_priv, 0, sizeof(*tr->blkcore_priv)); - mutex_lock(&mtd_table_mutex); ret = register_blkdev(tr->major, tr->name); diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c index a052648..952da30 100644 --- a/drivers/mtd/mtdblock.c +++ b/drivers/mtd/mtdblock.c @@ -278,11 +278,10 @@ static int mtdblock_open(struct mtd_blktrans_dev *mbd) } /* OK, it's not open. Create cache info for it */ - mtdblk = kmalloc(sizeof(struct mtdblk_dev), GFP_KERNEL); + mtdblk = kzalloc(sizeof(struct mtdblk_dev), GFP_KERNEL); if (!mtdblk) return -ENOMEM; - memset(mtdblk, 0, sizeof(*mtdblk)); mtdblk->count = 1; mtdblk->mtd = mtd; @@ -339,13 +338,11 @@ static int mtdblock_flush(struct mtd_blktrans_dev *dev) static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) { - struct mtd_blktrans_dev *dev = kmalloc(sizeof(*dev), GFP_KERNEL); + struct mtd_blktrans_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) return; - memset(dev, 0, sizeof(*dev)); - dev->mtd = mtd; dev->devnum = mtd->index; diff --git a/drivers/mtd/mtdblock_ro.c b/drivers/mtd/mtdblock_ro.c index 642ccc6..f79dbb4 100644 --- a/drivers/mtd/mtdblock_ro.c +++ b/drivers/mtd/mtdblock_ro.c @@ -33,13 +33,11 @@ static int mtdblock_writesect(struct mtd_blktrans_dev *dev, static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) { - struct mtd_blktrans_dev *dev = kmalloc(sizeof(*dev), GFP_KERNEL); + struct mtd_blktrans_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) return; - memset(dev, 0, sizeof(*dev)); - dev->mtd = mtd; dev->devnum = mtd->index; diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 07618f5..7c4adc6 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -431,7 +431,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, if(!(file->f_mode & 2)) return -EPERM; - erase=kmalloc(sizeof(struct erase_info),GFP_KERNEL); + erase=kzalloc(sizeof(struct erase_info),GFP_KERNEL); if (!erase) ret = -ENOMEM; else { @@ -440,7 +440,6 @@ static int mtd_ioctl(struct inode *inode, struct file *file, init_waitqueue_head(&waitq); - memset (erase,0,sizeof(struct erase_info)); if (copy_from_user(&erase->addr, argp, sizeof(struct erase_info_user))) { kfree(erase); diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index cf927a8..ec51483 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -708,14 +708,13 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c /* allocate the device structure */ size = SIZEOF_STRUCT_MTD_CONCAT(num_devs); - concat = kmalloc(size, GFP_KERNEL); + concat = kzalloc(size, GFP_KERNEL); if (!concat) { printk ("memory allocation error while creating concatenated device \"%s\"\n", name); return NULL; } - memset(concat, 0, size); concat->subdev = (struct mtd_info **) (concat + 1); /* diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index a20f75f..89692f8 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -323,14 +323,13 @@ int add_mtd_partitions(struct mtd_info *master, for (i = 0; i < nbparts; i++) { /* allocate the partition structure */ - slave = kmalloc (sizeof(*slave), GFP_KERNEL); + slave = kzalloc (sizeof(*slave), GFP_KERNEL); if (!slave) { printk ("memory allocation error while creating partitions for \"%s\"\n", master->name); del_mtd_partitions(master); return -ENOMEM; } - memset(slave, 0, sizeof(*slave)); list_add(&slave->list, &mtd_partitions); /* set up the MTD object for this partition */ diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index 6107f53..12608c1 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -1635,13 +1635,12 @@ static int __init doc_probe(unsigned long physadr) len = sizeof(struct mtd_info) + sizeof(struct nand_chip) + sizeof(struct doc_priv) + (2 * sizeof(struct nand_bbt_descr)); - mtd = kmalloc(len, GFP_KERNEL); + mtd = kzalloc(len, GFP_KERNEL); if (!mtd) { printk(KERN_ERR "DiskOnChip kmalloc (%d bytes) failed!\n", len); ret = -ENOMEM; goto fail; } - memset(mtd, 0, len); nand = (struct nand_chip *) (mtd + 1); doc = (struct doc_priv *) (nand + 1); diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 4e74fe9..5e121ce 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -960,14 +960,12 @@ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) struct nand_bbt_descr *md = this->bbt_md; len = mtd->size >> (this->bbt_erase_shift + 2); - /* Allocate memory (2bit per block) */ - this->bbt = kmalloc(len, GFP_KERNEL); + /* Allocate memory (2bit per block) and clear the memory bad block table */ + this->bbt = kzalloc(len, GFP_KERNEL); if (!this->bbt) { printk(KERN_ERR "nand_scan_bbt: Out of memory\n"); return -ENOMEM; } - /* Clear the memory bad block table */ - memset(this->bbt, 0x00, len); /* If no primary table decriptor is given, scan the device * to build a memory based bad block table diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index abebcab..3d39451 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -1511,14 +1511,12 @@ static int __init ns_init_module(void) } /* Allocate and initialize mtd_info, nand_chip and nandsim structures */ - nsmtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip) + nsmtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip) + sizeof(struct nandsim), GFP_KERNEL); if (!nsmtd) { NS_ERR("unable to allocate core structures.\n"); return -ENOMEM; } - memset(nsmtd, 0, sizeof(struct mtd_info) + sizeof(struct nand_chip) + - sizeof(struct nandsim)); chip = (struct nand_chip *)(nsmtd + 1); nsmtd->priv = (void *)chip; nand = (struct nandsim *)(chip + 1); diff --git a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c index f4d3854..4b1ba4f 100644 --- a/drivers/mtd/nftlcore.c +++ b/drivers/mtd/nftlcore.c @@ -57,13 +57,12 @@ static void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) DEBUG(MTD_DEBUG_LEVEL1, "NFTL: add_mtd for %s\n", mtd->name); - nftl = kmalloc(sizeof(struct NFTLrecord), GFP_KERNEL); + nftl = kzalloc(sizeof(struct NFTLrecord), GFP_KERNEL); if (!nftl) { printk(KERN_WARNING "NFTL: out of memory for data structures\n"); return; } - memset(nftl, 0, sizeof(*nftl)); nftl->mbd.mtd = mtd; nftl->mbd.devnum = -1; diff --git a/drivers/mtd/onenand/generic.c b/drivers/mtd/onenand/generic.c index af06a80..53eb563 100644 --- a/drivers/mtd/onenand/generic.c +++ b/drivers/mtd/onenand/generic.c @@ -45,12 +45,10 @@ static int __devinit generic_onenand_probe(struct device *dev) unsigned long size = res->end - res->start + 1; int err; - info = kmalloc(sizeof(struct onenand_info), GFP_KERNEL); + info = kzalloc(sizeof(struct onenand_info), GFP_KERNEL); if (!info) return -ENOMEM; - memset(info, 0, sizeof(struct onenand_info)); - if (!request_mem_region(res->start, size, dev->driver->name)) { err = -EBUSY; goto out_free_info; diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c index 1b00dac..e3822c1 100644 --- a/drivers/mtd/onenand/onenand_bbt.c +++ b/drivers/mtd/onenand/onenand_bbt.c @@ -177,14 +177,12 @@ int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) int len, ret = 0; len = mtd->size >> (this->erase_shift + 2); - /* Allocate memory (2bit per block) */ - bbm->bbt = kmalloc(len, GFP_KERNEL); + /* Allocate memory (2bit per block) and clear the memory bad block table */ + bbm->bbt = kzalloc(len, GFP_KERNEL); if (!bbm->bbt) { printk(KERN_ERR "onenand_scan_bbt: Out of memory\n"); return -ENOMEM; } - /* Clear the memory bad block table */ - memset(bbm->bbt, 0x00, len); /* Set the bad block position */ bbm->badblockpos = ONENAND_BADBLOCK_POS; @@ -230,14 +228,12 @@ int onenand_default_bbt(struct mtd_info *mtd) struct onenand_chip *this = mtd->priv; struct bbm_info *bbm; - this->bbm = kmalloc(sizeof(struct bbm_info), GFP_KERNEL); + this->bbm = kzalloc(sizeof(struct bbm_info), GFP_KERNEL); if (!this->bbm) return -ENOMEM; bbm = this->bbm; - memset(bbm, 0, sizeof(struct bbm_info)); - /* 1KB page has same configuration as 2KB page */ if (!bbm->badblock_pattern) bbm->badblock_pattern = &largepage_memorybased; diff --git a/drivers/mtd/redboot.c b/drivers/mtd/redboot.c index 5b58523..4b27721 100644 --- a/drivers/mtd/redboot.c +++ b/drivers/mtd/redboot.c @@ -165,15 +165,13 @@ static int parse_redboot_partitions(struct mtd_info *master, } } #endif - parts = kmalloc(sizeof(*parts)*nrparts + nulllen + namelen, GFP_KERNEL); + parts = kzalloc(sizeof(*parts)*nrparts + nulllen + namelen, GFP_KERNEL); if (!parts) { ret = -ENOMEM; goto out; } - memset(parts, 0, sizeof(*parts)*nrparts + nulllen + namelen); - nullname = (char *)&parts[nrparts]; #ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED if (nulllen > 0) { -- cgit v0.10.2 From 28bdd4a72d158083b7c7f56ec4ef5dfaf75d9464 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 29 Nov 2006 00:04:59 +0000 Subject: =?UTF-8?q?[MTD]=20[NAND]=20Update=20CAF=C3=89=20driver=20interrup?= =?UTF-8?q?t=20handler=20prototype?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/cafe.c b/drivers/mtd/nand/cafe.c index fad304b..c0a5ec1 100644 --- a/drivers/mtd/nand/cafe.c +++ b/drivers/mtd/nand/cafe.c @@ -312,7 +312,7 @@ static void cafe_select_chip(struct mtd_info *mtd, int chipnr) // cafe_dev_dbg(&cafe->pdev->dev, "select_chip %d\n", chipnr); } -static int cafe_nand_interrupt(int irq, void *id, struct pt_regs *regs) +static int cafe_nand_interrupt(int irq, void *id) { struct mtd_info *mtd = id; struct cafe_priv *cafe = mtd->priv; -- cgit v0.10.2 From fc029194999a4563d356cdf728e0c44fb7a49105 Mon Sep 17 00:00:00 2001 From: Timo Lindhorst Date: Mon, 27 Nov 2006 13:35:49 +0100 Subject: [MTD] [NAND] fix ifdef option in nand_ecc.c Fix up the config option in the #ifdef statements in nand_ecc.c Signed-off-by: Timo Lindhorst Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c index dd438ca..fde593e 100644 --- a/drivers/mtd/nand/nand_ecc.c +++ b/drivers/mtd/nand/nand_ecc.c @@ -112,7 +112,7 @@ int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, tmp2 |= (reg2 & 0x01) << 0; /* B7 -> B0 */ /* Calculate final ECC code */ -#ifdef CONFIG_NAND_ECC_SMC +#ifdef CONFIG_MTD_NAND_ECC_SMC ecc_code[0] = ~tmp2; ecc_code[1] = ~tmp1; #else @@ -148,7 +148,7 @@ int nand_correct_data(struct mtd_info *mtd, u_char *dat, { uint8_t s0, s1, s2; -#ifdef CONFIG_NAND_ECC_SMC +#ifdef CONFIG_MTD_NAND_ECC_SMC s0 = calc_ecc[0] ^ read_ecc[0]; s1 = calc_ecc[1] ^ read_ecc[1]; s2 = calc_ecc[2] ^ read_ecc[2]; -- cgit v0.10.2 From ce1060494a205d528aa72fea23bdae607396e621 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Wed, 29 Nov 2006 00:19:14 +0000 Subject: [MTD] Tidy bitrev usage in rtc_from4.c Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/rtc_from4.c b/drivers/mtd/nand/rtc_from4.c index 4a83a7d..02deec3 100644 --- a/drivers/mtd/nand/rtc_from4.c +++ b/drivers/mtd/nand/rtc_from4.c @@ -357,7 +357,7 @@ static int rtc_from4_correct_data(struct mtd_info *mtd, const u_char *buf, u_cha /* Read the syndrom pattern from the FPGA and correct the bitorder */ rs_ecc = (volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECC); for (i = 0; i < 8; i++) { - ecc[i] = byte_rev_table[(*rs_ecc) & 0xFF]; + ecc[i] = bitrev8(*rs_ecc); rs_ecc++; } -- cgit v0.10.2 From c0fe10aef354912c38c43991dd38c16f1828cfe3 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 13 Nov 2006 13:47:43 +0200 Subject: [MTD] increase MAX_MTD_DEVICES Signed-off-by: Artem Bityutskiy diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 4fc391e..e34bbc9 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -23,7 +23,7 @@ #define MTD_CHAR_MAJOR 90 #define MTD_BLOCK_MAJOR 31 -#define MAX_MTD_DEVICES 16 +#define MAX_MTD_DEVICES 32 #define MTD_ERASE_PENDING 0x01 #define MTD_ERASING 0x02 -- cgit v0.10.2 From eb6cf7bb71baa109041c04357b930a0c0bfa0db7 Mon Sep 17 00:00:00 2001 From: Yoichi Yuasa Date: Wed, 25 Oct 2006 23:29:17 +0900 Subject: [MTD] fix map probe name for cstm_mips_ixx This patch has fixed name of map probe for cstm_mips_ixx.c Signed-off-by: Yoichi Yuasa Signed-off-by: Artem Bityutskiy diff --git a/drivers/mtd/maps/cstm_mips_ixx.c b/drivers/mtd/maps/cstm_mips_ixx.c index d57eba2..2ef22a5 100644 --- a/drivers/mtd/maps/cstm_mips_ixx.c +++ b/drivers/mtd/maps/cstm_mips_ixx.c @@ -115,7 +115,7 @@ int __init init_cstm_mips_ixx(void) //printk(KERN_NOTICE "phymap %d cfi_probe: mymtd is %x\n",i,(unsigned int)mymtd); if (!mymtd) { jedec = 1; - mymtd = (struct mtd_info *)do_map_probe("jedec", &cstm_mips_ixx_map[i]); + mymtd = (struct mtd_info *)do_map_probe("jedec_probe", &cstm_mips_ixx_map[i]); printk(KERN_NOTICE "cstm_mips_ixx %d jedec: mymtd is %x\n",i,(unsigned int)mymtd); } if (mymtd) { -- cgit v0.10.2 From f6a7ecb18dabd88bd9f28e7bece564cabe8ffe82 Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Mon, 20 Nov 2006 20:15:36 -0600 Subject: [MTD] add MTD_BLKDEVS Kconfig option Add a MTD_BLKDEVS Kconfig option to cleanup the makefile a bit Signed-off-by: Josh Boyer Signed-off-by: Artem Bityutskiy diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 291660a..26f75c2 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -164,9 +164,15 @@ config MTD_CHAR memory chips, and also use ioctl() to obtain information about the device, or to erase parts of it. +config MTD_BLKDEVS + tristate "Common interface to block layer for MTD 'translation layers'" + depends on MTD && BLOCK + default n + config MTD_BLOCK tristate "Caching block device access to MTD devices" depends on MTD && BLOCK + select MTD_BLKDEVS ---help--- Although most flash chips have an erase size too large to be useful as block devices, it is possible to use MTD devices which are based @@ -189,6 +195,7 @@ config MTD_BLOCK config MTD_BLOCK_RO tristate "Readonly block device access to MTD devices" depends on MTD_BLOCK!=y && MTD && BLOCK + select MTD_BLKDEVS help This allows you to mount read-only file systems (such as cramfs) from an MTD device, without the overhead (and danger) of the caching @@ -200,6 +207,7 @@ config MTD_BLOCK_RO config FTL tristate "FTL (Flash Translation Layer) support" depends on MTD && BLOCK + select MTD_BLKDEVS ---help--- This provides support for the original Flash Translation Layer which is part of the PCMCIA specification. It uses a kind of pseudo- @@ -216,6 +224,7 @@ config FTL config NFTL tristate "NFTL (NAND Flash Translation Layer) support" depends on MTD && BLOCK + select MTD_BLKDEVS ---help--- This provides support for the NAND Flash Translation Layer which is used on M-Systems' DiskOnChip devices. It uses a kind of pseudo- @@ -239,6 +248,7 @@ config NFTL_RW config INFTL tristate "INFTL (Inverse NAND Flash Translation Layer) support" depends on MTD && BLOCK + select MTD_BLKDEVS ---help--- This provides support for the Inverse NAND Flash Translation Layer which is used on M-Systems' newer DiskOnChip devices. It @@ -256,6 +266,7 @@ config INFTL config RFD_FTL tristate "Resident Flash Disk (Flash Translation Layer) support" depends on MTD && BLOCK + select MTD_BLKDEVS ---help--- This provides support for the flash translation layer known as the Resident Flash Disk (RFD), as used by the Embedded BIOS @@ -266,6 +277,7 @@ config RFD_FTL config SSFDC tristate "NAND SSFDC (SmartMedia) read only translation layer" depends on MTD && BLOCK + select MTD_BLKDEVS help This enables read only access to SmartMedia formatted NAND flash. You can mount it with FAT file system. diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 1e36b9a..c130e62 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -15,13 +15,14 @@ obj-$(CONFIG_MTD_AFS_PARTS) += afs.o # 'Users' - code which presents functionality to userspace. obj-$(CONFIG_MTD_CHAR) += mtdchar.o -obj-$(CONFIG_MTD_BLOCK) += mtdblock.o mtd_blkdevs.o -obj-$(CONFIG_MTD_BLOCK_RO) += mtdblock_ro.o mtd_blkdevs.o -obj-$(CONFIG_FTL) += ftl.o mtd_blkdevs.o -obj-$(CONFIG_NFTL) += nftl.o mtd_blkdevs.o -obj-$(CONFIG_INFTL) += inftl.o mtd_blkdevs.o -obj-$(CONFIG_RFD_FTL) += rfd_ftl.o mtd_blkdevs.o -obj-$(CONFIG_SSFDC) += ssfdc.o mtd_blkdevs.o +obj-$(CONFIG_MTD_BLKDEVS) += mtd_blkdevs.o +obj-$(CONFIG_MTD_BLOCK) += mtdblock.o +obj-$(CONFIG_MTD_BLOCK_RO) += mtdblock_ro.o +obj-$(CONFIG_FTL) += ftl.o +obj-$(CONFIG_NFTL) += nftl.o +obj-$(CONFIG_INFTL) += inftl.o +obj-$(CONFIG_RFD_FTL) += rfd_ftl.o +obj-$(CONFIG_SSFDC) += ssfdc.o nftl-objs := nftlcore.o nftlmount.o inftl-objs := inftlcore.o inftlmount.o -- cgit v0.10.2 From 29072b96078ffde36f03d51e6b5d0cff1ba8c7df Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 28 Sep 2006 15:38:36 +0200 Subject: [MTD] NAND: add subpage write support Many SLC NANDs support up to 4 writes at one NAND page. Add support of this feature. Signed-off-by: Artem Bityutskiy diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index ec51483..0690268 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -772,6 +772,7 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c concat->mtd.ecc_stats.badblocks += subdev[i]->ecc_stats.badblocks; if (concat->mtd.writesize != subdev[i]->writesize || + concat->mtd.subpage_sft != subdev[i]->subpage_sft || concat->mtd.oobsize != subdev[i]->oobsize || concat->mtd.ecctype != subdev[i]->ecctype || concat->mtd.eccsize != subdev[i]->eccsize || diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 89692f8..bafd2fb 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -340,6 +340,7 @@ int add_mtd_partitions(struct mtd_info *master, slave->mtd.oobsize = master->oobsize; slave->mtd.ecctype = master->ecctype; slave->mtd.eccsize = master->eccsize; + slave->mtd.subpage_sft = master->subpage_sft; slave->mtd.name = parts[i].name; slave->mtd.bank_size = master->bank_size; diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 5dcb2e0..eed3271 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1590,7 +1590,7 @@ static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, return NULL; } -#define NOTALIGNED(x) (x & (mtd->writesize-1)) != 0 +#define NOTALIGNED(x) (x & (chip->subpagesize - 1)) != 0 /** * nand_do_write_ops - [Internal] NAND write with ECC @@ -1603,15 +1603,16 @@ static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) { - int chipnr, realpage, page, blockmask; + int chipnr, realpage, page, blockmask, column; struct nand_chip *chip = mtd->priv; uint32_t writelen = ops->len; uint8_t *oob = ops->oobbuf; uint8_t *buf = ops->datbuf; - int bytes = mtd->writesize; - int ret; + int ret, subpage; ops->retlen = 0; + if (!writelen) + return 0; /* reject writes, which are not page aligned */ if (NOTALIGNED(to) || NOTALIGNED(ops->len)) { @@ -1620,8 +1621,11 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, return -EINVAL; } - if (!writelen) - return 0; + column = to & (mtd->writesize - 1); + subpage = column || (writelen & (mtd->writesize - 1)); + + if (subpage && oob) + return -EINVAL; chipnr = (int)(to >> chip->chip_shift); chip->select_chip(mtd, chipnr); @@ -1644,12 +1648,24 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, memset(chip->oob_poi, 0xff, mtd->oobsize); while(1) { + int bytes = mtd->writesize; int cached = writelen > bytes && page != blockmask; + uint8_t *wbuf = buf; + + /* Partial page write ? */ + if (unlikely(column || writelen < (mtd->writesize - 1))) { + cached = 0; + bytes = min_t(int, bytes - column, (int) writelen); + chip->pagebuf = -1; + memset(chip->buffers->databuf, 0xff, mtd->writesize); + memcpy(&chip->buffers->databuf[column], buf, bytes); + wbuf = chip->buffers->databuf; + } if (unlikely(oob)) oob = nand_fill_oob(chip, oob, ops); - ret = chip->write_page(mtd, chip, buf, page, cached, + ret = chip->write_page(mtd, chip, wbuf, page, cached, (ops->mode == MTD_OOB_RAW)); if (ret) break; @@ -1658,6 +1674,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, if (!writelen) break; + column = 0; buf += bytes; realpage++; @@ -2201,8 +2218,8 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, /* Newer devices have all the information in additional id bytes */ if (!type->pagesize) { int extid; - /* The 3rd id byte contains non relevant data ATM */ - extid = chip->read_byte(mtd); + /* The 3rd id byte holds MLC / multichip data */ + chip->cellinfo = chip->read_byte(mtd); /* The 4th id byte is the important one */ extid = chip->read_byte(mtd); /* Calc pagesize */ @@ -2482,6 +2499,24 @@ int nand_scan_tail(struct mtd_info *mtd) } chip->ecc.total = chip->ecc.steps * chip->ecc.bytes; + /* + * Allow subpage writes up to ecc.steps. Not possible for MLC + * FLASH. + */ + if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && + !(chip->cellinfo & NAND_CI_CELLTYPE_MSK)) { + switch(chip->ecc.steps) { + case 2: + mtd->subpage_sft = 1; + break; + case 4: + case 8: + mtd->subpage_sft = 2; + break; + } + } + chip->subpagesize = mtd->writesize >> mtd->subpage_sft; + /* Initialize state */ chip->state = FL_READY; diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index e34bbc9..18acb6d 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -200,6 +200,8 @@ struct mtd_info { /* ECC status information */ struct mtd_ecc_stats ecc_stats; + /* Subpage shift (NAND) */ + int subpage_sft; void *priv; diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 6fc3e07..1aeedf2 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -166,6 +166,9 @@ typedef enum { * for all large page devices, as they do not support * autoincrement.*/ #define NAND_NO_READRDY 0x00000100 +/* Chip does not allow subpage writes */ +#define NAND_NO_SUBPAGE_WRITE 0x00000200 + /* Options valid for Samsung large page devices */ #define NAND_SAMSUNG_LP_OPTIONS \ @@ -193,6 +196,9 @@ typedef enum { /* Nand scan has allocated controller struct */ #define NAND_CONTROLLER_ALLOC 0x80000000 +/* Cell info constants */ +#define NAND_CI_CHIPNR_MSK 0x03 +#define NAND_CI_CELLTYPE_MSK 0x0C /* * nand_state_t - chip states @@ -341,6 +347,7 @@ struct nand_buffers { * @chipsize: [INTERN] the size of one chip for multichip arrays * @pagemask: [INTERN] page number mask = number of (pages / chip) - 1 * @pagebuf: [INTERN] holds the pagenumber which is currently in data_buf + * @subpagesize: [INTERN] holds the subpagesize * @ecclayout: [REPLACEABLE] the default ecc placement scheme * @bbt: [INTERN] bad block table pointer * @bbt_td: [REPLACEABLE] bad block table descriptor for flash lookup @@ -388,6 +395,8 @@ struct nand_chip { unsigned long chipsize; int pagemask; int pagebuf; + int subpagesize; + uint8_t cellinfo; int badblockpos; nand_state_t state; -- cgit v0.10.2 From 7799308f34d3c3371a319559687c78c0f2506fcf Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Wed, 11 Oct 2006 14:52:44 +0300 Subject: [MTD] add get_mtd_device_nm() function This patch adds one more function to the MTD interface to make it possible to open MTD devices by their names, not only numbers. This is very handy in many situations. Also, MTD device number depend on load order and may vary, while names are fixed. Signed-off-by: Artem Bityutskiy diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index c4d26de..06ec9f8 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -223,6 +224,42 @@ struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num) return ret; } +/** + * get_mtd_device_nm - obtain a validated handle for an MTD device by + * device name + * @name: MTD device name to open + * + * This function returns MTD device description structure in case of + * success and an error code in case of failure. + */ + +struct mtd_info *get_mtd_device_nm(const char *name) +{ + int i; + struct mtd_info *mtd = ERR_PTR(-ENODEV); + + mutex_lock(&mtd_table_mutex); + + for (i = 0; i < MAX_MTD_DEVICES; i++) { + if (mtd_table[i] && !strcmp(name, mtd_table[i]->name)) { + mtd = mtd_table[i]; + break; + } + } + + if (i == MAX_MTD_DEVICES) + goto out_unlock; + + if (!try_module_get(mtd->owner)) + goto out_unlock; + + mtd->usecount++; + +out_unlock: + mutex_unlock(&mtd_table_mutex); + return mtd; +} + void put_mtd_device(struct mtd_info *mtd) { int c; @@ -267,6 +304,7 @@ int default_mtd_writev(struct mtd_info *mtd, const struct kvec *vecs, EXPORT_SYMBOL(add_mtd_device); EXPORT_SYMBOL(del_mtd_device); EXPORT_SYMBOL(get_mtd_device); +EXPORT_SYMBOL(get_mtd_device_nm); EXPORT_SYMBOL(put_mtd_device); EXPORT_SYMBOL(register_mtd_user); EXPORT_SYMBOL(unregister_mtd_user); diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 18acb6d..89e937d 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -216,6 +216,7 @@ extern int add_mtd_device(struct mtd_info *mtd); extern int del_mtd_device (struct mtd_info *mtd); extern struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num); +extern struct mtd_info *get_mtd_device_nm(const char *name); extern void put_mtd_device(struct mtd_info *mtd); -- cgit v0.10.2 From 9fe912cea32aec18f860c95e8574410b5892481b Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Wed, 11 Oct 2006 14:52:45 +0300 Subject: [MTD] add get and put methods This patch adds get_device() and put_device() methods to the MTD description structure (struct mtd_info). These methods are called by MTD whenever the MTD device is get or put. They are needed when the underlying driver is something smarter then just flash chip driver, for example UBI. Signed-off-by: Artem Bityutskiy diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 06ec9f8..f11f55f 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -214,12 +214,23 @@ struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num) ret = NULL; } - if (ret && !try_module_get(ret->owner)) + if (!ret) + goto out_unlock; + + if (!try_module_get(ret->owner)) { + ret = NULL; + goto out_unlock; + } + + if (ret->get_device && ret->get_device(ret)) { + module_put(ret->owner); ret = NULL; + goto out_unlock; + } - if (ret) - ret->usecount++; + ret->usecount++; +out_unlock: mutex_unlock(&mtd_table_mutex); return ret; } @@ -235,8 +246,8 @@ struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num) struct mtd_info *get_mtd_device_nm(const char *name) { - int i; - struct mtd_info *mtd = ERR_PTR(-ENODEV); + int i, err = -ENODEV; + struct mtd_info *mtd = NULL; mutex_lock(&mtd_table_mutex); @@ -247,17 +258,27 @@ struct mtd_info *get_mtd_device_nm(const char *name) } } - if (i == MAX_MTD_DEVICES) + if (!mtd) goto out_unlock; if (!try_module_get(mtd->owner)) goto out_unlock; + if (mtd->get_device) { + err = mtd->get_device(mtd); + if (err) + goto out_put; + } + mtd->usecount++; + mutex_unlock(&mtd_table_mutex); + return mtd; +out_put: + module_put(mtd->owner); out_unlock: mutex_unlock(&mtd_table_mutex); - return mtd; + return ERR_PTR(err); } void put_mtd_device(struct mtd_info *mtd) @@ -266,6 +287,8 @@ void put_mtd_device(struct mtd_info *mtd) mutex_lock(&mtd_table_mutex); c = --mtd->usecount; + if (mtd->put_device) + mtd->put_device(mtd); mutex_unlock(&mtd_table_mutex); BUG_ON(c < 0); diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 89e937d..d644e57 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -207,6 +207,13 @@ struct mtd_info { struct module *owner; int usecount; + + /* If the driver is something smart, like UBI, it may need to maintain + * its own reference counting. The below functions are only for driver. + * The driver may register its callbacks. These callbacks are not + * supposed to be called by MTD users */ + int (*get_device) (struct mtd_info *mtd); + void (*put_device) (struct mtd_info *mtd); }; -- cgit v0.10.2 From 9c74034f8fc5d93fbe5656421cbbdc4c76ddda28 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Wed, 11 Oct 2006 14:52:47 +0300 Subject: [MTD] return error code from get_mtd_device() get_mtd_device() returns NULL in case of any failure. Teach it to return an error code instead. Fix all users as well. Signed-off-by: Artem Bityutskiy diff --git a/drivers/mtd/maps/nettel.c b/drivers/mtd/maps/nettel.c index f9e8e5b..528e523 100644 --- a/drivers/mtd/maps/nettel.c +++ b/drivers/mtd/maps/nettel.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -178,7 +179,7 @@ int nettel_eraseconfig(void) init_waitqueue_head(&wait_q); mtd = get_mtd_device(NULL, 2); - if (mtd) { + if (!IS_ERR(mtd)) { nettel_erase.mtd = mtd; nettel_erase.callback = nettel_erasecallback; nettel_erase.callback = NULL; diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 7c4adc6..3013d08 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -100,8 +101,8 @@ static int mtd_open(struct inode *inode, struct file *file) mtd = get_mtd_device(NULL, devnum); - if (!mtd) - return -ENODEV; + if (IS_ERR(mtd)) + return PTR_ERR(mtd); if (MTD_ABSENT == mtd->type) { put_mtd_device(mtd); diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index f11f55f..60f237f 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -193,14 +193,14 @@ int unregister_mtd_user (struct mtd_notifier *old) * Given a number and NULL address, return the num'th entry in the device * table, if any. Given an address and num == -1, search the device table * for a device with that address and return if it's still present. Given - * both, return the num'th driver only if its address matches. Return NULL - * if not. + * both, return the num'th driver only if its address matches. Return + * error code if not. */ struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num) { struct mtd_info *ret = NULL; - int i; + int i, err = -ENODEV; mutex_lock(&mtd_table_mutex); @@ -217,22 +217,24 @@ struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num) if (!ret) goto out_unlock; - if (!try_module_get(ret->owner)) { - ret = NULL; + if (!try_module_get(ret->owner)) goto out_unlock; - } - if (ret->get_device && ret->get_device(ret)) { - module_put(ret->owner); - ret = NULL; - goto out_unlock; + if (ret->get_device) { + err = ret->get_device(ret); + if (err) + goto out_put; } ret->usecount++; + mutex_unlock(&mtd_table_mutex); + return ret; +out_put: + module_put(ret->owner); out_unlock: mutex_unlock(&mtd_table_mutex); - return ret; + return ERR_PTR(err); } /** diff --git a/fs/jffs/jffs_fm.c b/fs/jffs/jffs_fm.c index 29b68d9..6aab317 100644 --- a/fs/jffs/jffs_fm.c +++ b/fs/jffs/jffs_fm.c @@ -17,6 +17,7 @@ * */ #include +#include #include #include #include "jffs_fm.h" @@ -104,7 +105,7 @@ jffs_build_begin(struct jffs_control *c, int unit) mtd = get_mtd_device(NULL, unit); - if (!mtd) { + if (IS_ERR(mtd)) { kfree(fmc); DJM(no_jffs_fmcontrol--); return NULL; diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c index bc4b810..590f60a 100644 --- a/fs/jffs2/super.c +++ b/fs/jffs2/super.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -184,9 +185,9 @@ static int jffs2_get_sb_mtdnr(struct file_system_type *fs_type, struct mtd_info *mtd; mtd = get_mtd_device(NULL, mtdnr); - if (!mtd) { + if (IS_ERR(mtd)) { D1(printk(KERN_DEBUG "jffs2: MTD device #%u doesn't appear to exist\n", mtdnr)); - return -EINVAL; + return PTR_ERR(mtd); } return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd, mnt); @@ -221,7 +222,7 @@ static int jffs2_get_sb(struct file_system_type *fs_type, D1(printk(KERN_DEBUG "jffs2_get_sb(): mtd:%%s, name \"%s\"\n", dev_name+4)); for (mtdnr = 0; mtdnr < MAX_MTD_DEVICES; mtdnr++) { mtd = get_mtd_device(NULL, mtdnr); - if (mtd) { + if (!IS_ERR(mtd)) { if (!strcmp(mtd->name, dev_name+4)) return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd, mnt); put_mtd_device(mtd); -- cgit v0.10.2 From dd36f2673573fc027945d488342f2f70664f0448 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 29 Nov 2006 16:33:03 +0000 Subject: [MTD] Use EXPORT_SYMBOL_GPL() for exported symbols. While we're fixing up the newly-added symbol, change the neighbouring ones too, for consistency and also to reflect the author's interpretation of the GPL -- which is that _no_ non-GPL modules are permitted. The author always intended his code to be released under the GPL, and believes that any new interpretation of 'EXPORT_SYMBOL' as being any different from 'EXPORT_SYMBOL_GPL' is entirely invalid; the GPL requires that _all_ exports have the semantics of the new 'EXPORT_SYMBOL_GPL', which means the extra four characters are entirely redundant. But since those four extra characters trigger the check for illegal modules in a way that just EXPORT_SYMBOL does not, it's useful to change anyway. This action in no way indicates an admission that there is any legal distinction between the two states, and in particular does not indicate that the author believes that non-GPL modules may use symbols exported with EXPORT_SYMBOL alone. Signed-off-by: David Woodhouse diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 60f237f..7070110 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -298,7 +298,7 @@ void put_mtd_device(struct mtd_info *mtd) } /* default_mtd_writev - default mtd writev method for MTD devices that - * dont implement their own + * don't implement their own */ int default_mtd_writev(struct mtd_info *mtd, const struct kvec *vecs, @@ -326,14 +326,14 @@ int default_mtd_writev(struct mtd_info *mtd, const struct kvec *vecs, return ret; } -EXPORT_SYMBOL(add_mtd_device); -EXPORT_SYMBOL(del_mtd_device); -EXPORT_SYMBOL(get_mtd_device); -EXPORT_SYMBOL(get_mtd_device_nm); -EXPORT_SYMBOL(put_mtd_device); -EXPORT_SYMBOL(register_mtd_user); -EXPORT_SYMBOL(unregister_mtd_user); -EXPORT_SYMBOL(default_mtd_writev); +EXPORT_SYMBOL_GPL(add_mtd_device); +EXPORT_SYMBOL_GPL(del_mtd_device); +EXPORT_SYMBOL_GPL(get_mtd_device); +EXPORT_SYMBOL_GPL(get_mtd_device_nm); +EXPORT_SYMBOL_GPL(put_mtd_device); +EXPORT_SYMBOL_GPL(register_mtd_user); +EXPORT_SYMBOL_GPL(unregister_mtd_user); +EXPORT_SYMBOL_GPL(default_mtd_writev); #ifdef CONFIG_PROC_FS -- cgit v0.10.2 From c9ac5977299dd106ddb759e7e10035770dff185b Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 30 Nov 2006 08:17:38 +0000 Subject: [MTD] Remove trailing whitespace The newly-added cafe_ecc.c had a lot of it because of the way the lookup table was auto-generated; clean up the other files too while we're at it. Signed-off-by: David Woodhouse diff --git a/drivers/mtd/chips/gen_probe.c b/drivers/mtd/chips/gen_probe.c index 77843d5..2eb696d 100644 --- a/drivers/mtd/chips/gen_probe.c +++ b/drivers/mtd/chips/gen_probe.c @@ -40,7 +40,7 @@ struct mtd_info *mtd_do_chip_probe(struct map_info *map, struct chip_probe *cp) if (mtd) { if (mtd->size > map->size) { printk(KERN_WARNING "Reducing visibility of %ldKiB chip to %ldKiB\n", - (unsigned long)mtd->size >> 10, + (unsigned long)mtd->size >> 10, (unsigned long)map->size >> 10); mtd->size = map->size; } diff --git a/drivers/mtd/maps/bast-flash.c b/drivers/mtd/maps/bast-flash.c index e074bb6..fc3b267 100644 --- a/drivers/mtd/maps/bast-flash.c +++ b/drivers/mtd/maps/bast-flash.c @@ -131,7 +131,7 @@ static int bast_flash_probe(struct platform_device *pdev) info->map.phys = res->start; info->map.size = res->end - res->start + 1; - info->map.name = pdev->dev.bus_id; + info->map.name = pdev->dev.bus_id; info->map.bankwidth = 2; if (info->map.size > AREA_MAXSIZE) diff --git a/drivers/mtd/maps/ck804xrom.c b/drivers/mtd/maps/ck804xrom.c index c222b88..238d42e 100644 --- a/drivers/mtd/maps/ck804xrom.c +++ b/drivers/mtd/maps/ck804xrom.c @@ -26,7 +26,7 @@ #define ADDRESS_NAME_LEN 18 -#define ROM_PROBE_STEP_SIZE (64*1024) +#define ROM_PROBE_STEP_SIZE (64*1024) struct ck804xrom_window { void __iomem *virt; diff --git a/drivers/mtd/maps/nettel.c b/drivers/mtd/maps/nettel.c index 528e523..9f53c65 100644 --- a/drivers/mtd/maps/nettel.c +++ b/drivers/mtd/maps/nettel.c @@ -472,7 +472,7 @@ out_unmap2: iounmap(nettel_amd_map.virt); return(rc); - + } /****************************************************************************/ diff --git a/drivers/mtd/nand/cafe.c b/drivers/mtd/nand/cafe.c index c0a5ec1..b8d9b64 100644 --- a/drivers/mtd/nand/cafe.c +++ b/drivers/mtd/nand/cafe.c @@ -1,4 +1,4 @@ -/* +/* * Driver for One Laptop Per Child ‘CAFÉ’ controller, aka Marvell 88ALP01 * * Copyright © 2006 Red Hat, Inc. @@ -60,7 +60,6 @@ struct cafe_priv { int page_addr; dma_addr_t dmaaddr; unsigned char *dmabuf; - }; static int usedma = 1; @@ -217,7 +216,7 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, ctl1 |= ((adrbytes-1)|8) << 27; if (command == NAND_CMD_SEQIN || command == NAND_CMD_ERASE1) { - /* Ignore the first command of a pair; the hardware + /* Ignore the first command of a pair; the hardware deals with them both at once, later */ cafe->ctl1 = ctl1; cafe_dev_dbg(&cafe->pdev->dev, "Setup for delayed command, ctl1 %08x, dlen %x\n", @@ -231,7 +230,7 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, cafe_writel(cafe, cafe->ctl2 | 0x100 | NAND_CMD_READSTART, NAND_CTRL2); do_command: - cafe_dev_dbg(&cafe->pdev->dev, "dlen %x, ctl1 %x, ctl2 %x\n", + cafe_dev_dbg(&cafe->pdev->dev, "dlen %x, ctl1 %x, ctl2 %x\n", cafe->datalen, ctl1, cafe_readl(cafe, NAND_CTRL2)); /* NB: The datasheet lies -- we really should be subtracting 1 here */ @@ -380,7 +379,7 @@ static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, uint32_t tmp = cafe_readl(cafe, NAND_ECC_SYN01 + (i*2)); syn[i] = tmp & 0xfff; syn[i+1] = (tmp >> 16) & 0xfff; - } + } if ((i = cafe_correct_ecc(buf, syn)) < 0) { dev_dbg(&cafe->pdev->dev, "Failed to correct ECC at %08x\n", @@ -404,7 +403,7 @@ static struct nand_ecclayout cafe_oobinfo_2048 = { .oobfree = {{14, 50}} }; -/* Ick. The BBT code really ought to be able to work this bit out +/* Ick. The BBT code really ought to be able to work this bit out for itself from the above, at least for the 2KiB case */ static uint8_t cafe_bbt_pattern_2048[] = { 'B', 'b', 't', '0' }; static uint8_t cafe_mirror_pattern_2048[] = { '1', 't', 'b', 'B' }; @@ -579,7 +578,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, cafe->nand.options |= NAND_SKIP_BBTSCAN; cafe->nand.block_bad = cafe_nand_block_bad; } - + /* Start off by resetting the NAND controller completely */ cafe_writel(cafe, 1, NAND_RESET); cafe_writel(cafe, 0, NAND_RESET); @@ -600,7 +599,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, err = request_irq(pdev->irq, &cafe_nand_interrupt, SA_SHIRQ, "CAFE NAND", mtd); if (err) { dev_warn(&pdev->dev, "Could not register IRQ %d\n", pdev->irq); - + goto out_free_dma; } #if 1 @@ -654,7 +653,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, writel(0x84600070, cafe->mmio); udelay(10); cafe_dev_dbg(&cafe->pdev->dev, "Status %x\n", cafe_readl(cafe, NAND_NONMEM)); -#endif +#endif /* Scan to find existance of the device */ if (nand_scan_ident(mtd, 1)) { err = -ENXIO; diff --git a/drivers/mtd/nand/cafe_ecc.c b/drivers/mtd/nand/cafe_ecc.c index 2d29265..1b9fa05 100644 --- a/drivers/mtd/nand/cafe_ecc.c +++ b/drivers/mtd/nand/cafe_ecc.c @@ -1,20 +1,20 @@ -/* Error correction for CAFÉ NAND controller +/* Error correction for CAFÉ NAND controller * * © 2006 Marvell, Inc. * Author: Tom Chiou * - * 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) + * 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 + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 + * this program; if not, write to the Free Software Foundation, Inc., 59 * Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ @@ -500,9 +500,9 @@ static void solve_2x3(unsigned short m[2][3], unsigned short *coefs) } static unsigned char gf64_inv[64] = { - 0, 1, 33, 62, 49, 43, 31, 44, 57, 37, 52, 28, 46, 40, 22, 25, - 61, 54, 51, 39, 26, 35, 14, 24, 23, 15, 20, 34, 11, 53, 45, 6, - 63, 2, 27, 21, 56, 9, 50, 19, 13, 47, 48, 5, 7, 30, 12, 41, + 0, 1, 33, 62, 49, 43, 31, 44, 57, 37, 52, 28, 46, 40, 22, 25, + 61, 54, 51, 39, 26, 35, 14, 24, 23, 15, 20, 34, 11, 53, 45, 6, + 63, 2, 27, 21, 56, 9, 50, 19, 13, 47, 48, 5, 7, 30, 12, 41, 42, 4, 38, 18, 10, 29, 17, 60, 36, 8, 59, 58, 55, 16, 3, 32 }; @@ -529,517 +529,517 @@ static unsigned short gf4096_inv(unsigned short din) } static unsigned short err_pos_lut[4096] = { - 0xfff, 0x000, 0x451, 0xfff, 0xfff, 0x3cf, 0xfff, 0x041, - 0xfff, 0xfff, 0xfff, 0xfff, 0x28a, 0xfff, 0x492, 0xfff, - 0x145, 0xfff, 0xfff, 0x514, 0xfff, 0x082, 0xfff, 0xfff, - 0xfff, 0x249, 0x38e, 0x410, 0xfff, 0x104, 0x208, 0x1c7, - 0xfff, 0xfff, 0xfff, 0xfff, 0x2cb, 0xfff, 0xfff, 0xfff, - 0x0c3, 0x34d, 0x4d3, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x186, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x30c, 0x555, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x166, 0xfff, 0xfff, 0xfff, 0xfff, - 0x385, 0x14e, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4e1, - 0xfff, 0xfff, 0xfff, 0xfff, 0x538, 0xfff, 0x16d, 0xfff, - 0xfff, 0xfff, 0x45b, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x29c, 0x2cc, 0x30b, 0x2b3, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x0b3, 0xfff, 0x2f7, - 0xfff, 0x32b, 0xfff, 0xfff, 0xfff, 0xfff, 0x0a7, 0xfff, - 0xfff, 0x2da, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x07e, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x11c, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x22f, 0xfff, 0x1f4, 0xfff, 0xfff, - 0x2b0, 0x504, 0xfff, 0x114, 0xfff, 0xfff, 0xfff, 0x21d, - 0xfff, 0xfff, 0xfff, 0xfff, 0x00d, 0x3c4, 0x340, 0x10f, - 0xfff, 0xfff, 0x266, 0x02e, 0xfff, 0xfff, 0xfff, 0x4f8, - 0x337, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x07b, 0x168, 0xfff, 0xfff, 0x0fe, - 0xfff, 0xfff, 0x51a, 0xfff, 0x458, 0xfff, 0x36d, 0xfff, - 0xfff, 0xfff, 0xfff, 0x073, 0x37d, 0x415, 0x550, 0xfff, - 0xfff, 0xfff, 0x23b, 0x4b4, 0xfff, 0xfff, 0xfff, 0x1a1, - 0xfff, 0xfff, 0x3aa, 0xfff, 0x117, 0x04d, 0x341, 0xfff, - 0xfff, 0xfff, 0xfff, 0x518, 0x03e, 0x0f2, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x363, 0xfff, 0x0b9, 0xfff, 0xfff, - 0x241, 0xfff, 0xfff, 0x049, 0xfff, 0xfff, 0xfff, 0xfff, - 0x15f, 0x52d, 0xfff, 0xfff, 0xfff, 0x29e, 0xfff, 0xfff, - 0xfff, 0xfff, 0x4cf, 0x0fc, 0xfff, 0x36f, 0x3d3, 0xfff, - 0x228, 0xfff, 0xfff, 0x45e, 0xfff, 0xfff, 0xfff, 0xfff, - 0x238, 0xfff, 0xfff, 0xfff, 0xfff, 0x47f, 0xfff, 0xfff, - 0x43a, 0x265, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x3e8, - 0xfff, 0xfff, 0x01a, 0xfff, 0xfff, 0xfff, 0xfff, 0x21e, - 0x1fc, 0x40b, 0xfff, 0xfff, 0xfff, 0x2d0, 0x159, 0xfff, - 0xfff, 0x313, 0xfff, 0xfff, 0x05c, 0x4cc, 0xfff, 0xfff, - 0x0f6, 0x3d5, 0xfff, 0xfff, 0xfff, 0x54f, 0xfff, 0xfff, - 0xfff, 0x172, 0x1e4, 0x07c, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x53c, 0x1ad, 0x535, - 0x19b, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0x092, 0xfff, 0x2be, 0xfff, 0xfff, 0x482, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x0e6, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x476, 0xfff, 0x51d, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0x342, 0x2b5, 0x22e, 0x09a, 0xfff, 0x08d, - 0x44f, 0x3ed, 0xfff, 0xfff, 0xfff, 0xfff, 0x3d1, 0xfff, - 0xfff, 0x543, 0xfff, 0x48f, 0xfff, 0x3d2, 0xfff, 0x0d5, - 0x113, 0x0ec, 0x427, 0xfff, 0xfff, 0xfff, 0x4c4, 0xfff, - 0xfff, 0x50a, 0xfff, 0x144, 0xfff, 0x105, 0x39f, 0x294, - 0x164, 0xfff, 0x31a, 0xfff, 0xfff, 0x49a, 0xfff, 0x130, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0x1be, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0x49e, 0x371, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x0e8, 0x49c, 0x0f4, 0xfff, - 0x338, 0x1a7, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x36c, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x1ae, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x31b, 0xfff, 0xfff, 0x2dd, 0x522, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x2f4, - 0x3c6, 0x30d, 0xfff, 0xfff, 0xfff, 0xfff, 0x34c, 0x18f, - 0x30a, 0xfff, 0x01f, 0x079, 0xfff, 0xfff, 0x54d, 0x46b, - 0x28c, 0x37f, 0xfff, 0xfff, 0xfff, 0xfff, 0x355, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x14f, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x359, 0x3fe, 0x3c5, 0xfff, 0xfff, - 0xfff, 0xfff, 0x423, 0xfff, 0xfff, 0x34a, 0x22c, 0xfff, - 0x25a, 0xfff, 0xfff, 0x4ad, 0xfff, 0x28d, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x547, 0xfff, 0xfff, 0xfff, 0xfff, - 0x2e2, 0xfff, 0xfff, 0x1d5, 0xfff, 0x2a8, 0xfff, 0xfff, - 0x03f, 0xfff, 0xfff, 0xfff, 0xfff, 0x3eb, 0x0fa, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x55b, 0xfff, - 0x08e, 0xfff, 0x3ae, 0xfff, 0x3a4, 0xfff, 0x282, 0x158, - 0xfff, 0x382, 0xfff, 0xfff, 0x499, 0xfff, 0xfff, 0x08a, - 0xfff, 0xfff, 0xfff, 0x456, 0x3be, 0xfff, 0x1e2, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x559, 0xfff, 0x1a0, 0xfff, - 0xfff, 0x0b4, 0xfff, 0xfff, 0xfff, 0x2df, 0xfff, 0xfff, - 0xfff, 0x07f, 0x4f5, 0xfff, 0xfff, 0x27c, 0x133, 0x017, - 0xfff, 0x3fd, 0xfff, 0xfff, 0xfff, 0x44d, 0x4cd, 0x17a, - 0x0d7, 0x537, 0xfff, 0xfff, 0x353, 0xfff, 0xfff, 0x351, - 0x366, 0xfff, 0x44a, 0xfff, 0x1a6, 0xfff, 0xfff, 0xfff, - 0x291, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x1e3, - 0xfff, 0xfff, 0xfff, 0xfff, 0x389, 0xfff, 0x07a, 0xfff, - 0x1b6, 0x2ed, 0xfff, 0xfff, 0xfff, 0xfff, 0x24e, 0x074, - 0xfff, 0xfff, 0x3dc, 0xfff, 0x4e3, 0xfff, 0xfff, 0xfff, - 0xfff, 0x4eb, 0xfff, 0xfff, 0x3b8, 0x4de, 0xfff, 0x19c, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x262, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x076, 0x4e8, 0x3da, - 0xfff, 0x531, 0xfff, 0xfff, 0x14a, 0xfff, 0x0a2, 0x433, - 0x3df, 0x1e9, 0xfff, 0xfff, 0xfff, 0xfff, 0x3e7, 0x285, - 0x2d8, 0xfff, 0xfff, 0xfff, 0x349, 0x18d, 0x098, 0xfff, - 0x0df, 0x4bf, 0xfff, 0xfff, 0x0b2, 0xfff, 0x346, 0x24d, - 0xfff, 0xfff, 0xfff, 0x24f, 0x4fa, 0x2f9, 0xfff, 0xfff, - 0x3c9, 0xfff, 0x2b4, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x056, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x179, 0xfff, 0x0e9, 0x3f0, 0x33d, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x1fd, 0xfff, 0xfff, 0x526, 0xfff, - 0xfff, 0xfff, 0x53d, 0xfff, 0xfff, 0xfff, 0x170, 0x331, - 0xfff, 0x068, 0xfff, 0xfff, 0xfff, 0x3f7, 0xfff, 0x3d8, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x09f, 0x556, 0xfff, 0xfff, 0x02d, 0xfff, 0xfff, - 0x553, 0xfff, 0xfff, 0xfff, 0x1f0, 0xfff, 0xfff, 0x4d6, - 0x41e, 0xfff, 0xfff, 0xfff, 0xfff, 0x4d5, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x248, 0xfff, 0xfff, 0xfff, 0x0a3, - 0xfff, 0x217, 0xfff, 0xfff, 0xfff, 0x4f1, 0x209, 0xfff, - 0xfff, 0x475, 0x234, 0x52b, 0x398, 0xfff, 0x08b, 0xfff, - 0xfff, 0xfff, 0xfff, 0x2c2, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0x268, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x4a3, 0xfff, 0x0aa, 0xfff, 0x1d9, 0xfff, 0xfff, - 0xfff, 0xfff, 0x155, 0xfff, 0xfff, 0xfff, 0xfff, 0x0bf, - 0x539, 0xfff, 0xfff, 0x2f1, 0x545, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x2a7, 0x06f, 0xfff, 0x378, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x25e, 0xfff, - 0xfff, 0xfff, 0xfff, 0x15d, 0x02a, 0xfff, 0xfff, 0x0bc, - 0x235, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0x150, 0xfff, 0x1a9, 0xfff, 0xfff, 0xfff, 0xfff, 0x381, - 0xfff, 0x04e, 0x270, 0x13f, 0xfff, 0xfff, 0x405, 0xfff, - 0x3cd, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x2ef, 0xfff, 0x06a, 0xfff, 0xfff, 0xfff, 0x34f, - 0x212, 0xfff, 0xfff, 0x0e2, 0xfff, 0x083, 0x298, 0xfff, - 0xfff, 0xfff, 0x0c2, 0xfff, 0xfff, 0x52e, 0xfff, 0x488, - 0xfff, 0xfff, 0xfff, 0x36b, 0xfff, 0xfff, 0xfff, 0x442, - 0x091, 0xfff, 0x41c, 0xfff, 0xfff, 0x3a5, 0xfff, 0x4e6, - 0xfff, 0xfff, 0x40d, 0x31d, 0xfff, 0xfff, 0xfff, 0x4c1, - 0x053, 0xfff, 0x418, 0x13c, 0xfff, 0x350, 0xfff, 0x0ae, - 0xfff, 0xfff, 0x41f, 0xfff, 0x470, 0xfff, 0x4ca, 0xfff, - 0xfff, 0xfff, 0x02b, 0x450, 0xfff, 0x1f8, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x293, 0xfff, - 0xfff, 0xfff, 0xfff, 0x411, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x0b8, 0xfff, 0xfff, 0xfff, - 0x3e1, 0xfff, 0xfff, 0xfff, 0xfff, 0x43c, 0xfff, 0x2b2, - 0x2ab, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x1ec, - 0xfff, 0xfff, 0xfff, 0x3f8, 0x034, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x11a, 0xfff, 0x541, 0x45c, 0x134, - 0x1cc, 0xfff, 0xfff, 0xfff, 0x469, 0xfff, 0xfff, 0x44b, - 0x161, 0xfff, 0xfff, 0xfff, 0x055, 0xfff, 0xfff, 0xfff, - 0xfff, 0x307, 0xfff, 0xfff, 0xfff, 0xfff, 0x2d1, 0xfff, - 0xfff, 0xfff, 0x124, 0x37b, 0x26b, 0x336, 0xfff, 0xfff, - 0x2e4, 0x3cb, 0xfff, 0xfff, 0x0f8, 0x3c8, 0xfff, 0xfff, - 0xfff, 0x461, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4b5, - 0x2cf, 0xfff, 0xfff, 0xfff, 0x20f, 0xfff, 0x35a, 0xfff, - 0x490, 0xfff, 0x185, 0xfff, 0xfff, 0xfff, 0xfff, 0x42e, - 0xfff, 0xfff, 0xfff, 0xfff, 0x54b, 0xfff, 0xfff, 0xfff, - 0x146, 0xfff, 0x412, 0xfff, 0xfff, 0xfff, 0x1ff, 0xfff, - 0xfff, 0x3e0, 0xfff, 0xfff, 0xfff, 0xfff, 0x2d5, 0xfff, - 0x4df, 0x505, 0xfff, 0x413, 0xfff, 0x1a5, 0xfff, 0x3b2, - 0xfff, 0xfff, 0xfff, 0x35b, 0xfff, 0x116, 0xfff, 0xfff, - 0x171, 0x4d0, 0xfff, 0x154, 0x12d, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x468, 0x4db, 0xfff, - 0xfff, 0x1df, 0xfff, 0xfff, 0xfff, 0xfff, 0x05a, 0xfff, - 0x0f1, 0x403, 0xfff, 0x22b, 0x2e0, 0xfff, 0xfff, 0xfff, - 0x2b7, 0x373, 0xfff, 0xfff, 0xfff, 0xfff, 0x13e, 0xfff, - 0xfff, 0xfff, 0x0d0, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0x329, 0x1d2, 0x3fa, 0x047, 0xfff, 0x2f2, 0xfff, 0xfff, - 0x141, 0x0ac, 0x1d7, 0xfff, 0x07d, 0xfff, 0xfff, 0xfff, - 0x1c1, 0xfff, 0x487, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x045, 0xfff, 0xfff, 0xfff, 0xfff, - 0x288, 0x0cd, 0xfff, 0xfff, 0xfff, 0xfff, 0x226, 0x1d8, - 0xfff, 0x153, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4cb, - 0x528, 0xfff, 0xfff, 0xfff, 0x20a, 0x343, 0x3a1, 0xfff, - 0xfff, 0xfff, 0x2d7, 0x2d3, 0x1aa, 0x4c5, 0xfff, 0xfff, - 0xfff, 0x42b, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x3e9, 0xfff, 0x20b, 0x260, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x37c, 0x2fd, - 0xfff, 0xfff, 0x2c8, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x31e, 0xfff, 0x335, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0x135, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0x35c, 0x4dd, 0x129, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0x1ef, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x34e, 0xfff, 0xfff, 0xfff, 0xfff, 0x407, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x3ad, 0xfff, 0xfff, 0xfff, - 0x379, 0xfff, 0xfff, 0x1d0, 0x38d, 0xfff, 0xfff, 0x1e8, - 0x184, 0x3c1, 0x1c4, 0xfff, 0x1f9, 0xfff, 0xfff, 0x424, - 0xfff, 0xfff, 0xfff, 0xfff, 0x1d3, 0x0d4, 0xfff, 0x4e9, - 0xfff, 0xfff, 0xfff, 0x530, 0x107, 0xfff, 0x106, 0x04f, - 0xfff, 0xfff, 0x4c7, 0x503, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x15c, 0xfff, 0x23f, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x4f3, 0xfff, 0xfff, 0x3c7, - 0xfff, 0x278, 0xfff, 0xfff, 0x0a6, 0xfff, 0xfff, 0xfff, - 0x122, 0x1cf, 0xfff, 0x327, 0xfff, 0x2e5, 0xfff, 0x29d, - 0xfff, 0xfff, 0x3f1, 0xfff, 0xfff, 0x48d, 0xfff, 0xfff, - 0xfff, 0xfff, 0x054, 0xfff, 0xfff, 0xfff, 0xfff, 0x178, - 0x27e, 0x4e0, 0x352, 0x02f, 0x09c, 0xfff, 0x2a0, 0xfff, - 0xfff, 0x46a, 0x457, 0xfff, 0xfff, 0x501, 0xfff, 0x2ba, - 0xfff, 0xfff, 0xfff, 0x54e, 0x2e7, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0x551, 0xfff, 0xfff, 0x1db, 0x2aa, 0xfff, - 0xfff, 0x4bc, 0xfff, 0xfff, 0x395, 0xfff, 0x0de, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x455, 0xfff, 0x17e, - 0xfff, 0x221, 0x4a7, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0x388, 0xfff, 0xfff, 0xfff, 0x308, 0xfff, 0xfff, 0xfff, - 0x20e, 0x4b9, 0xfff, 0x273, 0x20c, 0x09e, 0xfff, 0x057, - 0xfff, 0xfff, 0xfff, 0xfff, 0x3f2, 0xfff, 0x1a8, 0x3a6, - 0x14c, 0xfff, 0xfff, 0x071, 0xfff, 0xfff, 0x53a, 0xfff, - 0xfff, 0xfff, 0xfff, 0x109, 0xfff, 0xfff, 0x399, 0xfff, - 0x061, 0x4f0, 0x39e, 0x244, 0xfff, 0x035, 0xfff, 0xfff, - 0x305, 0x47e, 0x297, 0xfff, 0xfff, 0x2b8, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x1bc, 0xfff, 0x2fc, - 0xfff, 0xfff, 0x554, 0xfff, 0xfff, 0xfff, 0xfff, 0x3b6, - 0xfff, 0xfff, 0xfff, 0x515, 0x397, 0xfff, 0xfff, 0x12f, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4e5, - 0xfff, 0x4fc, 0xfff, 0xfff, 0x05e, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0x0a8, 0x3af, 0x015, 0xfff, 0xfff, 0xfff, - 0xfff, 0x138, 0xfff, 0xfff, 0xfff, 0x540, 0xfff, 0xfff, - 0xfff, 0x027, 0x523, 0x2f0, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0x16c, 0xfff, 0x27d, 0xfff, 0xfff, 0xfff, - 0xfff, 0x04c, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4dc, - 0xfff, 0xfff, 0x059, 0x301, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x1a3, 0xfff, 0x15a, 0xfff, 0xfff, - 0x0a5, 0xfff, 0x435, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x051, 0xfff, 0xfff, 0x131, 0xfff, 0x4f4, 0xfff, - 0xfff, 0xfff, 0xfff, 0x441, 0xfff, 0x4fb, 0xfff, 0x03b, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x1ed, 0x274, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x0d3, 0x55e, 0x1b3, - 0xfff, 0x0bd, 0xfff, 0xfff, 0xfff, 0xfff, 0x225, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x4b7, 0xfff, 0xfff, 0x2ff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4c3, 0xfff, - 0x383, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x2f6, - 0xfff, 0xfff, 0x1ee, 0xfff, 0x03d, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0x26f, 0x1dc, 0xfff, 0x0db, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x0ce, 0xfff, 0xfff, 0x127, 0x03a, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x311, 0xfff, - 0xfff, 0x13d, 0x09d, 0x47b, 0x2a6, 0x50d, 0x510, 0x19a, - 0xfff, 0x354, 0x414, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0x44c, 0x3b0, 0xfff, 0x23d, 0x429, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0x4c0, 0x416, 0xfff, 0x05b, 0xfff, 0xfff, 0x137, 0xfff, - 0x25f, 0x49f, 0xfff, 0x279, 0x013, 0xfff, 0xfff, 0xfff, - 0x269, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x3d0, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x077, 0xfff, 0xfff, 0x3fb, - 0xfff, 0xfff, 0xfff, 0xfff, 0x271, 0x3a0, 0xfff, 0xfff, - 0x40f, 0xfff, 0xfff, 0x3de, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x1ab, 0x26a, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x489, 0xfff, 0xfff, - 0x252, 0xfff, 0xfff, 0xfff, 0xfff, 0x1b7, 0x42f, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x3b7, - 0xfff, 0x2bb, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x0f7, 0x01d, 0xfff, 0x067, 0xfff, - 0xfff, 0xfff, 0xfff, 0x4e2, 0xfff, 0xfff, 0x4bb, 0xfff, - 0xfff, 0xfff, 0x17b, 0xfff, 0x0ee, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0x36e, 0xfff, 0xfff, 0xfff, 0x533, 0xfff, - 0xfff, 0xfff, 0x4d4, 0x356, 0xfff, 0xfff, 0x375, 0xfff, - 0xfff, 0xfff, 0xfff, 0x4a4, 0x513, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4ff, 0xfff, 0x2af, - 0xfff, 0xfff, 0x026, 0xfff, 0x0ad, 0xfff, 0xfff, 0xfff, - 0xfff, 0x26e, 0xfff, 0xfff, 0xfff, 0xfff, 0x493, 0xfff, - 0x463, 0x4d2, 0x4be, 0xfff, 0xfff, 0xfff, 0xfff, 0x4f2, - 0x0b6, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x32d, 0x315, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x13a, 0x4a1, 0xfff, 0x27a, 0xfff, 0xfff, 0xfff, - 0x47a, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0x334, 0xfff, 0xfff, 0xfff, 0xfff, 0x54c, 0xfff, 0xfff, - 0xfff, 0x0c9, 0x007, 0xfff, 0xfff, 0x12e, 0xfff, 0x0ff, - 0xfff, 0xfff, 0x3f5, 0x509, 0xfff, 0xfff, 0xfff, 0xfff, - 0x1c3, 0x2ad, 0xfff, 0xfff, 0x47c, 0x261, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x152, 0xfff, 0xfff, 0xfff, 0x339, - 0xfff, 0x243, 0x1c0, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0x063, 0xfff, 0xfff, 0x254, 0xfff, 0xfff, 0x173, 0xfff, - 0x0c7, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x362, 0x259, 0x485, 0x374, 0x0dc, 0x3ab, 0xfff, - 0x1c5, 0x534, 0x544, 0xfff, 0xfff, 0x508, 0xfff, 0x402, - 0x408, 0xfff, 0x0e7, 0xfff, 0xfff, 0x00a, 0x205, 0xfff, - 0xfff, 0x2b9, 0xfff, 0xfff, 0xfff, 0x465, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x23a, 0xfff, 0xfff, 0xfff, - 0xfff, 0x147, 0x19d, 0x115, 0x214, 0xfff, 0x090, 0x368, - 0xfff, 0x210, 0xfff, 0xfff, 0x280, 0x52a, 0x163, 0x148, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x326, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x2de, 0xfff, 0xfff, 0xfff, 0xfff, - 0x206, 0x2c1, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0x189, 0xfff, 0xfff, 0xfff, 0xfff, 0x367, 0xfff, 0x1a4, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x443, 0xfff, 0x27b, - 0xfff, 0xfff, 0x251, 0x549, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0x188, 0x04b, 0xfff, 0xfff, 0xfff, 0x31f, - 0x4a6, 0xfff, 0x246, 0x1de, 0x156, 0xfff, 0xfff, 0xfff, - 0x3a9, 0xfff, 0xfff, 0xfff, 0x2fa, 0xfff, 0x128, 0x0d1, - 0x449, 0x255, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x258, 0xfff, 0xfff, 0xfff, - 0x532, 0xfff, 0xfff, 0xfff, 0x303, 0x517, 0xfff, 0xfff, - 0x2a9, 0x24a, 0xfff, 0xfff, 0x231, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0x4b6, 0x516, 0xfff, 0xfff, 0x0e4, 0x0eb, - 0xfff, 0x4e4, 0xfff, 0x275, 0xfff, 0xfff, 0x031, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x025, 0x21a, 0xfff, 0x0cc, - 0x45f, 0x3d9, 0x289, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0x23e, 0xfff, 0xfff, 0xfff, 0x438, 0x097, - 0x419, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0x0a9, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0x37e, 0x0e0, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x431, - 0x372, 0xfff, 0xfff, 0xfff, 0x1ba, 0x06e, 0xfff, 0x1b1, - 0xfff, 0xfff, 0x12a, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0x193, 0xfff, 0xfff, 0xfff, 0xfff, 0x10a, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x048, 0x1b4, - 0xfff, 0xfff, 0xfff, 0xfff, 0x295, 0x140, 0x108, 0xfff, - 0xfff, 0xfff, 0xfff, 0x16f, 0xfff, 0x0a4, 0x37a, 0xfff, - 0x29a, 0xfff, 0x284, 0xfff, 0xfff, 0xfff, 0xfff, 0x4c6, - 0x2a2, 0x3a3, 0xfff, 0x201, 0xfff, 0xfff, 0xfff, 0x4bd, - 0x005, 0x54a, 0x3b5, 0x204, 0x2ee, 0x11d, 0x436, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x3ec, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x11f, 0x498, 0x21c, 0xfff, - 0xfff, 0xfff, 0x3d6, 0xfff, 0x4ab, 0xfff, 0x432, 0x2eb, - 0x542, 0x4fd, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x4ce, 0xfff, 0xfff, 0x2fb, 0xfff, - 0xfff, 0x2e1, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x1b9, 0x037, 0x0dd, - 0xfff, 0xfff, 0xfff, 0x2bf, 0x521, 0x496, 0x095, 0xfff, - 0xfff, 0x328, 0x070, 0x1bf, 0xfff, 0x393, 0xfff, 0xfff, - 0x102, 0xfff, 0xfff, 0x21b, 0xfff, 0x142, 0x263, 0x519, - 0xfff, 0x2a5, 0x177, 0xfff, 0x14d, 0x471, 0x4ae, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0x1f6, 0xfff, 0x481, 0xfff, 0xfff, 0xfff, 0x151, 0xfff, - 0xfff, 0xfff, 0x085, 0x33f, 0xfff, 0xfff, 0xfff, 0x084, - 0xfff, 0xfff, 0xfff, 0x345, 0x3a2, 0xfff, 0xfff, 0x0a0, - 0x0da, 0x024, 0xfff, 0xfff, 0xfff, 0x1bd, 0xfff, 0x55c, - 0x467, 0x445, 0xfff, 0xfff, 0xfff, 0x052, 0xfff, 0xfff, - 0xfff, 0xfff, 0x51e, 0xfff, 0xfff, 0x39d, 0xfff, 0x35f, - 0xfff, 0x376, 0x3ee, 0xfff, 0xfff, 0xfff, 0xfff, 0x448, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x16a, - 0xfff, 0x036, 0x38f, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x211, - 0xfff, 0xfff, 0xfff, 0x230, 0xfff, 0xfff, 0x3ba, 0xfff, - 0xfff, 0xfff, 0x3ce, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x229, 0xfff, 0x176, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x00b, 0xfff, 0x162, 0x018, 0xfff, - 0xfff, 0x233, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0x400, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x12b, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x3f4, 0xfff, 0x0f0, 0xfff, 0x1ac, 0xfff, 0xfff, - 0x119, 0xfff, 0x2c0, 0xfff, 0xfff, 0xfff, 0x49b, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x23c, 0xfff, - 0x4b3, 0x010, 0x064, 0xfff, 0xfff, 0x4ba, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x3c2, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x006, 0x196, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x100, 0x191, 0xfff, - 0x1ea, 0x29f, 0xfff, 0xfff, 0xfff, 0x276, 0xfff, 0xfff, - 0x2b1, 0x3b9, 0xfff, 0x03c, 0xfff, 0xfff, 0xfff, 0x180, - 0xfff, 0x08f, 0xfff, 0xfff, 0x19e, 0x019, 0xfff, 0x0b0, - 0x0fd, 0x332, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x06b, 0x2e8, 0xfff, 0x446, 0xfff, 0xfff, 0x004, - 0x247, 0x197, 0xfff, 0x112, 0x169, 0x292, 0xfff, 0x302, - 0xfff, 0xfff, 0x33b, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x287, 0x21f, 0xfff, 0x3ea, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4e7, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x3a8, 0xfff, 0xfff, 0x2bc, 0xfff, - 0x484, 0x296, 0xfff, 0x1c9, 0x08c, 0x1e5, 0x48a, 0xfff, - 0x360, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0x1ca, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x10d, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0x066, 0x2ea, 0x28b, 0x25b, 0xfff, 0x072, - 0xfff, 0xfff, 0xfff, 0xfff, 0x2b6, 0xfff, 0xfff, 0x272, - 0xfff, 0xfff, 0x525, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0x2ca, 0xfff, 0xfff, 0xfff, 0x299, 0xfff, 0xfff, 0xfff, - 0x558, 0x41a, 0xfff, 0x4f7, 0x557, 0xfff, 0x4a0, 0x344, - 0x12c, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x125, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0x40e, 0xfff, 0xfff, 0x502, 0xfff, 0x103, 0x3e6, 0xfff, - 0x527, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x45d, 0xfff, 0xfff, 0xfff, 0xfff, - 0x44e, 0xfff, 0xfff, 0xfff, 0xfff, 0x0d2, 0x4c9, 0x35e, - 0x459, 0x2d9, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x17d, - 0x0c4, 0xfff, 0xfff, 0xfff, 0x3ac, 0x390, 0x094, 0xfff, - 0x483, 0x0ab, 0xfff, 0x253, 0xfff, 0x391, 0xfff, 0xfff, - 0xfff, 0xfff, 0x123, 0x0ef, 0xfff, 0xfff, 0xfff, 0x330, - 0x38c, 0xfff, 0xfff, 0x2ae, 0xfff, 0xfff, 0xfff, 0x042, - 0x012, 0x06d, 0xfff, 0xfff, 0xfff, 0x32a, 0x3db, 0x364, - 0x2dc, 0xfff, 0x30f, 0x3d7, 0x4a5, 0x050, 0xfff, 0xfff, - 0x029, 0xfff, 0xfff, 0xfff, 0xfff, 0x1d1, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x480, 0xfff, - 0x4ed, 0x081, 0x0a1, 0xfff, 0xfff, 0xfff, 0x30e, 0x52f, - 0x257, 0xfff, 0xfff, 0x447, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x401, 0x3cc, 0xfff, 0xfff, 0x0fb, - 0x2c9, 0x42a, 0x314, 0x33e, 0x3bd, 0x318, 0xfff, 0x10e, - 0x2a1, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x24c, - 0x506, 0xfff, 0x267, 0xfff, 0xfff, 0x219, 0xfff, 0x1eb, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0x309, 0x3e2, 0x46c, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0x384, 0xfff, 0xfff, 0xfff, 0xfff, 0x50c, 0xfff, 0x24b, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x038, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x194, - 0x143, 0x3e3, 0xfff, 0xfff, 0xfff, 0x4c2, 0xfff, 0xfff, - 0x0e1, 0x25c, 0xfff, 0x237, 0xfff, 0x1fe, 0xfff, 0xfff, - 0xfff, 0x065, 0x2a4, 0xfff, 0x386, 0x55a, 0x11b, 0xfff, - 0xfff, 0x192, 0xfff, 0x183, 0x00e, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x4b2, 0x18e, 0xfff, 0xfff, 0xfff, - 0xfff, 0x486, 0x4ef, 0x0c6, 0x380, 0xfff, 0x4a8, 0xfff, - 0x0c5, 0xfff, 0xfff, 0xfff, 0xfff, 0x093, 0x1b8, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x2e6, - 0xfff, 0x0f3, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0x28e, 0xfff, 0x53b, 0x420, 0x22a, 0x33a, 0xfff, 0x387, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x2a3, 0xfff, 0xfff, - 0xfff, 0x428, 0x500, 0xfff, 0xfff, 0x120, 0x2c6, 0x290, - 0x2f5, 0x0e3, 0xfff, 0x0b7, 0xfff, 0x319, 0x474, 0xfff, - 0xfff, 0xfff, 0x529, 0x014, 0xfff, 0x41b, 0x40a, 0x18b, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x0d9, - 0xfff, 0x38a, 0xfff, 0xfff, 0xfff, 0xfff, 0x1ce, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x3b1, 0xfff, 0xfff, 0x05d, - 0x2c4, 0xfff, 0xfff, 0x4af, 0xfff, 0x030, 0xfff, 0xfff, - 0x203, 0xfff, 0x277, 0x256, 0xfff, 0xfff, 0xfff, 0x4f9, - 0xfff, 0x2c7, 0xfff, 0x466, 0x016, 0x1cd, 0xfff, 0x167, - 0xfff, 0xfff, 0x0c8, 0xfff, 0x43d, 0xfff, 0xfff, 0x020, - 0xfff, 0xfff, 0x232, 0x1cb, 0x1e0, 0xfff, 0xfff, 0x347, - 0xfff, 0x478, 0xfff, 0x365, 0xfff, 0xfff, 0xfff, 0xfff, - 0x358, 0xfff, 0x10b, 0xfff, 0x35d, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0x452, 0x22d, 0xfff, 0xfff, 0x47d, 0xfff, - 0x2f3, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x460, 0xfff, - 0xfff, 0xfff, 0x50b, 0xfff, 0xfff, 0xfff, 0x2ec, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x4b1, 0x422, 0xfff, 0xfff, - 0xfff, 0x2d4, 0xfff, 0x239, 0xfff, 0xfff, 0xfff, 0x439, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x491, 0x075, 0xfff, 0xfff, 0xfff, 0x06c, 0xfff, - 0xfff, 0x0f9, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x139, 0xfff, 0x4f6, 0xfff, 0xfff, 0x409, 0xfff, - 0xfff, 0x15b, 0xfff, 0xfff, 0x348, 0xfff, 0xfff, 0xfff, - 0xfff, 0x4a2, 0x49d, 0xfff, 0x033, 0x175, 0xfff, 0x039, - 0xfff, 0x312, 0x40c, 0xfff, 0xfff, 0x325, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x4aa, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x165, 0x3bc, 0x48c, 0x310, 0x096, - 0xfff, 0xfff, 0x250, 0x1a2, 0xfff, 0xfff, 0xfff, 0xfff, - 0x20d, 0x2ac, 0xfff, 0xfff, 0x39b, 0xfff, 0x377, 0xfff, - 0x512, 0x495, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x357, 0x4ea, 0xfff, 0xfff, - 0xfff, 0xfff, 0x198, 0xfff, 0xfff, 0xfff, 0x434, 0x04a, - 0xfff, 0xfff, 0xfff, 0xfff, 0x062, 0xfff, 0x1d6, 0x1c8, - 0xfff, 0x1f3, 0x281, 0xfff, 0x462, 0xfff, 0xfff, 0xfff, - 0x4b0, 0xfff, 0x207, 0xfff, 0xfff, 0xfff, 0xfff, 0x3dd, - 0xfff, 0xfff, 0x55d, 0xfff, 0x552, 0x494, 0x1af, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x227, 0xfff, 0xfff, 0x069, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x43e, - 0x0b5, 0xfff, 0x524, 0x2d2, 0xfff, 0xfff, 0xfff, 0x28f, - 0xfff, 0x01b, 0x50e, 0xfff, 0xfff, 0x1bb, 0xfff, 0xfff, - 0x41d, 0xfff, 0x32e, 0x48e, 0xfff, 0x1f7, 0x224, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x394, 0xfff, 0xfff, 0xfff, - 0xfff, 0x52c, 0xfff, 0xfff, 0xfff, 0x392, 0xfff, 0x1e7, - 0xfff, 0xfff, 0x3f9, 0x3a7, 0xfff, 0x51f, 0xfff, 0x0bb, - 0x118, 0x3ca, 0xfff, 0x1dd, 0xfff, 0x48b, 0xfff, 0xfff, - 0xfff, 0xfff, 0x50f, 0xfff, 0x0d6, 0xfff, 0x1fa, 0xfff, - 0x11e, 0xfff, 0xfff, 0xfff, 0xfff, 0x4d7, 0xfff, 0x078, - 0x008, 0xfff, 0x25d, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0x032, 0x33c, 0xfff, 0x4d9, 0x160, 0xfff, 0xfff, 0x300, - 0x0b1, 0xfff, 0x322, 0xfff, 0x4ec, 0xfff, 0xfff, 0x200, - 0x00c, 0x369, 0x473, 0xfff, 0xfff, 0x32c, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x53e, 0x3d4, 0x417, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0x34b, 0x001, 0x39a, 0x02c, 0xfff, 0xfff, 0x2ce, 0x00f, - 0xfff, 0x0ba, 0xfff, 0xfff, 0xfff, 0xfff, 0x060, 0xfff, - 0x406, 0xfff, 0xfff, 0xfff, 0x4ee, 0x4ac, 0xfff, 0x43f, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x29b, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x216, - 0x190, 0xfff, 0x396, 0x464, 0xfff, 0xfff, 0x323, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x2e9, 0xfff, 0x26d, - 0x2cd, 0x040, 0xfff, 0xfff, 0xfff, 0xfff, 0x38b, 0x3c0, - 0xfff, 0xfff, 0xfff, 0x1f2, 0xfff, 0x0ea, 0xfff, 0xfff, - 0x472, 0xfff, 0x1fb, 0xfff, 0xfff, 0x0af, 0x27f, 0xfff, - 0xfff, 0xfff, 0x479, 0x023, 0xfff, 0x0d8, 0x3b3, 0xfff, - 0xfff, 0xfff, 0x121, 0xfff, 0xfff, 0x3bf, 0xfff, 0xfff, - 0x16b, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0x45a, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0x0be, 0xfff, 0xfff, 0xfff, 0x111, 0xfff, 0x220, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0x09b, 0x218, 0xfff, 0x022, 0x202, 0xfff, - 0x4c8, 0xfff, 0x0ed, 0xfff, 0xfff, 0x182, 0xfff, 0xfff, - 0xfff, 0x17f, 0x213, 0xfff, 0x321, 0x36a, 0xfff, 0x086, - 0xfff, 0xfff, 0xfff, 0x43b, 0x088, 0xfff, 0xfff, 0xfff, - 0xfff, 0x26c, 0xfff, 0x2f8, 0x3b4, 0xfff, 0xfff, 0xfff, - 0x132, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x333, 0x444, - 0x0c1, 0x4d8, 0x46d, 0x264, 0xfff, 0xfff, 0xfff, 0xfff, - 0x426, 0xfff, 0xfff, 0xfff, 0xfff, 0x2fe, 0xfff, 0xfff, - 0xfff, 0xfff, 0x011, 0xfff, 0x05f, 0xfff, 0xfff, 0xfff, - 0xfff, 0x10c, 0x101, 0xfff, 0xfff, 0xfff, 0xfff, 0x110, - 0xfff, 0x044, 0x304, 0x361, 0x404, 0xfff, 0x51b, 0x099, - 0xfff, 0x440, 0xfff, 0xfff, 0xfff, 0x222, 0xfff, 0xfff, - 0xfff, 0xfff, 0x1b5, 0xfff, 0x136, 0x430, 0xfff, 0x1da, - 0xfff, 0xfff, 0xfff, 0x043, 0xfff, 0x17c, 0xfff, 0xfff, - 0xfff, 0x01c, 0xfff, 0xfff, 0xfff, 0x425, 0x236, 0xfff, - 0x317, 0xfff, 0xfff, 0x437, 0x3fc, 0xfff, 0x1f1, 0xfff, - 0x324, 0xfff, 0xfff, 0x0ca, 0x306, 0xfff, 0x548, 0xfff, - 0x46e, 0xfff, 0xfff, 0xfff, 0x4b8, 0x1c2, 0x286, 0xfff, - 0xfff, 0x087, 0x18a, 0x19f, 0xfff, 0xfff, 0xfff, 0xfff, - 0x18c, 0xfff, 0x215, 0xfff, 0xfff, 0xfff, 0xfff, 0x283, - 0xfff, 0xfff, 0xfff, 0x126, 0xfff, 0xfff, 0x370, 0xfff, - 0x53f, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x31c, 0xfff, - 0x4d1, 0xfff, 0xfff, 0xfff, 0x021, 0xfff, 0x157, 0xfff, - 0xfff, 0x028, 0x16e, 0xfff, 0x421, 0xfff, 0x1c6, 0xfff, - 0xfff, 0x511, 0xfff, 0xfff, 0x39c, 0x46f, 0x1b2, 0xfff, - 0xfff, 0x316, 0xfff, 0xfff, 0x009, 0xfff, 0xfff, 0x195, - 0xfff, 0x240, 0x546, 0xfff, 0xfff, 0x520, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x454, 0xfff, 0xfff, 0xfff, - 0x3f3, 0xfff, 0xfff, 0x187, 0xfff, 0x4a9, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x51c, 0x453, 0x1e6, 0xfff, - 0xfff, 0xfff, 0x1b0, 0xfff, 0x477, 0xfff, 0xfff, 0xfff, - 0x4fe, 0xfff, 0x32f, 0xfff, 0xfff, 0x15e, 0x1d4, 0xfff, - 0x0e5, 0xfff, 0xfff, 0xfff, 0x242, 0x14b, 0x046, 0xfff, - 0x3f6, 0x3bb, 0x3e4, 0xfff, 0xfff, 0x2e3, 0xfff, 0x245, - 0xfff, 0x149, 0xfff, 0xfff, 0xfff, 0x2db, 0xfff, 0xfff, - 0x181, 0xfff, 0x089, 0x2c5, 0xfff, 0x1f5, 0xfff, 0x2d6, - 0x507, 0xfff, 0x42d, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0x080, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, 0x3c3, 0x320, 0xfff, 0x1e1, - 0xfff, 0x0f5, 0x13b, 0xfff, 0xfff, 0xfff, 0x003, 0x4da, - 0xfff, 0xfff, 0xfff, 0x42c, 0xfff, 0xfff, 0x0cb, 0xfff, - 0x536, 0x2c3, 0xfff, 0xfff, 0xfff, 0xfff, 0x199, 0xfff, - 0xfff, 0x0c0, 0xfff, 0x01e, 0x497, 0xfff, 0xfff, 0x3e5, - 0xfff, 0xfff, 0xfff, 0x0cf, 0xfff, 0x2bd, 0xfff, 0x223, + 0xfff, 0x000, 0x451, 0xfff, 0xfff, 0x3cf, 0xfff, 0x041, + 0xfff, 0xfff, 0xfff, 0xfff, 0x28a, 0xfff, 0x492, 0xfff, + 0x145, 0xfff, 0xfff, 0x514, 0xfff, 0x082, 0xfff, 0xfff, + 0xfff, 0x249, 0x38e, 0x410, 0xfff, 0x104, 0x208, 0x1c7, + 0xfff, 0xfff, 0xfff, 0xfff, 0x2cb, 0xfff, 0xfff, 0xfff, + 0x0c3, 0x34d, 0x4d3, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x186, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x30c, 0x555, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x166, 0xfff, 0xfff, 0xfff, 0xfff, + 0x385, 0x14e, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4e1, + 0xfff, 0xfff, 0xfff, 0xfff, 0x538, 0xfff, 0x16d, 0xfff, + 0xfff, 0xfff, 0x45b, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x29c, 0x2cc, 0x30b, 0x2b3, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x0b3, 0xfff, 0x2f7, + 0xfff, 0x32b, 0xfff, 0xfff, 0xfff, 0xfff, 0x0a7, 0xfff, + 0xfff, 0x2da, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x07e, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x11c, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x22f, 0xfff, 0x1f4, 0xfff, 0xfff, + 0x2b0, 0x504, 0xfff, 0x114, 0xfff, 0xfff, 0xfff, 0x21d, + 0xfff, 0xfff, 0xfff, 0xfff, 0x00d, 0x3c4, 0x340, 0x10f, + 0xfff, 0xfff, 0x266, 0x02e, 0xfff, 0xfff, 0xfff, 0x4f8, + 0x337, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x07b, 0x168, 0xfff, 0xfff, 0x0fe, + 0xfff, 0xfff, 0x51a, 0xfff, 0x458, 0xfff, 0x36d, 0xfff, + 0xfff, 0xfff, 0xfff, 0x073, 0x37d, 0x415, 0x550, 0xfff, + 0xfff, 0xfff, 0x23b, 0x4b4, 0xfff, 0xfff, 0xfff, 0x1a1, + 0xfff, 0xfff, 0x3aa, 0xfff, 0x117, 0x04d, 0x341, 0xfff, + 0xfff, 0xfff, 0xfff, 0x518, 0x03e, 0x0f2, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x363, 0xfff, 0x0b9, 0xfff, 0xfff, + 0x241, 0xfff, 0xfff, 0x049, 0xfff, 0xfff, 0xfff, 0xfff, + 0x15f, 0x52d, 0xfff, 0xfff, 0xfff, 0x29e, 0xfff, 0xfff, + 0xfff, 0xfff, 0x4cf, 0x0fc, 0xfff, 0x36f, 0x3d3, 0xfff, + 0x228, 0xfff, 0xfff, 0x45e, 0xfff, 0xfff, 0xfff, 0xfff, + 0x238, 0xfff, 0xfff, 0xfff, 0xfff, 0x47f, 0xfff, 0xfff, + 0x43a, 0x265, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x3e8, + 0xfff, 0xfff, 0x01a, 0xfff, 0xfff, 0xfff, 0xfff, 0x21e, + 0x1fc, 0x40b, 0xfff, 0xfff, 0xfff, 0x2d0, 0x159, 0xfff, + 0xfff, 0x313, 0xfff, 0xfff, 0x05c, 0x4cc, 0xfff, 0xfff, + 0x0f6, 0x3d5, 0xfff, 0xfff, 0xfff, 0x54f, 0xfff, 0xfff, + 0xfff, 0x172, 0x1e4, 0x07c, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x53c, 0x1ad, 0x535, + 0x19b, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0x092, 0xfff, 0x2be, 0xfff, 0xfff, 0x482, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x0e6, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x476, 0xfff, 0x51d, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0x342, 0x2b5, 0x22e, 0x09a, 0xfff, 0x08d, + 0x44f, 0x3ed, 0xfff, 0xfff, 0xfff, 0xfff, 0x3d1, 0xfff, + 0xfff, 0x543, 0xfff, 0x48f, 0xfff, 0x3d2, 0xfff, 0x0d5, + 0x113, 0x0ec, 0x427, 0xfff, 0xfff, 0xfff, 0x4c4, 0xfff, + 0xfff, 0x50a, 0xfff, 0x144, 0xfff, 0x105, 0x39f, 0x294, + 0x164, 0xfff, 0x31a, 0xfff, 0xfff, 0x49a, 0xfff, 0x130, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0x1be, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0x49e, 0x371, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x0e8, 0x49c, 0x0f4, 0xfff, + 0x338, 0x1a7, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x36c, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x1ae, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x31b, 0xfff, 0xfff, 0x2dd, 0x522, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x2f4, + 0x3c6, 0x30d, 0xfff, 0xfff, 0xfff, 0xfff, 0x34c, 0x18f, + 0x30a, 0xfff, 0x01f, 0x079, 0xfff, 0xfff, 0x54d, 0x46b, + 0x28c, 0x37f, 0xfff, 0xfff, 0xfff, 0xfff, 0x355, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x14f, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x359, 0x3fe, 0x3c5, 0xfff, 0xfff, + 0xfff, 0xfff, 0x423, 0xfff, 0xfff, 0x34a, 0x22c, 0xfff, + 0x25a, 0xfff, 0xfff, 0x4ad, 0xfff, 0x28d, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x547, 0xfff, 0xfff, 0xfff, 0xfff, + 0x2e2, 0xfff, 0xfff, 0x1d5, 0xfff, 0x2a8, 0xfff, 0xfff, + 0x03f, 0xfff, 0xfff, 0xfff, 0xfff, 0x3eb, 0x0fa, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x55b, 0xfff, + 0x08e, 0xfff, 0x3ae, 0xfff, 0x3a4, 0xfff, 0x282, 0x158, + 0xfff, 0x382, 0xfff, 0xfff, 0x499, 0xfff, 0xfff, 0x08a, + 0xfff, 0xfff, 0xfff, 0x456, 0x3be, 0xfff, 0x1e2, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x559, 0xfff, 0x1a0, 0xfff, + 0xfff, 0x0b4, 0xfff, 0xfff, 0xfff, 0x2df, 0xfff, 0xfff, + 0xfff, 0x07f, 0x4f5, 0xfff, 0xfff, 0x27c, 0x133, 0x017, + 0xfff, 0x3fd, 0xfff, 0xfff, 0xfff, 0x44d, 0x4cd, 0x17a, + 0x0d7, 0x537, 0xfff, 0xfff, 0x353, 0xfff, 0xfff, 0x351, + 0x366, 0xfff, 0x44a, 0xfff, 0x1a6, 0xfff, 0xfff, 0xfff, + 0x291, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x1e3, + 0xfff, 0xfff, 0xfff, 0xfff, 0x389, 0xfff, 0x07a, 0xfff, + 0x1b6, 0x2ed, 0xfff, 0xfff, 0xfff, 0xfff, 0x24e, 0x074, + 0xfff, 0xfff, 0x3dc, 0xfff, 0x4e3, 0xfff, 0xfff, 0xfff, + 0xfff, 0x4eb, 0xfff, 0xfff, 0x3b8, 0x4de, 0xfff, 0x19c, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x262, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x076, 0x4e8, 0x3da, + 0xfff, 0x531, 0xfff, 0xfff, 0x14a, 0xfff, 0x0a2, 0x433, + 0x3df, 0x1e9, 0xfff, 0xfff, 0xfff, 0xfff, 0x3e7, 0x285, + 0x2d8, 0xfff, 0xfff, 0xfff, 0x349, 0x18d, 0x098, 0xfff, + 0x0df, 0x4bf, 0xfff, 0xfff, 0x0b2, 0xfff, 0x346, 0x24d, + 0xfff, 0xfff, 0xfff, 0x24f, 0x4fa, 0x2f9, 0xfff, 0xfff, + 0x3c9, 0xfff, 0x2b4, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x056, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x179, 0xfff, 0x0e9, 0x3f0, 0x33d, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x1fd, 0xfff, 0xfff, 0x526, 0xfff, + 0xfff, 0xfff, 0x53d, 0xfff, 0xfff, 0xfff, 0x170, 0x331, + 0xfff, 0x068, 0xfff, 0xfff, 0xfff, 0x3f7, 0xfff, 0x3d8, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x09f, 0x556, 0xfff, 0xfff, 0x02d, 0xfff, 0xfff, + 0x553, 0xfff, 0xfff, 0xfff, 0x1f0, 0xfff, 0xfff, 0x4d6, + 0x41e, 0xfff, 0xfff, 0xfff, 0xfff, 0x4d5, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x248, 0xfff, 0xfff, 0xfff, 0x0a3, + 0xfff, 0x217, 0xfff, 0xfff, 0xfff, 0x4f1, 0x209, 0xfff, + 0xfff, 0x475, 0x234, 0x52b, 0x398, 0xfff, 0x08b, 0xfff, + 0xfff, 0xfff, 0xfff, 0x2c2, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0x268, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x4a3, 0xfff, 0x0aa, 0xfff, 0x1d9, 0xfff, 0xfff, + 0xfff, 0xfff, 0x155, 0xfff, 0xfff, 0xfff, 0xfff, 0x0bf, + 0x539, 0xfff, 0xfff, 0x2f1, 0x545, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x2a7, 0x06f, 0xfff, 0x378, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x25e, 0xfff, + 0xfff, 0xfff, 0xfff, 0x15d, 0x02a, 0xfff, 0xfff, 0x0bc, + 0x235, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0x150, 0xfff, 0x1a9, 0xfff, 0xfff, 0xfff, 0xfff, 0x381, + 0xfff, 0x04e, 0x270, 0x13f, 0xfff, 0xfff, 0x405, 0xfff, + 0x3cd, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x2ef, 0xfff, 0x06a, 0xfff, 0xfff, 0xfff, 0x34f, + 0x212, 0xfff, 0xfff, 0x0e2, 0xfff, 0x083, 0x298, 0xfff, + 0xfff, 0xfff, 0x0c2, 0xfff, 0xfff, 0x52e, 0xfff, 0x488, + 0xfff, 0xfff, 0xfff, 0x36b, 0xfff, 0xfff, 0xfff, 0x442, + 0x091, 0xfff, 0x41c, 0xfff, 0xfff, 0x3a5, 0xfff, 0x4e6, + 0xfff, 0xfff, 0x40d, 0x31d, 0xfff, 0xfff, 0xfff, 0x4c1, + 0x053, 0xfff, 0x418, 0x13c, 0xfff, 0x350, 0xfff, 0x0ae, + 0xfff, 0xfff, 0x41f, 0xfff, 0x470, 0xfff, 0x4ca, 0xfff, + 0xfff, 0xfff, 0x02b, 0x450, 0xfff, 0x1f8, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x293, 0xfff, + 0xfff, 0xfff, 0xfff, 0x411, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x0b8, 0xfff, 0xfff, 0xfff, + 0x3e1, 0xfff, 0xfff, 0xfff, 0xfff, 0x43c, 0xfff, 0x2b2, + 0x2ab, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x1ec, + 0xfff, 0xfff, 0xfff, 0x3f8, 0x034, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x11a, 0xfff, 0x541, 0x45c, 0x134, + 0x1cc, 0xfff, 0xfff, 0xfff, 0x469, 0xfff, 0xfff, 0x44b, + 0x161, 0xfff, 0xfff, 0xfff, 0x055, 0xfff, 0xfff, 0xfff, + 0xfff, 0x307, 0xfff, 0xfff, 0xfff, 0xfff, 0x2d1, 0xfff, + 0xfff, 0xfff, 0x124, 0x37b, 0x26b, 0x336, 0xfff, 0xfff, + 0x2e4, 0x3cb, 0xfff, 0xfff, 0x0f8, 0x3c8, 0xfff, 0xfff, + 0xfff, 0x461, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4b5, + 0x2cf, 0xfff, 0xfff, 0xfff, 0x20f, 0xfff, 0x35a, 0xfff, + 0x490, 0xfff, 0x185, 0xfff, 0xfff, 0xfff, 0xfff, 0x42e, + 0xfff, 0xfff, 0xfff, 0xfff, 0x54b, 0xfff, 0xfff, 0xfff, + 0x146, 0xfff, 0x412, 0xfff, 0xfff, 0xfff, 0x1ff, 0xfff, + 0xfff, 0x3e0, 0xfff, 0xfff, 0xfff, 0xfff, 0x2d5, 0xfff, + 0x4df, 0x505, 0xfff, 0x413, 0xfff, 0x1a5, 0xfff, 0x3b2, + 0xfff, 0xfff, 0xfff, 0x35b, 0xfff, 0x116, 0xfff, 0xfff, + 0x171, 0x4d0, 0xfff, 0x154, 0x12d, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x468, 0x4db, 0xfff, + 0xfff, 0x1df, 0xfff, 0xfff, 0xfff, 0xfff, 0x05a, 0xfff, + 0x0f1, 0x403, 0xfff, 0x22b, 0x2e0, 0xfff, 0xfff, 0xfff, + 0x2b7, 0x373, 0xfff, 0xfff, 0xfff, 0xfff, 0x13e, 0xfff, + 0xfff, 0xfff, 0x0d0, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0x329, 0x1d2, 0x3fa, 0x047, 0xfff, 0x2f2, 0xfff, 0xfff, + 0x141, 0x0ac, 0x1d7, 0xfff, 0x07d, 0xfff, 0xfff, 0xfff, + 0x1c1, 0xfff, 0x487, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x045, 0xfff, 0xfff, 0xfff, 0xfff, + 0x288, 0x0cd, 0xfff, 0xfff, 0xfff, 0xfff, 0x226, 0x1d8, + 0xfff, 0x153, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4cb, + 0x528, 0xfff, 0xfff, 0xfff, 0x20a, 0x343, 0x3a1, 0xfff, + 0xfff, 0xfff, 0x2d7, 0x2d3, 0x1aa, 0x4c5, 0xfff, 0xfff, + 0xfff, 0x42b, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x3e9, 0xfff, 0x20b, 0x260, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x37c, 0x2fd, + 0xfff, 0xfff, 0x2c8, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x31e, 0xfff, 0x335, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0x135, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0x35c, 0x4dd, 0x129, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0x1ef, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x34e, 0xfff, 0xfff, 0xfff, 0xfff, 0x407, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x3ad, 0xfff, 0xfff, 0xfff, + 0x379, 0xfff, 0xfff, 0x1d0, 0x38d, 0xfff, 0xfff, 0x1e8, + 0x184, 0x3c1, 0x1c4, 0xfff, 0x1f9, 0xfff, 0xfff, 0x424, + 0xfff, 0xfff, 0xfff, 0xfff, 0x1d3, 0x0d4, 0xfff, 0x4e9, + 0xfff, 0xfff, 0xfff, 0x530, 0x107, 0xfff, 0x106, 0x04f, + 0xfff, 0xfff, 0x4c7, 0x503, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x15c, 0xfff, 0x23f, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x4f3, 0xfff, 0xfff, 0x3c7, + 0xfff, 0x278, 0xfff, 0xfff, 0x0a6, 0xfff, 0xfff, 0xfff, + 0x122, 0x1cf, 0xfff, 0x327, 0xfff, 0x2e5, 0xfff, 0x29d, + 0xfff, 0xfff, 0x3f1, 0xfff, 0xfff, 0x48d, 0xfff, 0xfff, + 0xfff, 0xfff, 0x054, 0xfff, 0xfff, 0xfff, 0xfff, 0x178, + 0x27e, 0x4e0, 0x352, 0x02f, 0x09c, 0xfff, 0x2a0, 0xfff, + 0xfff, 0x46a, 0x457, 0xfff, 0xfff, 0x501, 0xfff, 0x2ba, + 0xfff, 0xfff, 0xfff, 0x54e, 0x2e7, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0x551, 0xfff, 0xfff, 0x1db, 0x2aa, 0xfff, + 0xfff, 0x4bc, 0xfff, 0xfff, 0x395, 0xfff, 0x0de, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x455, 0xfff, 0x17e, + 0xfff, 0x221, 0x4a7, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0x388, 0xfff, 0xfff, 0xfff, 0x308, 0xfff, 0xfff, 0xfff, + 0x20e, 0x4b9, 0xfff, 0x273, 0x20c, 0x09e, 0xfff, 0x057, + 0xfff, 0xfff, 0xfff, 0xfff, 0x3f2, 0xfff, 0x1a8, 0x3a6, + 0x14c, 0xfff, 0xfff, 0x071, 0xfff, 0xfff, 0x53a, 0xfff, + 0xfff, 0xfff, 0xfff, 0x109, 0xfff, 0xfff, 0x399, 0xfff, + 0x061, 0x4f0, 0x39e, 0x244, 0xfff, 0x035, 0xfff, 0xfff, + 0x305, 0x47e, 0x297, 0xfff, 0xfff, 0x2b8, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x1bc, 0xfff, 0x2fc, + 0xfff, 0xfff, 0x554, 0xfff, 0xfff, 0xfff, 0xfff, 0x3b6, + 0xfff, 0xfff, 0xfff, 0x515, 0x397, 0xfff, 0xfff, 0x12f, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4e5, + 0xfff, 0x4fc, 0xfff, 0xfff, 0x05e, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0x0a8, 0x3af, 0x015, 0xfff, 0xfff, 0xfff, + 0xfff, 0x138, 0xfff, 0xfff, 0xfff, 0x540, 0xfff, 0xfff, + 0xfff, 0x027, 0x523, 0x2f0, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0x16c, 0xfff, 0x27d, 0xfff, 0xfff, 0xfff, + 0xfff, 0x04c, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4dc, + 0xfff, 0xfff, 0x059, 0x301, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x1a3, 0xfff, 0x15a, 0xfff, 0xfff, + 0x0a5, 0xfff, 0x435, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x051, 0xfff, 0xfff, 0x131, 0xfff, 0x4f4, 0xfff, + 0xfff, 0xfff, 0xfff, 0x441, 0xfff, 0x4fb, 0xfff, 0x03b, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x1ed, 0x274, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x0d3, 0x55e, 0x1b3, + 0xfff, 0x0bd, 0xfff, 0xfff, 0xfff, 0xfff, 0x225, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x4b7, 0xfff, 0xfff, 0x2ff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4c3, 0xfff, + 0x383, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x2f6, + 0xfff, 0xfff, 0x1ee, 0xfff, 0x03d, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0x26f, 0x1dc, 0xfff, 0x0db, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x0ce, 0xfff, 0xfff, 0x127, 0x03a, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x311, 0xfff, + 0xfff, 0x13d, 0x09d, 0x47b, 0x2a6, 0x50d, 0x510, 0x19a, + 0xfff, 0x354, 0x414, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0x44c, 0x3b0, 0xfff, 0x23d, 0x429, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0x4c0, 0x416, 0xfff, 0x05b, 0xfff, 0xfff, 0x137, 0xfff, + 0x25f, 0x49f, 0xfff, 0x279, 0x013, 0xfff, 0xfff, 0xfff, + 0x269, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x3d0, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x077, 0xfff, 0xfff, 0x3fb, + 0xfff, 0xfff, 0xfff, 0xfff, 0x271, 0x3a0, 0xfff, 0xfff, + 0x40f, 0xfff, 0xfff, 0x3de, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x1ab, 0x26a, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x489, 0xfff, 0xfff, + 0x252, 0xfff, 0xfff, 0xfff, 0xfff, 0x1b7, 0x42f, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x3b7, + 0xfff, 0x2bb, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x0f7, 0x01d, 0xfff, 0x067, 0xfff, + 0xfff, 0xfff, 0xfff, 0x4e2, 0xfff, 0xfff, 0x4bb, 0xfff, + 0xfff, 0xfff, 0x17b, 0xfff, 0x0ee, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0x36e, 0xfff, 0xfff, 0xfff, 0x533, 0xfff, + 0xfff, 0xfff, 0x4d4, 0x356, 0xfff, 0xfff, 0x375, 0xfff, + 0xfff, 0xfff, 0xfff, 0x4a4, 0x513, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4ff, 0xfff, 0x2af, + 0xfff, 0xfff, 0x026, 0xfff, 0x0ad, 0xfff, 0xfff, 0xfff, + 0xfff, 0x26e, 0xfff, 0xfff, 0xfff, 0xfff, 0x493, 0xfff, + 0x463, 0x4d2, 0x4be, 0xfff, 0xfff, 0xfff, 0xfff, 0x4f2, + 0x0b6, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x32d, 0x315, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x13a, 0x4a1, 0xfff, 0x27a, 0xfff, 0xfff, 0xfff, + 0x47a, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0x334, 0xfff, 0xfff, 0xfff, 0xfff, 0x54c, 0xfff, 0xfff, + 0xfff, 0x0c9, 0x007, 0xfff, 0xfff, 0x12e, 0xfff, 0x0ff, + 0xfff, 0xfff, 0x3f5, 0x509, 0xfff, 0xfff, 0xfff, 0xfff, + 0x1c3, 0x2ad, 0xfff, 0xfff, 0x47c, 0x261, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x152, 0xfff, 0xfff, 0xfff, 0x339, + 0xfff, 0x243, 0x1c0, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0x063, 0xfff, 0xfff, 0x254, 0xfff, 0xfff, 0x173, 0xfff, + 0x0c7, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x362, 0x259, 0x485, 0x374, 0x0dc, 0x3ab, 0xfff, + 0x1c5, 0x534, 0x544, 0xfff, 0xfff, 0x508, 0xfff, 0x402, + 0x408, 0xfff, 0x0e7, 0xfff, 0xfff, 0x00a, 0x205, 0xfff, + 0xfff, 0x2b9, 0xfff, 0xfff, 0xfff, 0x465, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x23a, 0xfff, 0xfff, 0xfff, + 0xfff, 0x147, 0x19d, 0x115, 0x214, 0xfff, 0x090, 0x368, + 0xfff, 0x210, 0xfff, 0xfff, 0x280, 0x52a, 0x163, 0x148, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x326, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x2de, 0xfff, 0xfff, 0xfff, 0xfff, + 0x206, 0x2c1, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0x189, 0xfff, 0xfff, 0xfff, 0xfff, 0x367, 0xfff, 0x1a4, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x443, 0xfff, 0x27b, + 0xfff, 0xfff, 0x251, 0x549, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0x188, 0x04b, 0xfff, 0xfff, 0xfff, 0x31f, + 0x4a6, 0xfff, 0x246, 0x1de, 0x156, 0xfff, 0xfff, 0xfff, + 0x3a9, 0xfff, 0xfff, 0xfff, 0x2fa, 0xfff, 0x128, 0x0d1, + 0x449, 0x255, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x258, 0xfff, 0xfff, 0xfff, + 0x532, 0xfff, 0xfff, 0xfff, 0x303, 0x517, 0xfff, 0xfff, + 0x2a9, 0x24a, 0xfff, 0xfff, 0x231, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0x4b6, 0x516, 0xfff, 0xfff, 0x0e4, 0x0eb, + 0xfff, 0x4e4, 0xfff, 0x275, 0xfff, 0xfff, 0x031, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x025, 0x21a, 0xfff, 0x0cc, + 0x45f, 0x3d9, 0x289, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0x23e, 0xfff, 0xfff, 0xfff, 0x438, 0x097, + 0x419, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0x0a9, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0x37e, 0x0e0, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x431, + 0x372, 0xfff, 0xfff, 0xfff, 0x1ba, 0x06e, 0xfff, 0x1b1, + 0xfff, 0xfff, 0x12a, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0x193, 0xfff, 0xfff, 0xfff, 0xfff, 0x10a, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x048, 0x1b4, + 0xfff, 0xfff, 0xfff, 0xfff, 0x295, 0x140, 0x108, 0xfff, + 0xfff, 0xfff, 0xfff, 0x16f, 0xfff, 0x0a4, 0x37a, 0xfff, + 0x29a, 0xfff, 0x284, 0xfff, 0xfff, 0xfff, 0xfff, 0x4c6, + 0x2a2, 0x3a3, 0xfff, 0x201, 0xfff, 0xfff, 0xfff, 0x4bd, + 0x005, 0x54a, 0x3b5, 0x204, 0x2ee, 0x11d, 0x436, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x3ec, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x11f, 0x498, 0x21c, 0xfff, + 0xfff, 0xfff, 0x3d6, 0xfff, 0x4ab, 0xfff, 0x432, 0x2eb, + 0x542, 0x4fd, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x4ce, 0xfff, 0xfff, 0x2fb, 0xfff, + 0xfff, 0x2e1, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x1b9, 0x037, 0x0dd, + 0xfff, 0xfff, 0xfff, 0x2bf, 0x521, 0x496, 0x095, 0xfff, + 0xfff, 0x328, 0x070, 0x1bf, 0xfff, 0x393, 0xfff, 0xfff, + 0x102, 0xfff, 0xfff, 0x21b, 0xfff, 0x142, 0x263, 0x519, + 0xfff, 0x2a5, 0x177, 0xfff, 0x14d, 0x471, 0x4ae, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0x1f6, 0xfff, 0x481, 0xfff, 0xfff, 0xfff, 0x151, 0xfff, + 0xfff, 0xfff, 0x085, 0x33f, 0xfff, 0xfff, 0xfff, 0x084, + 0xfff, 0xfff, 0xfff, 0x345, 0x3a2, 0xfff, 0xfff, 0x0a0, + 0x0da, 0x024, 0xfff, 0xfff, 0xfff, 0x1bd, 0xfff, 0x55c, + 0x467, 0x445, 0xfff, 0xfff, 0xfff, 0x052, 0xfff, 0xfff, + 0xfff, 0xfff, 0x51e, 0xfff, 0xfff, 0x39d, 0xfff, 0x35f, + 0xfff, 0x376, 0x3ee, 0xfff, 0xfff, 0xfff, 0xfff, 0x448, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x16a, + 0xfff, 0x036, 0x38f, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x211, + 0xfff, 0xfff, 0xfff, 0x230, 0xfff, 0xfff, 0x3ba, 0xfff, + 0xfff, 0xfff, 0x3ce, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x229, 0xfff, 0x176, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x00b, 0xfff, 0x162, 0x018, 0xfff, + 0xfff, 0x233, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0x400, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x12b, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x3f4, 0xfff, 0x0f0, 0xfff, 0x1ac, 0xfff, 0xfff, + 0x119, 0xfff, 0x2c0, 0xfff, 0xfff, 0xfff, 0x49b, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x23c, 0xfff, + 0x4b3, 0x010, 0x064, 0xfff, 0xfff, 0x4ba, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x3c2, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x006, 0x196, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x100, 0x191, 0xfff, + 0x1ea, 0x29f, 0xfff, 0xfff, 0xfff, 0x276, 0xfff, 0xfff, + 0x2b1, 0x3b9, 0xfff, 0x03c, 0xfff, 0xfff, 0xfff, 0x180, + 0xfff, 0x08f, 0xfff, 0xfff, 0x19e, 0x019, 0xfff, 0x0b0, + 0x0fd, 0x332, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x06b, 0x2e8, 0xfff, 0x446, 0xfff, 0xfff, 0x004, + 0x247, 0x197, 0xfff, 0x112, 0x169, 0x292, 0xfff, 0x302, + 0xfff, 0xfff, 0x33b, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x287, 0x21f, 0xfff, 0x3ea, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4e7, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x3a8, 0xfff, 0xfff, 0x2bc, 0xfff, + 0x484, 0x296, 0xfff, 0x1c9, 0x08c, 0x1e5, 0x48a, 0xfff, + 0x360, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0x1ca, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x10d, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0x066, 0x2ea, 0x28b, 0x25b, 0xfff, 0x072, + 0xfff, 0xfff, 0xfff, 0xfff, 0x2b6, 0xfff, 0xfff, 0x272, + 0xfff, 0xfff, 0x525, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0x2ca, 0xfff, 0xfff, 0xfff, 0x299, 0xfff, 0xfff, 0xfff, + 0x558, 0x41a, 0xfff, 0x4f7, 0x557, 0xfff, 0x4a0, 0x344, + 0x12c, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x125, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0x40e, 0xfff, 0xfff, 0x502, 0xfff, 0x103, 0x3e6, 0xfff, + 0x527, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x45d, 0xfff, 0xfff, 0xfff, 0xfff, + 0x44e, 0xfff, 0xfff, 0xfff, 0xfff, 0x0d2, 0x4c9, 0x35e, + 0x459, 0x2d9, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x17d, + 0x0c4, 0xfff, 0xfff, 0xfff, 0x3ac, 0x390, 0x094, 0xfff, + 0x483, 0x0ab, 0xfff, 0x253, 0xfff, 0x391, 0xfff, 0xfff, + 0xfff, 0xfff, 0x123, 0x0ef, 0xfff, 0xfff, 0xfff, 0x330, + 0x38c, 0xfff, 0xfff, 0x2ae, 0xfff, 0xfff, 0xfff, 0x042, + 0x012, 0x06d, 0xfff, 0xfff, 0xfff, 0x32a, 0x3db, 0x364, + 0x2dc, 0xfff, 0x30f, 0x3d7, 0x4a5, 0x050, 0xfff, 0xfff, + 0x029, 0xfff, 0xfff, 0xfff, 0xfff, 0x1d1, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x480, 0xfff, + 0x4ed, 0x081, 0x0a1, 0xfff, 0xfff, 0xfff, 0x30e, 0x52f, + 0x257, 0xfff, 0xfff, 0x447, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x401, 0x3cc, 0xfff, 0xfff, 0x0fb, + 0x2c9, 0x42a, 0x314, 0x33e, 0x3bd, 0x318, 0xfff, 0x10e, + 0x2a1, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x24c, + 0x506, 0xfff, 0x267, 0xfff, 0xfff, 0x219, 0xfff, 0x1eb, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0x309, 0x3e2, 0x46c, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0x384, 0xfff, 0xfff, 0xfff, 0xfff, 0x50c, 0xfff, 0x24b, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x038, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x194, + 0x143, 0x3e3, 0xfff, 0xfff, 0xfff, 0x4c2, 0xfff, 0xfff, + 0x0e1, 0x25c, 0xfff, 0x237, 0xfff, 0x1fe, 0xfff, 0xfff, + 0xfff, 0x065, 0x2a4, 0xfff, 0x386, 0x55a, 0x11b, 0xfff, + 0xfff, 0x192, 0xfff, 0x183, 0x00e, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x4b2, 0x18e, 0xfff, 0xfff, 0xfff, + 0xfff, 0x486, 0x4ef, 0x0c6, 0x380, 0xfff, 0x4a8, 0xfff, + 0x0c5, 0xfff, 0xfff, 0xfff, 0xfff, 0x093, 0x1b8, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x2e6, + 0xfff, 0x0f3, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0x28e, 0xfff, 0x53b, 0x420, 0x22a, 0x33a, 0xfff, 0x387, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x2a3, 0xfff, 0xfff, + 0xfff, 0x428, 0x500, 0xfff, 0xfff, 0x120, 0x2c6, 0x290, + 0x2f5, 0x0e3, 0xfff, 0x0b7, 0xfff, 0x319, 0x474, 0xfff, + 0xfff, 0xfff, 0x529, 0x014, 0xfff, 0x41b, 0x40a, 0x18b, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x0d9, + 0xfff, 0x38a, 0xfff, 0xfff, 0xfff, 0xfff, 0x1ce, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x3b1, 0xfff, 0xfff, 0x05d, + 0x2c4, 0xfff, 0xfff, 0x4af, 0xfff, 0x030, 0xfff, 0xfff, + 0x203, 0xfff, 0x277, 0x256, 0xfff, 0xfff, 0xfff, 0x4f9, + 0xfff, 0x2c7, 0xfff, 0x466, 0x016, 0x1cd, 0xfff, 0x167, + 0xfff, 0xfff, 0x0c8, 0xfff, 0x43d, 0xfff, 0xfff, 0x020, + 0xfff, 0xfff, 0x232, 0x1cb, 0x1e0, 0xfff, 0xfff, 0x347, + 0xfff, 0x478, 0xfff, 0x365, 0xfff, 0xfff, 0xfff, 0xfff, + 0x358, 0xfff, 0x10b, 0xfff, 0x35d, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0x452, 0x22d, 0xfff, 0xfff, 0x47d, 0xfff, + 0x2f3, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x460, 0xfff, + 0xfff, 0xfff, 0x50b, 0xfff, 0xfff, 0xfff, 0x2ec, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x4b1, 0x422, 0xfff, 0xfff, + 0xfff, 0x2d4, 0xfff, 0x239, 0xfff, 0xfff, 0xfff, 0x439, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x491, 0x075, 0xfff, 0xfff, 0xfff, 0x06c, 0xfff, + 0xfff, 0x0f9, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x139, 0xfff, 0x4f6, 0xfff, 0xfff, 0x409, 0xfff, + 0xfff, 0x15b, 0xfff, 0xfff, 0x348, 0xfff, 0xfff, 0xfff, + 0xfff, 0x4a2, 0x49d, 0xfff, 0x033, 0x175, 0xfff, 0x039, + 0xfff, 0x312, 0x40c, 0xfff, 0xfff, 0x325, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x4aa, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x165, 0x3bc, 0x48c, 0x310, 0x096, + 0xfff, 0xfff, 0x250, 0x1a2, 0xfff, 0xfff, 0xfff, 0xfff, + 0x20d, 0x2ac, 0xfff, 0xfff, 0x39b, 0xfff, 0x377, 0xfff, + 0x512, 0x495, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x357, 0x4ea, 0xfff, 0xfff, + 0xfff, 0xfff, 0x198, 0xfff, 0xfff, 0xfff, 0x434, 0x04a, + 0xfff, 0xfff, 0xfff, 0xfff, 0x062, 0xfff, 0x1d6, 0x1c8, + 0xfff, 0x1f3, 0x281, 0xfff, 0x462, 0xfff, 0xfff, 0xfff, + 0x4b0, 0xfff, 0x207, 0xfff, 0xfff, 0xfff, 0xfff, 0x3dd, + 0xfff, 0xfff, 0x55d, 0xfff, 0x552, 0x494, 0x1af, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x227, 0xfff, 0xfff, 0x069, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x43e, + 0x0b5, 0xfff, 0x524, 0x2d2, 0xfff, 0xfff, 0xfff, 0x28f, + 0xfff, 0x01b, 0x50e, 0xfff, 0xfff, 0x1bb, 0xfff, 0xfff, + 0x41d, 0xfff, 0x32e, 0x48e, 0xfff, 0x1f7, 0x224, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x394, 0xfff, 0xfff, 0xfff, + 0xfff, 0x52c, 0xfff, 0xfff, 0xfff, 0x392, 0xfff, 0x1e7, + 0xfff, 0xfff, 0x3f9, 0x3a7, 0xfff, 0x51f, 0xfff, 0x0bb, + 0x118, 0x3ca, 0xfff, 0x1dd, 0xfff, 0x48b, 0xfff, 0xfff, + 0xfff, 0xfff, 0x50f, 0xfff, 0x0d6, 0xfff, 0x1fa, 0xfff, + 0x11e, 0xfff, 0xfff, 0xfff, 0xfff, 0x4d7, 0xfff, 0x078, + 0x008, 0xfff, 0x25d, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0x032, 0x33c, 0xfff, 0x4d9, 0x160, 0xfff, 0xfff, 0x300, + 0x0b1, 0xfff, 0x322, 0xfff, 0x4ec, 0xfff, 0xfff, 0x200, + 0x00c, 0x369, 0x473, 0xfff, 0xfff, 0x32c, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x53e, 0x3d4, 0x417, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0x34b, 0x001, 0x39a, 0x02c, 0xfff, 0xfff, 0x2ce, 0x00f, + 0xfff, 0x0ba, 0xfff, 0xfff, 0xfff, 0xfff, 0x060, 0xfff, + 0x406, 0xfff, 0xfff, 0xfff, 0x4ee, 0x4ac, 0xfff, 0x43f, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x29b, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x216, + 0x190, 0xfff, 0x396, 0x464, 0xfff, 0xfff, 0x323, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x2e9, 0xfff, 0x26d, + 0x2cd, 0x040, 0xfff, 0xfff, 0xfff, 0xfff, 0x38b, 0x3c0, + 0xfff, 0xfff, 0xfff, 0x1f2, 0xfff, 0x0ea, 0xfff, 0xfff, + 0x472, 0xfff, 0x1fb, 0xfff, 0xfff, 0x0af, 0x27f, 0xfff, + 0xfff, 0xfff, 0x479, 0x023, 0xfff, 0x0d8, 0x3b3, 0xfff, + 0xfff, 0xfff, 0x121, 0xfff, 0xfff, 0x3bf, 0xfff, 0xfff, + 0x16b, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0x45a, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0x0be, 0xfff, 0xfff, 0xfff, 0x111, 0xfff, 0x220, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0x09b, 0x218, 0xfff, 0x022, 0x202, 0xfff, + 0x4c8, 0xfff, 0x0ed, 0xfff, 0xfff, 0x182, 0xfff, 0xfff, + 0xfff, 0x17f, 0x213, 0xfff, 0x321, 0x36a, 0xfff, 0x086, + 0xfff, 0xfff, 0xfff, 0x43b, 0x088, 0xfff, 0xfff, 0xfff, + 0xfff, 0x26c, 0xfff, 0x2f8, 0x3b4, 0xfff, 0xfff, 0xfff, + 0x132, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x333, 0x444, + 0x0c1, 0x4d8, 0x46d, 0x264, 0xfff, 0xfff, 0xfff, 0xfff, + 0x426, 0xfff, 0xfff, 0xfff, 0xfff, 0x2fe, 0xfff, 0xfff, + 0xfff, 0xfff, 0x011, 0xfff, 0x05f, 0xfff, 0xfff, 0xfff, + 0xfff, 0x10c, 0x101, 0xfff, 0xfff, 0xfff, 0xfff, 0x110, + 0xfff, 0x044, 0x304, 0x361, 0x404, 0xfff, 0x51b, 0x099, + 0xfff, 0x440, 0xfff, 0xfff, 0xfff, 0x222, 0xfff, 0xfff, + 0xfff, 0xfff, 0x1b5, 0xfff, 0x136, 0x430, 0xfff, 0x1da, + 0xfff, 0xfff, 0xfff, 0x043, 0xfff, 0x17c, 0xfff, 0xfff, + 0xfff, 0x01c, 0xfff, 0xfff, 0xfff, 0x425, 0x236, 0xfff, + 0x317, 0xfff, 0xfff, 0x437, 0x3fc, 0xfff, 0x1f1, 0xfff, + 0x324, 0xfff, 0xfff, 0x0ca, 0x306, 0xfff, 0x548, 0xfff, + 0x46e, 0xfff, 0xfff, 0xfff, 0x4b8, 0x1c2, 0x286, 0xfff, + 0xfff, 0x087, 0x18a, 0x19f, 0xfff, 0xfff, 0xfff, 0xfff, + 0x18c, 0xfff, 0x215, 0xfff, 0xfff, 0xfff, 0xfff, 0x283, + 0xfff, 0xfff, 0xfff, 0x126, 0xfff, 0xfff, 0x370, 0xfff, + 0x53f, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x31c, 0xfff, + 0x4d1, 0xfff, 0xfff, 0xfff, 0x021, 0xfff, 0x157, 0xfff, + 0xfff, 0x028, 0x16e, 0xfff, 0x421, 0xfff, 0x1c6, 0xfff, + 0xfff, 0x511, 0xfff, 0xfff, 0x39c, 0x46f, 0x1b2, 0xfff, + 0xfff, 0x316, 0xfff, 0xfff, 0x009, 0xfff, 0xfff, 0x195, + 0xfff, 0x240, 0x546, 0xfff, 0xfff, 0x520, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x454, 0xfff, 0xfff, 0xfff, + 0x3f3, 0xfff, 0xfff, 0x187, 0xfff, 0x4a9, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x51c, 0x453, 0x1e6, 0xfff, + 0xfff, 0xfff, 0x1b0, 0xfff, 0x477, 0xfff, 0xfff, 0xfff, + 0x4fe, 0xfff, 0x32f, 0xfff, 0xfff, 0x15e, 0x1d4, 0xfff, + 0x0e5, 0xfff, 0xfff, 0xfff, 0x242, 0x14b, 0x046, 0xfff, + 0x3f6, 0x3bb, 0x3e4, 0xfff, 0xfff, 0x2e3, 0xfff, 0x245, + 0xfff, 0x149, 0xfff, 0xfff, 0xfff, 0x2db, 0xfff, 0xfff, + 0x181, 0xfff, 0x089, 0x2c5, 0xfff, 0x1f5, 0xfff, 0x2d6, + 0x507, 0xfff, 0x42d, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0x080, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, 0x3c3, 0x320, 0xfff, 0x1e1, + 0xfff, 0x0f5, 0x13b, 0xfff, 0xfff, 0xfff, 0x003, 0x4da, + 0xfff, 0xfff, 0xfff, 0x42c, 0xfff, 0xfff, 0x0cb, 0xfff, + 0x536, 0x2c3, 0xfff, 0xfff, 0xfff, 0xfff, 0x199, 0xfff, + 0xfff, 0x0c0, 0xfff, 0x01e, 0x497, 0xfff, 0xfff, 0x3e5, + 0xfff, 0xfff, 0xfff, 0x0cf, 0xfff, 0x2bd, 0xfff, 0x223, 0xfff, 0x3ff, 0xfff, 0x058, 0x174, 0x3ef, 0xfff, 0x002 }; @@ -1357,7 +1357,7 @@ int cafe_correct_ecc(unsigned char *buf, buf[i+12], buf[i+13], buf[i+14], buf[i+15]); } } - + if (chk_no_err_only(chk_syndrome_list, err_info) && diff --git a/drivers/mtd/nand/cs553x_nand.c b/drivers/mtd/nand/cs553x_nand.c index 94924d5..8296305 100644 --- a/drivers/mtd/nand/cs553x_nand.c +++ b/drivers/mtd/nand/cs553x_nand.c @@ -11,7 +11,7 @@ * published by the Free Software Foundation. * * Overview: - * This is a device driver for the NAND flash controller found on + * This is a device driver for the NAND flash controller found on * the AMD CS5535/CS5536 companion chipsets for the Geode processor. * */ @@ -303,7 +303,7 @@ static int __init cs553x_init(void) err = cs553x_init_one(i, !!(val & FLSH_MEM_IO), val & 0xFFFFFFFF); } - /* Register all devices together here. This means we can easily hack it to + /* Register all devices together here. This means we can easily hack it to do mtdconcat etc. if we want to. */ for (i = 0; i < NR_CS553X_CONTROLLERS; i++) { if (cs553x_mtd[i]) { diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index ff5cef2..8b32035 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -283,7 +283,7 @@ static void s3c2410_nand_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) { struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); - + if (cmd == NAND_CMD_NONE) return; diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index fc84ddc..c48aa79 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -406,7 +406,7 @@ static int onenand_try_interrupt_wait(struct mtd_info *mtd, int state) /* Release the irq */ free_irq(this->irq, this); - + this->wait = onenand_wait; } -- cgit v0.10.2 From 6c33cafc794d07c9254c160789120a0e98c088c9 Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Wed, 29 Nov 2006 14:26:07 +0100 Subject: [MTD] bugfix: DataFlash is not bit writable This patch fixes the "jffs2_flash_writev(): Non-contiguous write to 00825300 with mtd_dataflash" bug. Signed-off-by: Haavard Skinnemoen Signed-off-by: Artem Bityutskiy diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c index 5db7160..10a4f4e 100644 --- a/drivers/mtd/devices/mtd_dataflash.c +++ b/drivers/mtd/devices/mtd_dataflash.c @@ -480,7 +480,7 @@ add_dataflash(struct spi_device *spi, char *name, device->writesize = pagesize; device->owner = THIS_MODULE; device->type = MTD_DATAFLASH; - device->flags = MTD_CAP_NORFLASH; + device->flags = MTD_WRITEABLE; device->erase = dataflash_erase; device->read = dataflash_read; device->write = dataflash_write; -- cgit v0.10.2 From 7dcb483de3b33e74ddd2040bd7b6ba96d86a91f8 Mon Sep 17 00:00:00 2001 From: Mariusz Kozlowski Date: Fri, 1 Dec 2006 09:59:49 +0000 Subject: [MTD] [NAND] Compile fix in rfc_from4.c Signed-off-by: Mariusz Kozlowski Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/rtc_from4.c b/drivers/mtd/nand/rtc_from4.c index 02deec3..9189ec8 100644 --- a/drivers/mtd/nand/rtc_from4.c +++ b/drivers/mtd/nand/rtc_from4.c @@ -456,7 +456,7 @@ static int rtc_from4_errstat(struct mtd_info *mtd, struct nand_chip *this, rtn = nand_do_read(mtd, page, len, &retlen, buf); /* if read failed or > 1-bit error corrected */ - if (rtn || (mtd->ecc_stats.corrected - corrected) > 1) { + if (rtn || (mtd->ecc_stats.corrected - corrected) > 1) er_stat |= 1 << 1; kfree(buf); } -- cgit v0.10.2 From 0b47d654089c5ce3f2ea26a4485db9bcead1e515 Mon Sep 17 00:00:00 2001 From: Yoshinori Sato Date: Fri, 1 Dec 2006 10:01:50 +0000 Subject: [MTD] redboot partition combined fis / config problem Can't analyze FIS directory in CYGSEM_REDBOOT_FLASH_COMBINED_FIS_AND_CONFIG really. Signed-off-by: Yoshinori Sato Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse diff --git a/drivers/mtd/redboot.c b/drivers/mtd/redboot.c index 4b27721..b525921 100644 --- a/drivers/mtd/redboot.c +++ b/drivers/mtd/redboot.c @@ -110,6 +110,9 @@ static int parse_redboot_partitions(struct mtd_info *master, } } break; + } else { + /* re-calculate of real numslots */ + numslots = buf[i].size / sizeof(struct fis_image_desc); } } if (i == numslots) { -- cgit v0.10.2 From 9e86619b6d58806d1a8b67c12c5e3e3a74818fb6 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Tue, 5 Dec 2006 07:41:09 +0100 Subject: mmc: pxamci compilation fix since commit fcaf71fd51f9cfc504455d3e19ec242e4b2073ed struct mmc_host does not have a dev field. Retrieve the device with mmc_dev() instead. Signed-off-by: Sascha Hauer Signed-off-by: Pierre Ossman diff --git a/drivers/mmc/pxamci.c b/drivers/mmc/pxamci.c index 471e9f4..45a9283 100644 --- a/drivers/mmc/pxamci.c +++ b/drivers/mmc/pxamci.c @@ -355,7 +355,7 @@ static int pxamci_get_ro(struct mmc_host *mmc) struct pxamci_host *host = mmc_priv(mmc); if (host->pdata && host->pdata->get_ro) - return host->pdata->get_ro(mmc->dev); + return host->pdata->get_ro(mmc_dev(mmc)); /* Host doesn't support read only detection so assume writeable */ return 0; } @@ -383,7 +383,7 @@ static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) host->power_mode = ios->power_mode; if (host->pdata && host->pdata->setpower) - host->pdata->setpower(mmc->dev, ios->vdd); + host->pdata->setpower(mmc_dev(mmc), ios->vdd); if (ios->power_mode == MMC_POWER_ON) host->cmdat |= CMDAT_INIT; -- cgit v0.10.2 From bf8c80a6a5153e7f170f240731c650eeed6d78ff Mon Sep 17 00:00:00 2001 From: Yoichi Yuasa Date: Tue, 5 Dec 2006 07:43:38 +0100 Subject: mmc: fix au1xmmc build error This patch has fixed the following build error abou au1xmmc. drivers/mmc/au1xmmc.c: In function `au1xmmc_poll_event': drivers/mmc/au1xmmc.c:835: warning: unused variable `status' drivers/mmc/au1xmmc.c: At top level: drivers/mmc/au1xmmc.c:878: error: parse error before "const" drivers/mmc/au1xmmc.c: In function `au1xmmc_probe': drivers/mmc/au1xmmc.c:909: error: `au1xmmc_ops' undeclared (first use in this function) drivers/mmc/au1xmmc.c:909: error: (Each undeclared identifier is reported only once drivers/mmc/au1xmmc.c:909: error: for each function it appears in.) drivers/mmc/au1xmmc.c: At top level: drivers/mmc/au1xmmc.c:659: warning: 'au1xmmc_request' defined but not used drivers/mmc/au1xmmc.c:719: warning: 'au1xmmc_set_ios' defined but not used make[2]: *** [drivers/mmc/au1xmmc.o] Error 1 make[1]: *** [drivers/mmc] Error 2 make: *** [drivers] Error 2 Signed-off-by: Yoichi Yuasa Signed-off-by: Pierre Ossman diff --git a/drivers/mmc/au1xmmc.c b/drivers/mmc/au1xmmc.c index 447fba5..800527c 100644 --- a/drivers/mmc/au1xmmc.c +++ b/drivers/mmc/au1xmmc.c @@ -875,7 +875,7 @@ static void au1xmmc_init_dma(struct au1xmmc_host *host) host->rx_chan = rxchan; } -struct const mmc_host_ops au1xmmc_ops = { +static const struct mmc_host_ops au1xmmc_ops = { .request = au1xmmc_request, .set_ios = au1xmmc_set_ios, }; -- cgit v0.10.2 From 418b2e56b8a61ea85f7a9c5d327e1a2c61d1b2db Mon Sep 17 00:00:00 2001 From: Timo Lindhorst Date: Tue, 5 Dec 2006 15:23:33 +0100 Subject: [MTD] NAND: use SmartMedia ECC byte order for ndfc Select MTD_NAND_ECC_SMC (ECC byte order according to the Smart Media Specification) if MTD_NAND_NDFC is used. Using the wrong byte order causes fatal, unnoticed data damage. For further information see: http://lists.infradead.org/pipermail/linux-mtd/2006-November/016920.html Signed-off-by: Timo Lindhorst Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 0662775..358f55a 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -133,6 +133,7 @@ config MTD_NAND_S3C2410_HWECC config MTD_NAND_NDFC tristate "NDFC NanD Flash Controller" depends on MTD_NAND && 44x + select MTD_NAND_ECC_SMC help NDFC Nand Flash Controllers are integrated in EP44x SoCs -- cgit v0.10.2 From 36b12fb709d229f277efbbe710031d5a429b2412 Mon Sep 17 00:00:00 2001 From: Dave Kleikamp Date: Wed, 6 Dec 2006 17:48:32 -0600 Subject: JFS: Fix conflicting superblock flags JFS_NOINTEGRITY and JFS_USRQUOTA are defined to be the same value. Change JFS_NOINTEGRITY to 0x40 and re-order the flags in the header file to avoid repeating this problem. Signed-off-by: Dave Kleikamp diff --git a/fs/jfs/jfs_filsys.h b/fs/jfs/jfs_filsys.h index eb550b3..38f70ac 100644 --- a/fs/jfs/jfs_filsys.h +++ b/fs/jfs/jfs_filsys.h @@ -29,31 +29,21 @@ /* * file system option (superblock flag) */ -/* mount time flag to disable journaling to disk */ -#define JFS_NOINTEGRITY 0x00000010 + +/* directory option */ +#define JFS_UNICODE 0x00000001 /* unicode name */ /* mount time flags for error handling */ #define JFS_ERR_REMOUNT_RO 0x00000002 /* remount read-only */ #define JFS_ERR_CONTINUE 0x00000004 /* continue */ #define JFS_ERR_PANIC 0x00000008 /* panic */ +/* Quota support */ #define JFS_USRQUOTA 0x00000010 #define JFS_GRPQUOTA 0x00000020 -/* platform option (conditional compilation) */ -#define JFS_AIX 0x80000000 /* AIX support */ -/* POSIX name/directory support */ - -#define JFS_OS2 0x40000000 /* OS/2 support */ -/* case-insensitive name/directory support */ - -#define JFS_DFS 0x20000000 /* DCE DFS LFS support */ - -#define JFS_LINUX 0x10000000 /* Linux support */ -/* case-sensitive name/directory support */ - -/* directory option */ -#define JFS_UNICODE 0x00000001 /* unicode name */ +/* mount time flag to disable journaling to disk */ +#define JFS_NOINTEGRITY 0x00000040 /* commit option */ #define JFS_COMMIT 0x00000f00 /* commit option mask */ @@ -61,6 +51,7 @@ #define JFS_LAZYCOMMIT 0x00000200 /* lazy commit */ #define JFS_TMPFS 0x00000400 /* temporary file system - * do not log/commit: + * Never implemented */ /* log logical volume option */ @@ -74,16 +65,25 @@ #define JFS_SPARSE 0x00020000 /* sparse regular file */ /* DASD Limits F226941 */ -#define JFS_DASD_ENABLED 0x00040000 /* DASD limits enabled */ -#define JFS_DASD_PRIME 0x00080000 /* Prime DASD usage on boot */ +#define JFS_DASD_ENABLED 0x00040000 /* DASD limits enabled */ +#define JFS_DASD_PRIME 0x00080000 /* Prime DASD usage on boot */ /* big endian flag */ -#define JFS_SWAP_BYTES 0x00100000 /* running on big endian computer */ +#define JFS_SWAP_BYTES 0x00100000 /* running on big endian computer */ /* Directory index */ -#define JFS_DIR_INDEX 0x00200000 /* Persistent index for */ - /* directory entries */ +#define JFS_DIR_INDEX 0x00200000 /* Persistent index for */ +/* platform options */ +#define JFS_LINUX 0x10000000 /* Linux support */ +#define JFS_DFS 0x20000000 /* DCE DFS LFS support */ +/* Never implemented */ + +#define JFS_OS2 0x40000000 /* OS/2 support */ +/* case-insensitive name/directory support */ + +#define JFS_AIX 0x80000000 /* AIX support */ +/* POSIX name/directory support - Never implemented*/ /* * buffer cache configuration -- cgit v0.10.2 From 776c9443e28dddbde9b513db6cb8221c45b3a269 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=3D=3Futf-8=3Fq=3FMichel=5FD=3DC3=3DA4nzer=3F=3D?= Date: Tue, 24 Oct 2006 22:24:38 +1000 Subject: drm: add support for secondary vertical blank interrupt to DRM core Signed-off-by: Dave Airlie diff --git a/drivers/char/drm/drm.h b/drivers/char/drm/drm.h index 5642ac4..077d0b1 100644 --- a/drivers/char/drm/drm.h +++ b/drivers/char/drm/drm.h @@ -465,10 +465,12 @@ typedef struct drm_irq_busid { typedef enum { _DRM_VBLANK_ABSOLUTE = 0x0, /**< Wait for specific vblank sequence number */ _DRM_VBLANK_RELATIVE = 0x1, /**< Wait for given number of vblanks */ + _DRM_VBLANK_SECONDARY = 0x20000000, /**< Secondary display controller */ _DRM_VBLANK_SIGNAL = 0x40000000 /**< Send signal instead of blocking */ } drm_vblank_seq_type_t; -#define _DRM_VBLANK_FLAGS_MASK _DRM_VBLANK_SIGNAL +#define _DRM_VBLANK_TYPES_MASK (_DRM_VBLANK_ABSOLUTE | _DRM_VBLANK_RELATIVE) +#define _DRM_VBLANK_FLAGS_MASK (_DRM_VBLANK_SIGNAL | _DRM_VBLANK_SECONDARY) struct drm_wait_vblank_request { drm_vblank_seq_type_t type; diff --git a/drivers/char/drm/drmP.h b/drivers/char/drm/drmP.h index 7690a59..d7135d4 100644 --- a/drivers/char/drm/drmP.h +++ b/drivers/char/drm/drmP.h @@ -97,6 +97,7 @@ #define DRIVER_IRQ_VBL 0x100 #define DRIVER_DMA_QUEUE 0x200 #define DRIVER_FB_DMA 0x400 +#define DRIVER_IRQ_VBL2 0x800 /***********************************************************************/ /** \name Begin the DRM... */ @@ -562,6 +563,7 @@ struct drm_driver { void (*kernel_context_switch_unlock) (struct drm_device * dev, drm_lock_t *lock); int (*vblank_wait) (struct drm_device * dev, unsigned int *sequence); + int (*vblank_wait2) (struct drm_device * dev, unsigned int *sequence); int (*dri_library_name) (struct drm_device *dev, char *buf); /** @@ -708,8 +710,10 @@ typedef struct drm_device { wait_queue_head_t vbl_queue; /**< VBLANK wait queue */ atomic_t vbl_received; + atomic_t vbl_received2; /**< number of secondary VBLANK interrupts */ spinlock_t vbl_lock; drm_vbl_sig_t vbl_sigs; /**< signal list to send on VBLANK */ + drm_vbl_sig_t vbl_sigs2; /**< signals to send on secondary VBLANK */ unsigned int vbl_pending; /*@} */ diff --git a/drivers/char/drm/drm_core.h b/drivers/char/drm/drm_core.h index f4f9db6c..3167390 100644 --- a/drivers/char/drm/drm_core.h +++ b/drivers/char/drm/drm_core.h @@ -24,11 +24,11 @@ #define CORE_NAME "drm" #define CORE_DESC "DRM shared core routines" -#define CORE_DATE "20051102" +#define CORE_DATE "20060810" #define DRM_IF_MAJOR 1 -#define DRM_IF_MINOR 2 +#define DRM_IF_MINOR 3 #define CORE_MAJOR 1 -#define CORE_MINOR 0 -#define CORE_PATCHLEVEL 1 +#define CORE_MINOR 1 +#define CORE_PATCHLEVEL 0 diff --git a/drivers/char/drm/drm_irq.c b/drivers/char/drm/drm_irq.c index 4553a3a..3c77756 100644 --- a/drivers/char/drm/drm_irq.c +++ b/drivers/char/drm/drm_irq.c @@ -121,6 +121,7 @@ static int drm_irq_install(drm_device_t * dev) spin_lock_init(&dev->vbl_lock); INIT_LIST_HEAD(&dev->vbl_sigs.head); + INIT_LIST_HEAD(&dev->vbl_sigs2.head); dev->vbl_pending = 0; } @@ -248,9 +249,7 @@ int drm_wait_vblank(DRM_IOCTL_ARGS) struct timeval now; int ret = 0; unsigned int flags; - - if (!drm_core_check_feature(dev, DRIVER_IRQ_VBL)) - return -EINVAL; + atomic_t *seq; if (!dev->irq) return -EINVAL; @@ -258,9 +257,26 @@ int drm_wait_vblank(DRM_IOCTL_ARGS) if (copy_from_user(&vblwait, argp, sizeof(vblwait))) return -EFAULT; - switch (vblwait.request.type & ~_DRM_VBLANK_FLAGS_MASK) { + if (vblwait.request.type & + ~(_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK)) { + DRM_ERROR("Unsupported type value 0x%x, supported mask 0x%x\n", + vblwait.request.type, + (_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK)); + return -EINVAL; + } + + flags = vblwait.request.type & _DRM_VBLANK_FLAGS_MASK; + + if (!drm_core_check_feature(dev, (flags & _DRM_VBLANK_SECONDARY) ? + DRIVER_IRQ_VBL2 : DRIVER_IRQ_VBL)) + return -EINVAL; + + seq = (flags & _DRM_VBLANK_SECONDARY) ? &dev->vbl_received2 : + &dev->vbl_received; + + switch (vblwait.request.type & _DRM_VBLANK_TYPES_MASK) { case _DRM_VBLANK_RELATIVE: - vblwait.request.sequence += atomic_read(&dev->vbl_received); + vblwait.request.sequence += atomic_read(seq); vblwait.request.type &= ~_DRM_VBLANK_RELATIVE; case _DRM_VBLANK_ABSOLUTE: break; @@ -268,13 +284,13 @@ int drm_wait_vblank(DRM_IOCTL_ARGS) return -EINVAL; } - flags = vblwait.request.type & _DRM_VBLANK_FLAGS_MASK; - if (flags & _DRM_VBLANK_SIGNAL) { unsigned long irqflags; + drm_vbl_sig_t *vbl_sigs = (flags & _DRM_VBLANK_SECONDARY) + ? &dev->vbl_sigs2 : &dev->vbl_sigs; drm_vbl_sig_t *vbl_sig; - vblwait.reply.sequence = atomic_read(&dev->vbl_received); + vblwait.reply.sequence = atomic_read(seq); spin_lock_irqsave(&dev->vbl_lock, irqflags); @@ -282,7 +298,7 @@ int drm_wait_vblank(DRM_IOCTL_ARGS) * for the same vblank sequence number; nothing to be done in * that case */ - list_for_each_entry(vbl_sig, &dev->vbl_sigs.head, head) { + list_for_each_entry(vbl_sig, &vbl_sigs->head, head) { if (vbl_sig->sequence == vblwait.request.sequence && vbl_sig->info.si_signo == vblwait.request.signal && vbl_sig->task == current) { @@ -315,11 +331,14 @@ int drm_wait_vblank(DRM_IOCTL_ARGS) spin_lock_irqsave(&dev->vbl_lock, irqflags); - list_add_tail((struct list_head *)vbl_sig, &dev->vbl_sigs.head); + list_add_tail((struct list_head *)vbl_sig, &vbl_sigs->head); spin_unlock_irqrestore(&dev->vbl_lock, irqflags); } else { - if (dev->driver->vblank_wait) + if (flags & _DRM_VBLANK_SECONDARY) { + if (dev->driver->vblank_wait2) + ret = dev->driver->vblank_wait2(dev, &vblwait.request.sequence); + } else if (dev->driver->vblank_wait) ret = dev->driver->vblank_wait(dev, &vblwait.request.sequence); @@ -347,25 +366,32 @@ int drm_wait_vblank(DRM_IOCTL_ARGS) */ void drm_vbl_send_signals(drm_device_t * dev) { - struct list_head *list, *tmp; - drm_vbl_sig_t *vbl_sig; - unsigned int vbl_seq = atomic_read(&dev->vbl_received); unsigned long flags; + int i; spin_lock_irqsave(&dev->vbl_lock, flags); - list_for_each_safe(list, tmp, &dev->vbl_sigs.head) { - vbl_sig = list_entry(list, drm_vbl_sig_t, head); - if ((vbl_seq - vbl_sig->sequence) <= (1 << 23)) { - vbl_sig->info.si_code = vbl_seq; - send_sig_info(vbl_sig->info.si_signo, &vbl_sig->info, - vbl_sig->task); + for (i = 0; i < 2; i++) { + struct list_head *list, *tmp; + drm_vbl_sig_t *vbl_sig; + drm_vbl_sig_t *vbl_sigs = i ? &dev->vbl_sigs2 : &dev->vbl_sigs; + unsigned int vbl_seq = atomic_read(i ? &dev->vbl_received2 : + &dev->vbl_received); + + list_for_each_safe(list, tmp, &vbl_sigs->head) { + vbl_sig = list_entry(list, drm_vbl_sig_t, head); + if ((vbl_seq - vbl_sig->sequence) <= (1 << 23)) { + vbl_sig->info.si_code = vbl_seq; + send_sig_info(vbl_sig->info.si_signo, + &vbl_sig->info, vbl_sig->task); - list_del(list); + list_del(list); - drm_free(vbl_sig, sizeof(*vbl_sig), DRM_MEM_DRIVER); + drm_free(vbl_sig, sizeof(*vbl_sig), + DRM_MEM_DRIVER); - dev->vbl_pending--; + dev->vbl_pending--; + } } } -- cgit v0.10.2 From 68815bad7239989d92f315c10d9ef65a11945a75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=3D=3Futf-8=3Fq=3FMichel=5FD=3DC3=3DA4nzer=3F=3D?= Date: Tue, 24 Oct 2006 22:28:51 +1000 Subject: drm: add support for secondary vertical blank interrupt to i915 When the vertical blank interrupt is enabled for both pipes, pipe A is considered primary and pipe B secondary. When it's only enabled for one pipe, it's always considered primary for backwards compatibility. Signed-off-by: Dave Airlie diff --git a/drivers/char/drm/i915_drv.c b/drivers/char/drm/i915_drv.c index 8e2e609..85bcc27 100644 --- a/drivers/char/drm/i915_drv.c +++ b/drivers/char/drm/i915_drv.c @@ -44,12 +44,14 @@ static struct drm_driver driver = { */ .driver_features = DRIVER_USE_AGP | DRIVER_REQUIRE_AGP | /* DRIVER_USE_MTRR |*/ - DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_IRQ_VBL, + DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_IRQ_VBL | + DRIVER_IRQ_VBL2, .load = i915_driver_load, .lastclose = i915_driver_lastclose, .preclose = i915_driver_preclose, .device_is_agp = i915_driver_device_is_agp, .vblank_wait = i915_driver_vblank_wait, + .vblank_wait2 = i915_driver_vblank_wait2, .irq_preinstall = i915_driver_irq_preinstall, .irq_postinstall = i915_driver_irq_postinstall, .irq_uninstall = i915_driver_irq_uninstall, diff --git a/drivers/char/drm/i915_drv.h b/drivers/char/drm/i915_drv.h index fdc2bf1..5f0c4fa 100644 --- a/drivers/char/drm/i915_drv.h +++ b/drivers/char/drm/i915_drv.h @@ -117,6 +117,7 @@ extern int i915_irq_emit(DRM_IOCTL_ARGS); extern int i915_irq_wait(DRM_IOCTL_ARGS); extern int i915_driver_vblank_wait(drm_device_t *dev, unsigned int *sequence); +extern int i915_driver_vblank_wait2(drm_device_t *dev, unsigned int *sequence); extern irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS); extern void i915_driver_irq_preinstall(drm_device_t * dev); extern void i915_driver_irq_postinstall(drm_device_t * dev); diff --git a/drivers/char/drm/i915_irq.c b/drivers/char/drm/i915_irq.c index 0d4a162..33d401876 100644 --- a/drivers/char/drm/i915_irq.c +++ b/drivers/char/drm/i915_irq.c @@ -60,7 +60,16 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) DRM_WAKEUP(&dev_priv->irq_queue); if (temp & (VSYNC_PIPEA_FLAG | VSYNC_PIPEB_FLAG)) { - atomic_inc(&dev->vbl_received); + if ((dev_priv->vblank_pipe & + (DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B)) + == (DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B)) { + if (temp & VSYNC_PIPEA_FLAG) + atomic_inc(&dev->vbl_received); + if (temp & VSYNC_PIPEB_FLAG) + atomic_inc(&dev->vbl_received2); + } else + atomic_inc(&dev->vbl_received); + DRM_WAKEUP(&dev->vbl_queue); drm_vbl_send_signals(dev); } @@ -120,7 +129,8 @@ static int i915_wait_irq(drm_device_t * dev, int irq_nr) return ret; } -int i915_driver_vblank_wait(drm_device_t *dev, unsigned int *sequence) +static int i915_driver_vblank_do_wait(drm_device_t *dev, unsigned int *sequence, + atomic_t *counter) { drm_i915_private_t *dev_priv = dev->dev_private; unsigned int cur_vblank; @@ -132,7 +142,7 @@ int i915_driver_vblank_wait(drm_device_t *dev, unsigned int *sequence) } DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ, - (((cur_vblank = atomic_read(&dev->vbl_received)) + (((cur_vblank = atomic_read(counter)) - *sequence) <= (1<<23))); *sequence = cur_vblank; @@ -141,6 +151,16 @@ int i915_driver_vblank_wait(drm_device_t *dev, unsigned int *sequence) } +int i915_driver_vblank_wait(drm_device_t *dev, unsigned int *sequence) +{ + return i915_driver_vblank_do_wait(dev, sequence, &dev->vbl_received); +} + +int i915_driver_vblank_wait2(drm_device_t *dev, unsigned int *sequence) +{ + return i915_driver_vblank_do_wait(dev, sequence, &dev->vbl_received2); +} + /* Needs the lock as it touches the ring. */ int i915_irq_emit(DRM_IOCTL_ARGS) -- cgit v0.10.2 From bea5679f9cb97b7e41786c8500df56665cd21e56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=3D=3Futf-8=3Fq=3FMichel=5FD=3DC3=3DA4nzer=3F=3D?= Date: Tue, 24 Oct 2006 23:04:19 +1000 Subject: drm: Add support for tracking drawable information to core Actually make the existing ioctls for adding and removing drawables do something useful, and add another ioctl for the X server to update drawable information. The only kind of drawable information tracked so far is cliprects. Only reallocate cliprect memory if the number of cliprects changes. Also improve diagnostic output. hook up drm ioctl update draw export drm_get_drawable_info symbol Signed-off-by: Dave Airlie diff --git a/drivers/char/drm/drm.h b/drivers/char/drm/drm.h index 077d0b1..07a69902 100644 --- a/drivers/char/drm/drm.h +++ b/drivers/char/drm/drm.h @@ -117,6 +117,14 @@ typedef struct drm_clip_rect { } drm_clip_rect_t; /** + * Drawable information. + */ +typedef struct drm_drawable_info { + unsigned int num_rects; + drm_clip_rect_t *rects; +} drm_drawable_info_t; + +/** * Texture region, */ typedef struct drm_tex_region { @@ -444,6 +452,20 @@ typedef struct drm_draw { } drm_draw_t; /** + * DRM_IOCTL_UPDATE_DRAW ioctl argument type. + */ +typedef enum { + DRM_DRAWABLE_CLIPRECTS, +} drm_drawable_info_type_t; + +typedef struct drm_update_draw { + drm_drawable_t handle; + unsigned int type; + unsigned int num; + unsigned long long data; +} drm_update_draw_t; + +/** * DRM_IOCTL_GET_MAGIC and DRM_IOCTL_AUTH_MAGIC ioctl argument type. */ typedef struct drm_auth { @@ -625,6 +647,8 @@ typedef struct drm_set_version { #define DRM_IOCTL_WAIT_VBLANK DRM_IOWR(0x3a, drm_wait_vblank_t) +#define DRM_IOCTL_UPDATE_DRAW DRM_IOW(0x3f, drm_update_draw_t) + /** * Device specific ioctls should only be in their respective headers * The device specific ioctl range is from 0x40 to 0x79. diff --git a/drivers/char/drm/drmP.h b/drivers/char/drm/drmP.h index d7135d4..01e1f25 100644 --- a/drivers/char/drm/drmP.h +++ b/drivers/char/drm/drmP.h @@ -742,6 +742,15 @@ typedef struct drm_device { drm_local_map_t *agp_buffer_map; unsigned int agp_buffer_token; drm_head_t primary; /**< primary screen head */ + + /** \name Drawable information */ + /*@{ */ + spinlock_t drw_lock; + unsigned int drw_bitfield_length; + u32 *drw_bitfield; + unsigned int drw_info_length; + drm_drawable_info_t **drw_info; + /*@} */ } drm_device_t; static __inline__ int drm_core_check_feature(struct drm_device *dev, @@ -889,6 +898,10 @@ extern int drm_adddraw(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); extern int drm_rmdraw(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); +extern int drm_update_drawable_info(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern drm_drawable_info_t *drm_get_drawable_info(drm_device_t *dev, + drm_drawable_t id); /* Authentication IOCTL support (drm_auth.h) */ extern int drm_getmagic(struct inode *inode, struct file *filp, diff --git a/drivers/char/drm/drm_drawable.c b/drivers/char/drm/drm_drawable.c index 7857453..e5f97de 100644 --- a/drivers/char/drm/drm_drawable.c +++ b/drivers/char/drm/drm_drawable.c @@ -4,6 +4,7 @@ * * \author Rickard E. (Rik) Faith * \author Gareth Hughes + * \author Michel Dänzer */ /* @@ -36,21 +37,234 @@ #include "drmP.h" /** No-op. */ -int drm_adddraw(struct inode *inode, struct file *filp, - unsigned int cmd, unsigned long arg) +int drm_adddraw(DRM_IOCTL_ARGS) { + DRM_DEVICE; + unsigned long irqflags; + int i, j = 0; drm_draw_t draw; - draw.handle = 0; /* NOOP */ + spin_lock_irqsave(&dev->drw_lock, irqflags); + + for (i = 0; i < dev->drw_bitfield_length; i++) { + u32 bitfield = dev->drw_bitfield[i]; + + if (bitfield == ~0) + continue; + + for (; j < sizeof(bitfield); j++) + if (!(bitfield & (1 << j))) + goto done; + } +done: + + if (i == dev->drw_bitfield_length) { + u32 *new_bitfield = drm_realloc(dev->drw_bitfield, i * 4, + (i + 1) * 4, DRM_MEM_BUFS); + + if (!new_bitfield) { + DRM_ERROR("Failed to allocate new drawable bitfield\n"); + spin_unlock_irqrestore(&dev->drw_lock, irqflags); + return DRM_ERR(ENOMEM); + } + + if (32 * (i + 1) > dev->drw_info_length) { + void *new_info = drm_realloc(dev->drw_info, + dev->drw_info_length * + sizeof(drm_drawable_info_t*), + 32 * (i + 1) * + sizeof(drm_drawable_info_t*), + DRM_MEM_BUFS); + + if (!new_info) { + DRM_ERROR("Failed to allocate new drawable info" + " array\n"); + + drm_free(new_bitfield, (i + 1) * 4, DRM_MEM_BUFS); + spin_unlock_irqrestore(&dev->drw_lock, irqflags); + return DRM_ERR(ENOMEM); + } + + dev->drw_info = (drm_drawable_info_t**)new_info; + } + + new_bitfield[i] = 0; + + dev->drw_bitfield = new_bitfield; + dev->drw_bitfield_length++; + } + + dev->drw_bitfield[i] |= 1 << j; + + draw.handle = i * sizeof(u32) + j; DRM_DEBUG("%d\n", draw.handle); - if (copy_to_user((drm_draw_t __user *) arg, &draw, sizeof(draw))) - return -EFAULT; + + dev->drw_info[draw.handle] = NULL; + + spin_unlock_irqrestore(&dev->drw_lock, irqflags); + + DRM_COPY_TO_USER_IOCTL((drm_draw_t __user *)data, draw, sizeof(draw)); + return 0; } /** No-op. */ -int drm_rmdraw(struct inode *inode, struct file *filp, - unsigned int cmd, unsigned long arg) +int drm_rmdraw(DRM_IOCTL_ARGS) { - return 0; /* NOOP */ + DRM_DEVICE; + drm_draw_t draw; + unsigned int idx, mod; + unsigned long irqflags; + + DRM_COPY_FROM_USER_IOCTL(draw, (drm_draw_t __user *) data, + sizeof(draw)); + + idx = draw.handle / 32; + mod = draw.handle % 32; + + spin_lock_irqsave(&dev->drw_lock, irqflags); + + if (idx >= dev->drw_bitfield_length || + !(dev->drw_bitfield[idx] & (1 << mod))) { + DRM_DEBUG("No such drawable %d\n", draw.handle); + spin_unlock_irqrestore(&dev->drw_lock, irqflags); + return 0; + } + + dev->drw_bitfield[idx] &= ~(1 << mod); + + if (idx == (dev->drw_bitfield_length - 1)) { + while (idx >= 0 && !dev->drw_bitfield[idx]) + --idx; + + if (idx != draw.handle / 32) { + u32 *new_bitfield = drm_realloc(dev->drw_bitfield, + dev->drw_bitfield_length * 4, + (idx + 1) * 4, + DRM_MEM_BUFS); + + if (new_bitfield || idx == -1) { + dev->drw_bitfield = new_bitfield; + dev->drw_bitfield_length = idx + 1; + } + } + } + + if (32 * dev->drw_bitfield_length < dev->drw_info_length) { + void *new_info = drm_realloc(dev->drw_info, + dev->drw_info_length * + sizeof(drm_drawable_info_t*), + 32 * dev->drw_bitfield_length * + sizeof(drm_drawable_info_t*), + DRM_MEM_BUFS); + + if (new_info || !dev->drw_bitfield_length) { + dev->drw_info = (drm_drawable_info_t**)new_info; + dev->drw_info_length = 32 * dev->drw_bitfield_length; + } + } + + spin_unlock_irqrestore(&dev->drw_lock, irqflags); + + DRM_DEBUG("%d\n", draw.handle); + return 0; +} + +int drm_update_drawable_info(DRM_IOCTL_ARGS) { + DRM_DEVICE; + drm_update_draw_t update; + unsigned int id, idx, mod; + unsigned long irqflags; + drm_drawable_info_t *info; + void *new_data; + + DRM_COPY_FROM_USER_IOCTL(update, (drm_update_draw_t __user *) data, + sizeof(update)); + + id = update.handle; + idx = id / 32; + mod = id % 32; + + spin_lock_irqsave(&dev->drw_lock, irqflags); + + if (idx >= dev->drw_bitfield_length || + !(dev->drw_bitfield[idx] & (1 << mod))) { + DRM_ERROR("No such drawable %d\n", update.handle); + spin_unlock_irqrestore(&dev->drw_lock, irqflags); + return DRM_ERR(EINVAL); + } + + info = dev->drw_info[id]; + + if (!info) { + info = drm_calloc(1, sizeof(drm_drawable_info_t), DRM_MEM_BUFS); + + if (!info) { + DRM_ERROR("Failed to allocate drawable info memory\n"); + spin_unlock_irqrestore(&dev->drw_lock, irqflags); + return DRM_ERR(ENOMEM); + } + + dev->drw_info[id] = info; + } + + switch (update.type) { + case DRM_DRAWABLE_CLIPRECTS: + if (update.num != info->num_rects) { + new_data = drm_alloc(update.num * + sizeof(drm_clip_rect_t), + DRM_MEM_BUFS); + + if (!new_data) { + DRM_ERROR("Can't allocate cliprect memory\n"); + spin_unlock_irqrestore(&dev->drw_lock, irqflags); + return DRM_ERR(ENOMEM); + } + + info->rects = new_data; + } + + if (DRM_COPY_FROM_USER(info->rects, + (drm_clip_rect_t __user *) + (unsigned long)update.data, + update.num * sizeof(drm_clip_rect_t))) { + DRM_ERROR("Can't copy cliprects from userspace\n"); + spin_unlock_irqrestore(&dev->drw_lock, irqflags); + return DRM_ERR(EFAULT); + } + + if (update.num != info->num_rects) { + drm_free(info->rects, info->num_rects * + sizeof(drm_clip_rect_t), DRM_MEM_BUFS); + info->num_rects = update.num; + } + + DRM_DEBUG("Updated %d cliprects for drawable %d\n", + info->num_rects, id); + break; + default: + DRM_ERROR("Invalid update type %d\n", update.type); + spin_unlock_irqrestore(&dev->drw_lock, irqflags); + return DRM_ERR(EINVAL); + } + + spin_unlock_irqrestore(&dev->drw_lock, irqflags); + + return 0; +} + +/** + * Caller must hold the drawable spinlock! + */ +drm_drawable_info_t *drm_get_drawable_info(drm_device_t *dev, drm_drawable_t id) { + unsigned int idx = id / 32, mod = id % 32; + + if (idx >= dev->drw_bitfield_length || + !(dev->drw_bitfield[idx] & (1 << mod))) { + DRM_DEBUG("No such drawable %d\n", id); + return NULL; + } + + return dev->drw_info[id]; } +EXPORT_SYMBOL(drm_get_drawable_info); diff --git a/drivers/char/drm/drm_drv.c b/drivers/char/drm/drm_drv.c index b366c5b..59de4a0 100644 --- a/drivers/char/drm/drm_drv.c +++ b/drivers/char/drm/drm_drv.c @@ -116,6 +116,8 @@ static drm_ioctl_desc_t drm_ioctls[] = { [DRM_IOCTL_NR(DRM_IOCTL_SG_FREE)] = {drm_sg_free, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY}, [DRM_IOCTL_NR(DRM_IOCTL_WAIT_VBLANK)] = {drm_wait_vblank, 0}, + + [DRM_IOCTL_NR(DRM_IOCTL_UPDATE_DRAW)] = {drm_update_drawable_info, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY}, }; #define DRIVER_IOCTL_COUNT ARRAY_SIZE( drm_ioctls ) diff --git a/drivers/char/drm/drm_stub.c b/drivers/char/drm/drm_stub.c index 7b1d4e8..6f748e1 100644 --- a/drivers/char/drm/drm_stub.c +++ b/drivers/char/drm/drm_stub.c @@ -60,6 +60,7 @@ static int drm_fill_in_dev(drm_device_t * dev, struct pci_dev *pdev, int retcode; spin_lock_init(&dev->count_lock); + spin_lock_init(&dev->drw_lock); init_timer(&dev->timer); mutex_init(&dev->struct_mutex); mutex_init(&dev->ctxlist_mutex); -- cgit v0.10.2 From 2e54a007622ac75d63bdc1dd71d435446293f4a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=3D=3Futf-8=3Fq=3FMichel=5FD=3DC3=3DA4nzer=3F=3D?= Date: Tue, 24 Oct 2006 23:08:16 +1000 Subject: drm: Add support for interrupt triggered driver callback with lock held to DRM core. Signed-off-by: Dave Airlie diff --git a/drivers/char/drm/drmP.h b/drivers/char/drm/drmP.h index 01e1f25..2f18329 100644 --- a/drivers/char/drm/drmP.h +++ b/drivers/char/drm/drmP.h @@ -715,6 +715,8 @@ typedef struct drm_device { drm_vbl_sig_t vbl_sigs; /**< signal list to send on VBLANK */ drm_vbl_sig_t vbl_sigs2; /**< signals to send on secondary VBLANK */ unsigned int vbl_pending; + spinlock_t tasklet_lock; /**< For drm_locked_tasklet */ + void (*locked_tasklet_func)(struct drm_device *dev); /*@} */ cycles_t ctx_start; @@ -966,6 +968,7 @@ extern int drm_wait_vblank(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); extern int drm_vblank_wait(drm_device_t * dev, unsigned int *vbl_seq); extern void drm_vbl_send_signals(drm_device_t * dev); +extern void drm_locked_tasklet(drm_device_t *dev, void(*func)(drm_device_t*)); /* AGP/GART support (drm_agpsupport.h) */ extern drm_agp_head_t *drm_agp_init(drm_device_t * dev); diff --git a/drivers/char/drm/drm_irq.c b/drivers/char/drm/drm_irq.c index 3c77756..2b10e5b 100644 --- a/drivers/char/drm/drm_irq.c +++ b/drivers/char/drm/drm_irq.c @@ -119,6 +119,7 @@ static int drm_irq_install(drm_device_t * dev) init_waitqueue_head(&dev->vbl_queue); spin_lock_init(&dev->vbl_lock); + spin_lock_init(&dev->tasklet_lock); INIT_LIST_HEAD(&dev->vbl_sigs.head); INIT_LIST_HEAD(&dev->vbl_sigs2.head); @@ -176,6 +177,8 @@ int drm_irq_uninstall(drm_device_t * dev) free_irq(dev->irq, dev); + dev->locked_tasklet_func = NULL; + return 0; } @@ -399,3 +402,76 @@ void drm_vbl_send_signals(drm_device_t * dev) } EXPORT_SYMBOL(drm_vbl_send_signals); + +/** + * Tasklet wrapper function. + * + * \param data DRM device in disguise. + * + * Attempts to grab the HW lock and calls the driver callback on success. On + * failure, leave the lock marked as contended so the callback can be called + * from drm_unlock(). + */ +static void drm_locked_tasklet_func(unsigned long data) +{ + drm_device_t *dev = (drm_device_t*)data; + unsigned long irqflags; + + spin_lock_irqsave(&dev->tasklet_lock, irqflags); + + if (!dev->locked_tasklet_func || + !drm_lock_take(&dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT)) { + spin_unlock_irqrestore(&dev->tasklet_lock, irqflags); + return; + } + + dev->lock.lock_time = jiffies; + atomic_inc(&dev->counts[_DRM_STAT_LOCKS]); + + dev->locked_tasklet_func(dev); + + drm_lock_free(dev, &dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT); + + dev->locked_tasklet_func = NULL; + + spin_unlock_irqrestore(&dev->tasklet_lock, irqflags); +} + +/** + * Schedule a tasklet to call back a driver hook with the HW lock held. + * + * \param dev DRM device. + * \param func Driver callback. + * + * This is intended for triggering actions that require the HW lock from an + * interrupt handler. The lock will be grabbed ASAP after the interrupt handler + * completes. Note that the callback may be called from interrupt or process + * context, it must not make any assumptions about this. Also, the HW lock will + * be held with the kernel context or any client context. + */ +void drm_locked_tasklet(drm_device_t *dev, void (*func)(drm_device_t*)) +{ + unsigned long irqflags; + static DECLARE_TASKLET(drm_tasklet, drm_locked_tasklet_func, 0); + + if (test_bit(TASKLET_STATE_SCHED, &drm_tasklet.state)) + return; + + spin_lock_irqsave(&dev->tasklet_lock, irqflags); + + if (dev->locked_tasklet_func) { + spin_unlock_irqrestore(&dev->tasklet_lock, irqflags); + return; + } + + dev->locked_tasklet_func = func; + + spin_unlock_irqrestore(&dev->tasklet_lock, irqflags); + + drm_tasklet.data = (unsigned long)dev; + + tasklet_hi_schedule(&drm_tasklet); +} +EXPORT_SYMBOL(drm_locked_tasklet); diff --git a/drivers/char/drm/drm_lock.c b/drivers/char/drm/drm_lock.c index f9e4530..e0abe5c 100644 --- a/drivers/char/drm/drm_lock.c +++ b/drivers/char/drm/drm_lock.c @@ -155,6 +155,7 @@ int drm_unlock(struct inode *inode, struct file *filp, drm_file_t *priv = filp->private_data; drm_device_t *dev = priv->head->dev; drm_lock_t lock; + unsigned int irqflags; if (copy_from_user(&lock, (drm_lock_t __user *) arg, sizeof(lock))) return -EFAULT; @@ -165,6 +166,16 @@ int drm_unlock(struct inode *inode, struct file *filp, return -EINVAL; } + spin_lock_irqsave(&dev->tasklet_lock, irqflags); + + if (dev->locked_tasklet_func) { + dev->locked_tasklet_func(dev); + + dev->locked_tasklet_func = NULL; + } + + spin_unlock_irqrestore(&dev->tasklet_lock, irqflags); + atomic_inc(&dev->counts[_DRM_STAT_UNLOCKS]); /* kernel_context_switch isn't used by any of the x86 drm -- cgit v0.10.2 From b03ed6f2fc519930fe3950365be59f0c079ce5d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=3D=3Futf-8=3Fq=3FMichel=5FD=3DC3=3DA4nzer=3F=3D?= Date: Tue, 24 Oct 2006 23:18:49 +1000 Subject: drm: drawable locking + memory management fixes + copyright Signed-off-by: Dave Airlie diff --git a/drivers/char/drm/drm_drawable.c b/drivers/char/drm/drm_drawable.c index e5f97de..5580c57 100644 --- a/drivers/char/drm/drm_drawable.c +++ b/drivers/char/drm/drm_drawable.c @@ -12,6 +12,7 @@ * * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * Copyright 2006 Tungsten Graphics, Inc., Bismarck, North Dakota. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -36,70 +37,86 @@ #include "drmP.h" -/** No-op. */ +/** + * Allocate drawable ID and memory to store information about it. + */ int drm_adddraw(DRM_IOCTL_ARGS) { DRM_DEVICE; unsigned long irqflags; - int i, j = 0; + int i, j; + u32 *bitfield = dev->drw_bitfield; + unsigned int bitfield_length = dev->drw_bitfield_length; + drm_drawable_info_t **info = dev->drw_info; + unsigned int info_length = dev->drw_info_length; drm_draw_t draw; - spin_lock_irqsave(&dev->drw_lock, irqflags); - - for (i = 0; i < dev->drw_bitfield_length; i++) { - u32 bitfield = dev->drw_bitfield[i]; - - if (bitfield == ~0) + for (i = 0, j = 0; i < bitfield_length; i++) { + if (bitfield[i] == ~0) continue; - for (; j < sizeof(bitfield); j++) - if (!(bitfield & (1 << j))) + for (; j < 8 * sizeof(*bitfield); j++) + if (!(bitfield[i] & (1 << j))) goto done; } done: - if (i == dev->drw_bitfield_length) { - u32 *new_bitfield = drm_realloc(dev->drw_bitfield, i * 4, - (i + 1) * 4, DRM_MEM_BUFS); + if (i == bitfield_length) { + bitfield_length++; + + bitfield = drm_alloc(bitfield_length * sizeof(*bitfield), + DRM_MEM_BUFS); - if (!new_bitfield) { + if (!bitfield) { DRM_ERROR("Failed to allocate new drawable bitfield\n"); - spin_unlock_irqrestore(&dev->drw_lock, irqflags); return DRM_ERR(ENOMEM); } - if (32 * (i + 1) > dev->drw_info_length) { - void *new_info = drm_realloc(dev->drw_info, - dev->drw_info_length * - sizeof(drm_drawable_info_t*), - 32 * (i + 1) * - sizeof(drm_drawable_info_t*), - DRM_MEM_BUFS); + if (8 * sizeof(*bitfield) * bitfield_length > info_length) { + info_length += 8 * sizeof(*bitfield); - if (!new_info) { + info = drm_alloc(info_length * sizeof(*info), + DRM_MEM_BUFS); + + if (!info) { DRM_ERROR("Failed to allocate new drawable info" " array\n"); - drm_free(new_bitfield, (i + 1) * 4, DRM_MEM_BUFS); - spin_unlock_irqrestore(&dev->drw_lock, irqflags); + drm_free(bitfield, + bitfield_length * sizeof(*bitfield), + DRM_MEM_BUFS); return DRM_ERR(ENOMEM); } - - dev->drw_info = (drm_drawable_info_t**)new_info; } - new_bitfield[i] = 0; - - dev->drw_bitfield = new_bitfield; - dev->drw_bitfield_length++; + bitfield[i] = 0; } - dev->drw_bitfield[i] |= 1 << j; - - draw.handle = i * sizeof(u32) + j; + draw.handle = i * 8 * sizeof(*bitfield) + j; DRM_DEBUG("%d\n", draw.handle); - dev->drw_info[draw.handle] = NULL; + spin_lock_irqsave(&dev->drw_lock, irqflags); + + bitfield[i] |= 1 << j; + info[draw.handle] = NULL; + + if (bitfield != dev->drw_bitfield) { + memcpy(bitfield, dev->drw_bitfield, dev->drw_bitfield_length * + sizeof(*bitfield)); + drm_free(dev->drw_bitfield, sizeof(*bitfield) * + dev->drw_bitfield_length, DRM_MEM_BUFS); + dev->drw_bitfield = bitfield; + dev->drw_bitfield_length = bitfield_length; + } + + if (info != dev->drw_info) { + memcpy(info, dev->drw_info, dev->drw_info_length * + sizeof(*info)); + drm_free(dev->drw_info, sizeof(*info) * dev->drw_info_length, + DRM_MEM_BUFS); + dev->drw_info = info; + dev->drw_info_length = info_length; + } spin_unlock_irqrestore(&dev->drw_lock, irqflags); @@ -108,63 +125,85 @@ done: return 0; } -/** No-op. */ +/** + * Free drawable ID and memory to store information about it. + */ int drm_rmdraw(DRM_IOCTL_ARGS) { DRM_DEVICE; drm_draw_t draw; - unsigned int idx, mod; + unsigned int idx, shift; unsigned long irqflags; + u32 *bitfield = dev->drw_bitfield; + unsigned int bitfield_length = dev->drw_bitfield_length; + drm_drawable_info_t **info = dev->drw_info; + unsigned int info_length = dev->drw_info_length; DRM_COPY_FROM_USER_IOCTL(draw, (drm_draw_t __user *) data, sizeof(draw)); - idx = draw.handle / 32; - mod = draw.handle % 32; + idx = draw.handle / (8 * sizeof(*bitfield)); + shift = draw.handle % (8 * sizeof(*bitfield)); - spin_lock_irqsave(&dev->drw_lock, irqflags); - - if (idx >= dev->drw_bitfield_length || - !(dev->drw_bitfield[idx] & (1 << mod))) { + if (idx >= bitfield_length || + !(bitfield[idx] & (1 << shift))) { DRM_DEBUG("No such drawable %d\n", draw.handle); - spin_unlock_irqrestore(&dev->drw_lock, irqflags); return 0; } - dev->drw_bitfield[idx] &= ~(1 << mod); + spin_lock_irqsave(&dev->drw_lock, irqflags); + + bitfield[idx] &= ~(1 << shift); + + spin_unlock_irqrestore(&dev->drw_lock, irqflags); - if (idx == (dev->drw_bitfield_length - 1)) { - while (idx >= 0 && !dev->drw_bitfield[idx]) + /* Can we shrink the arrays? */ + if (idx == bitfield_length - 1) { + while (idx >= 0 && !bitfield[idx]) --idx; - if (idx != draw.handle / 32) { - u32 *new_bitfield = drm_realloc(dev->drw_bitfield, - dev->drw_bitfield_length * 4, - (idx + 1) * 4, - DRM_MEM_BUFS); + bitfield_length = idx + 1; - if (new_bitfield || idx == -1) { - dev->drw_bitfield = new_bitfield; - dev->drw_bitfield_length = idx + 1; - } + if (idx != draw.handle / (8 * sizeof(*bitfield))) + bitfield = drm_alloc(bitfield_length * + sizeof(*bitfield), DRM_MEM_BUFS); + + if (!bitfield && bitfield_length) { + bitfield = dev->drw_bitfield; + bitfield_length = dev->drw_bitfield_length; } } - if (32 * dev->drw_bitfield_length < dev->drw_info_length) { - void *new_info = drm_realloc(dev->drw_info, - dev->drw_info_length * - sizeof(drm_drawable_info_t*), - 32 * dev->drw_bitfield_length * - sizeof(drm_drawable_info_t*), - DRM_MEM_BUFS); - - if (new_info || !dev->drw_bitfield_length) { - dev->drw_info = (drm_drawable_info_t**)new_info; - dev->drw_info_length = 32 * dev->drw_bitfield_length; + if (bitfield != dev->drw_bitfield) { + info_length = 8 * sizeof(*bitfield) * bitfield_length; + + info = drm_alloc(info_length * sizeof(*info), DRM_MEM_BUFS); + + if (!info && info_length) { + info = dev->drw_info; + info_length = dev->drw_info_length; } - } - spin_unlock_irqrestore(&dev->drw_lock, irqflags); + spin_lock_irqsave(&dev->drw_lock, irqflags); + + memcpy(bitfield, dev->drw_bitfield, bitfield_length * + sizeof(*bitfield)); + drm_free(dev->drw_bitfield, sizeof(*bitfield) * + dev->drw_bitfield_length, DRM_MEM_BUFS); + dev->drw_bitfield = bitfield; + dev->drw_bitfield_length = bitfield_length; + + if (info != dev->drw_info) { + memcpy(info, dev->drw_info, info_length * + sizeof(*info)); + drm_free(dev->drw_info, sizeof(*info) * + dev->drw_info_length, DRM_MEM_BUFS); + dev->drw_info = info; + dev->drw_info_length = info_length; + } + + spin_unlock_irqrestore(&dev->drw_lock, irqflags); + } DRM_DEBUG("%d\n", draw.handle); return 0; @@ -173,24 +212,22 @@ int drm_rmdraw(DRM_IOCTL_ARGS) int drm_update_drawable_info(DRM_IOCTL_ARGS) { DRM_DEVICE; drm_update_draw_t update; - unsigned int id, idx, mod; - unsigned long irqflags; + unsigned int id, idx, shift; + u32 *bitfield = dev->drw_bitfield; + unsigned long irqflags, bitfield_length = dev->drw_bitfield_length; drm_drawable_info_t *info; - void *new_data; + drm_clip_rect_t *rects; + int err; DRM_COPY_FROM_USER_IOCTL(update, (drm_update_draw_t __user *) data, sizeof(update)); id = update.handle; - idx = id / 32; - mod = id % 32; + idx = id / (8 * sizeof(*bitfield)); + shift = id % (8 * sizeof(*bitfield)); - spin_lock_irqsave(&dev->drw_lock, irqflags); - - if (idx >= dev->drw_bitfield_length || - !(dev->drw_bitfield[idx] & (1 << mod))) { + if (idx >= bitfield_length || !(bitfield[idx] & (1 << shift))) { DRM_ERROR("No such drawable %d\n", update.handle); - spin_unlock_irqrestore(&dev->drw_lock, irqflags); return DRM_ERR(EINVAL); } @@ -201,66 +238,77 @@ int drm_update_drawable_info(DRM_IOCTL_ARGS) { if (!info) { DRM_ERROR("Failed to allocate drawable info memory\n"); - spin_unlock_irqrestore(&dev->drw_lock, irqflags); return DRM_ERR(ENOMEM); } - - dev->drw_info[id] = info; } switch (update.type) { case DRM_DRAWABLE_CLIPRECTS: if (update.num != info->num_rects) { - new_data = drm_alloc(update.num * - sizeof(drm_clip_rect_t), - DRM_MEM_BUFS); - - if (!new_data) { - DRM_ERROR("Can't allocate cliprect memory\n"); - spin_unlock_irqrestore(&dev->drw_lock, irqflags); - return DRM_ERR(ENOMEM); - } - - info->rects = new_data; + rects = drm_alloc(update.num * sizeof(drm_clip_rect_t), + DRM_MEM_BUFS); + } else + rects = info->rects; + + if (update.num && !rects) { + DRM_ERROR("Failed to allocate cliprect memory\n"); + err = DRM_ERR(ENOMEM); + goto error; } - if (DRM_COPY_FROM_USER(info->rects, - (drm_clip_rect_t __user *) - (unsigned long)update.data, - update.num * sizeof(drm_clip_rect_t))) { - DRM_ERROR("Can't copy cliprects from userspace\n"); - spin_unlock_irqrestore(&dev->drw_lock, irqflags); - return DRM_ERR(EFAULT); + if (update.num && DRM_COPY_FROM_USER(rects, + (drm_clip_rect_t __user *) + (unsigned long)update.data, + update.num * + sizeof(*rects))) { + DRM_ERROR("Failed to copy cliprects from userspace\n"); + err = DRM_ERR(EFAULT); + goto error; } - if (update.num != info->num_rects) { + spin_lock_irqsave(&dev->drw_lock, irqflags); + + if (rects != info->rects) { drm_free(info->rects, info->num_rects * sizeof(drm_clip_rect_t), DRM_MEM_BUFS); - info->num_rects = update.num; } + info->rects = rects; + info->num_rects = update.num; + dev->drw_info[id] = info; + + spin_unlock_irqrestore(&dev->drw_lock, irqflags); + DRM_DEBUG("Updated %d cliprects for drawable %d\n", info->num_rects, id); break; default: DRM_ERROR("Invalid update type %d\n", update.type); - spin_unlock_irqrestore(&dev->drw_lock, irqflags); return DRM_ERR(EINVAL); } - spin_unlock_irqrestore(&dev->drw_lock, irqflags); - return 0; + +error: + if (!dev->drw_info[id]) + drm_free(info, sizeof(*info), DRM_MEM_BUFS); + else if (rects != dev->drw_info[id]->rects) + drm_free(rects, update.num * + sizeof(drm_clip_rect_t), DRM_MEM_BUFS); + + return err; } /** * Caller must hold the drawable spinlock! */ drm_drawable_info_t *drm_get_drawable_info(drm_device_t *dev, drm_drawable_t id) { - unsigned int idx = id / 32, mod = id % 32; + u32 *bitfield = dev->drw_bitfield; + unsigned int idx = id / (8 * sizeof(*bitfield)); + unsigned int shift = id % (8 * sizeof(*bitfield)); if (idx >= dev->drw_bitfield_length || - !(dev->drw_bitfield[idx] & (1 << mod))) { + !(bitfield[idx] & (1 << shift))) { DRM_DEBUG("No such drawable %d\n", id); return NULL; } -- cgit v0.10.2 From cdec2f82b11afbe4933fa9a9b3ed567db14fd237 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=3D=3Futf-8=3Fq=3FMichel=5FD=3DC3=3DA4nzer=3F=3D?= Date: Tue, 24 Oct 2006 23:20:15 +1000 Subject: drm: Change first valid DRM drawable ID to be 1 instead of 0. This makes it easier for userspace to know when it needs to allocate an ID. Also free drawable information memory when it's no longer needed. Signed-off-by: Dave Airlie diff --git a/drivers/char/drm/drm_drawable.c b/drivers/char/drm/drm_drawable.c index 5580c57..ce7a027 100644 --- a/drivers/char/drm/drm_drawable.c +++ b/drivers/char/drm/drm_drawable.c @@ -92,13 +92,13 @@ done: bitfield[i] = 0; } - draw.handle = i * 8 * sizeof(*bitfield) + j; + draw.handle = i * 8 * sizeof(*bitfield) + j + 1; DRM_DEBUG("%d\n", draw.handle); spin_lock_irqsave(&dev->drw_lock, irqflags); bitfield[i] |= 1 << j; - info[draw.handle] = NULL; + info[draw.handle - 1] = NULL; if (bitfield != dev->drw_bitfield) { memcpy(bitfield, dev->drw_bitfield, dev->drw_bitfield_length * @@ -132,7 +132,7 @@ int drm_rmdraw(DRM_IOCTL_ARGS) { DRM_DEVICE; drm_draw_t draw; - unsigned int idx, shift; + unsigned int id, idx, shift; unsigned long irqflags; u32 *bitfield = dev->drw_bitfield; unsigned int bitfield_length = dev->drw_bitfield_length; @@ -142,10 +142,11 @@ int drm_rmdraw(DRM_IOCTL_ARGS) DRM_COPY_FROM_USER_IOCTL(draw, (drm_draw_t __user *) data, sizeof(draw)); - idx = draw.handle / (8 * sizeof(*bitfield)); - shift = draw.handle % (8 * sizeof(*bitfield)); + id = draw.handle - 1; + idx = id / (8 * sizeof(*bitfield)); + shift = id % (8 * sizeof(*bitfield)); - if (idx >= bitfield_length || + if (idx < 0 || idx >= bitfield_length || !(bitfield[idx] & (1 << shift))) { DRM_DEBUG("No such drawable %d\n", draw.handle); return 0; @@ -157,6 +158,12 @@ int drm_rmdraw(DRM_IOCTL_ARGS) spin_unlock_irqrestore(&dev->drw_lock, irqflags); + if (info[id]) { + drm_free(info[id]->rects, info[id]->num_rects * + sizeof(drm_clip_rect_t), DRM_MEM_BUFS); + drm_free(info[id], sizeof(**info), DRM_MEM_BUFS); + } + /* Can we shrink the arrays? */ if (idx == bitfield_length - 1) { while (idx >= 0 && !bitfield[idx]) @@ -164,7 +171,7 @@ int drm_rmdraw(DRM_IOCTL_ARGS) bitfield_length = idx + 1; - if (idx != draw.handle / (8 * sizeof(*bitfield))) + if (idx != id / (8 * sizeof(*bitfield))) bitfield = drm_alloc(bitfield_length * sizeof(*bitfield), DRM_MEM_BUFS); @@ -222,11 +229,12 @@ int drm_update_drawable_info(DRM_IOCTL_ARGS) { DRM_COPY_FROM_USER_IOCTL(update, (drm_update_draw_t __user *) data, sizeof(update)); - id = update.handle; + id = update.handle - 1; idx = id / (8 * sizeof(*bitfield)); shift = id % (8 * sizeof(*bitfield)); - if (idx >= bitfield_length || !(bitfield[idx] & (1 << shift))) { + if (idx < 0 || idx >= bitfield_length || + !(bitfield[idx] & (1 << shift))) { DRM_ERROR("No such drawable %d\n", update.handle); return DRM_ERR(EINVAL); } @@ -304,10 +312,13 @@ error: */ drm_drawable_info_t *drm_get_drawable_info(drm_device_t *dev, drm_drawable_t id) { u32 *bitfield = dev->drw_bitfield; - unsigned int idx = id / (8 * sizeof(*bitfield)); - unsigned int shift = id % (8 * sizeof(*bitfield)); + unsigned int idx, shift; + + id--; + idx = id / (8 * sizeof(*bitfield)); + shift = id % (8 * sizeof(*bitfield)); - if (idx >= dev->drw_bitfield_length || + if (idx < 0 || idx >= dev->drw_bitfield_length || !(bitfield[idx] & (1 << shift))) { DRM_DEBUG("No such drawable %d\n", id); return NULL; diff --git a/drivers/char/drm/drm_drv.c b/drivers/char/drm/drm_drv.c index 59de4a0..a70af0d 100644 --- a/drivers/char/drm/drm_drv.c +++ b/drivers/char/drm/drm_drv.c @@ -153,6 +153,18 @@ int drm_lastclose(drm_device_t * dev) if (dev->irq_enabled) drm_irq_uninstall(dev); + /* Free drawable information memory */ + for (i = 0; i < dev->drw_bitfield_length / sizeof(*dev->drw_bitfield); + i++) { + drm_drawable_info_t *info = drm_get_drawable_info(dev, i); + + if (info) { + drm_free(info->rects, info->num_rects * + sizeof(drm_clip_rect_t), DRM_MEM_BUFS); + drm_free(info, sizeof(*info), DRM_MEM_BUFS); + } + } + mutex_lock(&dev->struct_mutex); del_timer(&dev->timer); -- cgit v0.10.2 From 507c0185a72e89002757a58f6c64de3df84da0de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=3D=3Futf-8=3Fq=3FFelix=5FK=3DC3=3DBChling=3F=3D?= Date: Tue, 24 Oct 2006 23:28:23 +1000 Subject: drm: drm_rmdraw: Declare id and idx as signed so testing for < 0 works as intended. Signed-off-by: Dave Airlie diff --git a/drivers/char/drm/drm_drawable.c b/drivers/char/drm/drm_drawable.c index ce7a027..de37d5f 100644 --- a/drivers/char/drm/drm_drawable.c +++ b/drivers/char/drm/drm_drawable.c @@ -132,7 +132,8 @@ int drm_rmdraw(DRM_IOCTL_ARGS) { DRM_DEVICE; drm_draw_t draw; - unsigned int id, idx, shift; + int id, idx; + unsigned int shift; unsigned long irqflags; u32 *bitfield = dev->drw_bitfield; unsigned int bitfield_length = dev->drw_bitfield_length; -- cgit v0.10.2 From 8163e418f71e46a28bac6625b4c633c13bd53c8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=3D=3Futf-8=3Fq=3FMichel=5FD=3DC3=3DA4nzer=3F=3D?= Date: Tue, 24 Oct 2006 23:30:01 +1000 Subject: drm: Make locked tasklet handling more robust. Initialize the spinlock unconditionally when struct drm_device is filled in, and return early in drm_locked_tasklet() if the driver doesn't support IRQs. Signed-off-by: Dave Airlie diff --git a/drivers/char/drm/drm_irq.c b/drivers/char/drm/drm_irq.c index 2b10e5b..b08608a 100644 --- a/drivers/char/drm/drm_irq.c +++ b/drivers/char/drm/drm_irq.c @@ -119,7 +119,6 @@ static int drm_irq_install(drm_device_t * dev) init_waitqueue_head(&dev->vbl_queue); spin_lock_init(&dev->vbl_lock); - spin_lock_init(&dev->tasklet_lock); INIT_LIST_HEAD(&dev->vbl_sigs.head); INIT_LIST_HEAD(&dev->vbl_sigs2.head); @@ -456,7 +455,8 @@ void drm_locked_tasklet(drm_device_t *dev, void (*func)(drm_device_t*)) unsigned long irqflags; static DECLARE_TASKLET(drm_tasklet, drm_locked_tasklet_func, 0); - if (test_bit(TASKLET_STATE_SCHED, &drm_tasklet.state)) + if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ) || + test_bit(TASKLET_STATE_SCHED, &drm_tasklet.state)) return; spin_lock_irqsave(&dev->tasklet_lock, irqflags); diff --git a/drivers/char/drm/drm_stub.c b/drivers/char/drm/drm_stub.c index 6f748e1..5fd6dc0 100644 --- a/drivers/char/drm/drm_stub.c +++ b/drivers/char/drm/drm_stub.c @@ -61,6 +61,7 @@ static int drm_fill_in_dev(drm_device_t * dev, struct pci_dev *pdev, spin_lock_init(&dev->count_lock); spin_lock_init(&dev->drw_lock); + spin_lock_init(&dev->tasklet_lock); init_timer(&dev->timer); mutex_init(&dev->struct_mutex); mutex_init(&dev->ctxlist_mutex); -- cgit v0.10.2 From ab285d74e6742422fd0465577a31fb03fe9ed241 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=3D=3Futf-8=3Fq=3FMichel=5FD=3DC3=3DA4nzer=3F=3D?= Date: Tue, 24 Oct 2006 23:34:18 +1000 Subject: drm: Core vsync: Add flag DRM_VBLANK_NEXTONMISS. When this flag is set and the target sequence is missed, wait for the next vertical blank instead of returning immediately. Signed-off-by: Dave Airlie diff --git a/drivers/char/drm/drm.h b/drivers/char/drm/drm.h index 07a69902..3f28a15 100644 --- a/drivers/char/drm/drm.h +++ b/drivers/char/drm/drm.h @@ -487,12 +487,14 @@ typedef struct drm_irq_busid { typedef enum { _DRM_VBLANK_ABSOLUTE = 0x0, /**< Wait for specific vblank sequence number */ _DRM_VBLANK_RELATIVE = 0x1, /**< Wait for given number of vblanks */ + _DRM_VBLANK_NEXTONMISS = 0x10000000, /**< If missed, wait for next vblank */ _DRM_VBLANK_SECONDARY = 0x20000000, /**< Secondary display controller */ _DRM_VBLANK_SIGNAL = 0x40000000 /**< Send signal instead of blocking */ } drm_vblank_seq_type_t; #define _DRM_VBLANK_TYPES_MASK (_DRM_VBLANK_ABSOLUTE | _DRM_VBLANK_RELATIVE) -#define _DRM_VBLANK_FLAGS_MASK (_DRM_VBLANK_SIGNAL | _DRM_VBLANK_SECONDARY) +#define _DRM_VBLANK_FLAGS_MASK (_DRM_VBLANK_SIGNAL | _DRM_VBLANK_SECONDARY | \ + _DRM_VBLANK_NEXTONMISS) struct drm_wait_vblank_request { drm_vblank_seq_type_t type; diff --git a/drivers/char/drm/drm_irq.c b/drivers/char/drm/drm_irq.c index b08608a..78aae5b 100644 --- a/drivers/char/drm/drm_irq.c +++ b/drivers/char/drm/drm_irq.c @@ -250,8 +250,7 @@ int drm_wait_vblank(DRM_IOCTL_ARGS) drm_wait_vblank_t vblwait; struct timeval now; int ret = 0; - unsigned int flags; - atomic_t *seq; + unsigned int flags, seq; if (!dev->irq) return -EINVAL; @@ -273,12 +272,12 @@ int drm_wait_vblank(DRM_IOCTL_ARGS) DRIVER_IRQ_VBL2 : DRIVER_IRQ_VBL)) return -EINVAL; - seq = (flags & _DRM_VBLANK_SECONDARY) ? &dev->vbl_received2 : - &dev->vbl_received; + seq = atomic_read((flags & _DRM_VBLANK_SECONDARY) ? &dev->vbl_received2 + : &dev->vbl_received); switch (vblwait.request.type & _DRM_VBLANK_TYPES_MASK) { case _DRM_VBLANK_RELATIVE: - vblwait.request.sequence += atomic_read(seq); + vblwait.request.sequence += seq; vblwait.request.type &= ~_DRM_VBLANK_RELATIVE; case _DRM_VBLANK_ABSOLUTE: break; @@ -286,13 +285,18 @@ int drm_wait_vblank(DRM_IOCTL_ARGS) return -EINVAL; } + if ((flags & _DRM_VBLANK_NEXTONMISS) && + (seq - vblwait.request.sequence) <= (1<<23)) { + vblwait.request.sequence = seq + 1; + } + if (flags & _DRM_VBLANK_SIGNAL) { unsigned long irqflags; drm_vbl_sig_t *vbl_sigs = (flags & _DRM_VBLANK_SECONDARY) ? &dev->vbl_sigs2 : &dev->vbl_sigs; drm_vbl_sig_t *vbl_sig; - vblwait.reply.sequence = atomic_read(seq); + vblwait.reply.sequence = seq; spin_lock_irqsave(&dev->vbl_lock, irqflags); -- cgit v0.10.2 From 049b323321bbcb476b799f50dc6444c0ed5a0e0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=3D=3Futf-8=3Fq=3FMichel=5FD=3DC3=3DA4nzer=3F=3D?= Date: Tue, 24 Oct 2006 23:34:58 +1000 Subject: drm: Core vsync: Don't clobber target sequence number when scheduling signal. It looks like this would have caused signals to always get sent on the next vertical blank, regardless of the sequence number. Signed-off-by: Dave Airlie diff --git a/drivers/char/drm/drm_irq.c b/drivers/char/drm/drm_irq.c index 78aae5b..9d00c51 100644 --- a/drivers/char/drm/drm_irq.c +++ b/drivers/char/drm/drm_irq.c @@ -296,8 +296,6 @@ int drm_wait_vblank(DRM_IOCTL_ARGS) ? &dev->vbl_sigs2 : &dev->vbl_sigs; drm_vbl_sig_t *vbl_sig; - vblwait.reply.sequence = seq; - spin_lock_irqsave(&dev->vbl_lock, irqflags); /* Check if this task has already scheduled the same signal @@ -310,6 +308,7 @@ int drm_wait_vblank(DRM_IOCTL_ARGS) && vbl_sig->task == current) { spin_unlock_irqrestore(&dev->vbl_lock, irqflags); + vblwait.reply.sequence = seq; goto done; } } @@ -340,6 +339,8 @@ int drm_wait_vblank(DRM_IOCTL_ARGS) list_add_tail((struct list_head *)vbl_sig, &vbl_sigs->head); spin_unlock_irqrestore(&dev->vbl_lock, irqflags); + + vblwait.reply.sequence = seq; } else { if (flags & _DRM_VBLANK_SECONDARY) { if (dev->driver->vblank_wait2) -- cgit v0.10.2 From a6b54f3f5050c0cbc0c35dd48064846c6302706b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=3D=3Futf-8=3Fq=3FMichel=5FD=3DC3=3DA4nzer=3F=3D?= Date: Tue, 24 Oct 2006 23:37:43 +1000 Subject: drm: i915: Add ioctl for scheduling buffer swaps at vertical blanks. This uses the core facility to schedule a driver callback that will be called ASAP after the given vertical blank interrupt with the HW lock held. Signed-off-by: Dave Airlie diff --git a/drivers/char/drm/i915_dma.c b/drivers/char/drm/i915_dma.c index fb7913f..9354ce3 100644 --- a/drivers/char/drm/i915_dma.c +++ b/drivers/char/drm/i915_dma.c @@ -162,6 +162,7 @@ static int i915_initialize(drm_device_t * dev, dev_priv->ring.virtual_start = dev_priv->ring.map.handle; + dev_priv->cpp = init->cpp; dev_priv->back_offset = init->back_offset; dev_priv->front_offset = init->front_offset; dev_priv->current_page = 0; @@ -782,6 +783,7 @@ drm_ioctl_desc_t i915_ioctls[] = { [DRM_IOCTL_NR(DRM_I915_DESTROY_HEAP)] = { i915_mem_destroy_heap, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY }, [DRM_IOCTL_NR(DRM_I915_SET_VBLANK_PIPE)] = { i915_vblank_pipe_set, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY }, [DRM_IOCTL_NR(DRM_I915_GET_VBLANK_PIPE)] = { i915_vblank_pipe_get, DRM_AUTH }, + [DRM_IOCTL_NR(DRM_I915_VBLANK_SWAP)] = {i915_vblank_swap, DRM_AUTH}, }; int i915_max_ioctl = DRM_ARRAY_SIZE(i915_ioctls); diff --git a/drivers/char/drm/i915_drm.h b/drivers/char/drm/i915_drm.h index 6af83e6..8926beb 100644 --- a/drivers/char/drm/i915_drm.h +++ b/drivers/char/drm/i915_drm.h @@ -132,6 +132,7 @@ typedef struct _drm_i915_sarea { #define DRM_I915_DESTROY_HEAP 0x0c #define DRM_I915_SET_VBLANK_PIPE 0x0d #define DRM_I915_GET_VBLANK_PIPE 0x0e +#define DRM_I915_VBLANK_SWAP 0x0f #define DRM_IOCTL_I915_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t) #define DRM_IOCTL_I915_FLUSH DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH) @@ -243,4 +244,12 @@ typedef struct drm_i915_vblank_pipe { int pipe; } drm_i915_vblank_pipe_t; +/* Schedule buffer swap at given vertical blank: + */ +typedef struct drm_i915_vblank_swap { + drm_drawable_t drawable; + unsigned int pipe; + unsigned int sequence; +} drm_i915_vblank_swap_t; + #endif /* _I915_DRM_H_ */ diff --git a/drivers/char/drm/i915_drv.h b/drivers/char/drm/i915_drv.h index 5f0c4fa..334b0ce 100644 --- a/drivers/char/drm/i915_drv.h +++ b/drivers/char/drm/i915_drv.h @@ -71,6 +71,13 @@ struct mem_block { DRMFILE filp; /* 0: free, -1: heap, other: real files */ }; +typedef struct _drm_i915_vbl_swap { + struct list_head head; + drm_drawable_t drw_id; + unsigned int pipe; + unsigned int sequence; +} drm_i915_vbl_swap_t; + typedef struct drm_i915_private { drm_local_map_t *sarea; drm_local_map_t *mmio_map; @@ -83,6 +90,7 @@ typedef struct drm_i915_private { dma_addr_t dma_status_page; unsigned long counter; + unsigned int cpp; int back_offset; int front_offset; int current_page; @@ -98,6 +106,10 @@ typedef struct drm_i915_private { struct mem_block *agp_heap; unsigned int sr01, adpa, ppcr, dvob, dvoc, lvds; int vblank_pipe; + + spinlock_t swaps_lock; + drm_i915_vbl_swap_t vbl_swaps; + unsigned int swaps_pending; } drm_i915_private_t; extern drm_ioctl_desc_t i915_ioctls[]; @@ -124,6 +136,7 @@ extern void i915_driver_irq_postinstall(drm_device_t * dev); extern void i915_driver_irq_uninstall(drm_device_t * dev); extern int i915_vblank_pipe_set(DRM_IOCTL_ARGS); extern int i915_vblank_pipe_get(DRM_IOCTL_ARGS); +extern int i915_vblank_swap(DRM_IOCTL_ARGS); /* i915_mem.c */ extern int i915_mem_alloc(DRM_IOCTL_ARGS); @@ -257,6 +270,10 @@ extern int i915_wait_ring(drm_device_t * dev, int n, const char *caller); #define GFX_OP_DRAWRECT_INFO_I965 ((0x7900<<16)|0x2) +#define XY_SRC_COPY_BLT_CMD ((2<<29)|(0x53<<22)|6) +#define XY_SRC_COPY_BLT_WRITE_ALPHA (1<<21) +#define XY_SRC_COPY_BLT_WRITE_RGB (1<<20) + #define MI_BATCH_BUFFER ((0x30<<23)|1) #define MI_BATCH_BUFFER_START (0x31<<23) #define MI_BATCH_BUFFER_END (0xA<<23) diff --git a/drivers/char/drm/i915_irq.c b/drivers/char/drm/i915_irq.c index 33d401876..a93f1f3 100644 --- a/drivers/char/drm/i915_irq.c +++ b/drivers/char/drm/i915_irq.c @@ -37,6 +37,99 @@ #define MAX_NOPID ((u32)~0) +/** + * Emit blits for scheduled buffer swaps. + * + * This function will be called with the HW lock held. + */ +static void i915_vblank_tasklet(drm_device_t *dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + unsigned int irqflags; + struct list_head *list, *tmp; + + DRM_DEBUG("\n"); + + spin_lock_irqsave(&dev_priv->swaps_lock, irqflags); + + list_for_each_safe(list, tmp, &dev_priv->vbl_swaps.head) { + drm_i915_vbl_swap_t *vbl_swap = + list_entry(list, drm_i915_vbl_swap_t, head); + atomic_t *counter = vbl_swap->pipe ? &dev->vbl_received2 : + &dev->vbl_received; + + if ((atomic_read(counter) - vbl_swap->sequence) <= (1<<23)) { + drm_drawable_info_t *drw; + + spin_unlock(&dev_priv->swaps_lock); + + spin_lock(&dev->drw_lock); + + drw = drm_get_drawable_info(dev, vbl_swap->drw_id); + + if (drw) { + int i, num_rects = drw->num_rects; + drm_clip_rect_t *rect = drw->rects; + drm_i915_sarea_t *sarea_priv = + dev_priv->sarea_priv; + u32 cpp = dev_priv->cpp; + u32 cmd = (cpp == 4) ? (XY_SRC_COPY_BLT_CMD | + XY_SRC_COPY_BLT_WRITE_ALPHA | + XY_SRC_COPY_BLT_WRITE_RGB) + : XY_SRC_COPY_BLT_CMD; + u32 pitchropcpp = (sarea_priv->pitch * cpp) | + (0xcc << 16) | (cpp << 23) | + (1 << 24); + RING_LOCALS; + + i915_kernel_lost_context(dev); + + BEGIN_LP_RING(6); + + OUT_RING(GFX_OP_DRAWRECT_INFO); + OUT_RING(0); + OUT_RING(0); + OUT_RING(sarea_priv->width | + sarea_priv->height << 16); + OUT_RING(sarea_priv->width | + sarea_priv->height << 16); + OUT_RING(0); + + ADVANCE_LP_RING(); + + sarea_priv->ctxOwner = DRM_KERNEL_CONTEXT; + + for (i = 0; i < num_rects; i++, rect++) { + BEGIN_LP_RING(8); + + OUT_RING(cmd); + OUT_RING(pitchropcpp); + OUT_RING((rect->y1 << 16) | rect->x1); + OUT_RING((rect->y2 << 16) | rect->x2); + OUT_RING(sarea_priv->front_offset); + OUT_RING((rect->y1 << 16) | rect->x1); + OUT_RING(pitchropcpp & 0xffff); + OUT_RING(sarea_priv->back_offset); + + ADVANCE_LP_RING(); + } + } + + spin_unlock(&dev->drw_lock); + + spin_lock(&dev_priv->swaps_lock); + + list_del(list); + + drm_free(vbl_swap, sizeof(*vbl_swap), DRM_MEM_DRIVER); + + dev_priv->swaps_pending--; + } + } + + spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags); +} + irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) { drm_device_t *dev = (drm_device_t *) arg; @@ -72,6 +165,8 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) DRM_WAKEUP(&dev->vbl_queue); drm_vbl_send_signals(dev); + + drm_locked_tasklet(dev, i915_vblank_tasklet); } return IRQ_HANDLED; @@ -271,6 +366,90 @@ int i915_vblank_pipe_get(DRM_IOCTL_ARGS) return 0; } +/** + * Schedule buffer swap at given vertical blank. + */ +int i915_vblank_swap(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + drm_i915_private_t *dev_priv = dev->dev_private; + drm_i915_vblank_swap_t swap; + drm_i915_vbl_swap_t *vbl_swap; + unsigned int irqflags; + struct list_head *list; + + if (!dev_priv) { + DRM_ERROR("%s called with no initialization\n", __func__); + return DRM_ERR(EINVAL); + } + + if (dev_priv->sarea_priv->rotation) { + DRM_DEBUG("Rotation not supported\n"); + return DRM_ERR(EINVAL); + } + + if (dev_priv->swaps_pending >= 100) { + DRM_DEBUG("Too many swaps queued\n"); + return DRM_ERR(EBUSY); + } + + DRM_COPY_FROM_USER_IOCTL(swap, (drm_i915_vblank_swap_t __user *) data, + sizeof(swap)); + + if (swap.pipe > 1 || !(dev_priv->vblank_pipe & (1 << swap.pipe))) { + DRM_ERROR("Invalid pipe %d\n", swap.pipe); + return DRM_ERR(EINVAL); + } + + spin_lock_irqsave(&dev->drw_lock, irqflags); + + if (!drm_get_drawable_info(dev, swap.drawable)) { + spin_unlock_irqrestore(&dev->drw_lock, irqflags); + DRM_ERROR("Invalid drawable ID %d\n", swap.drawable); + return DRM_ERR(EINVAL); + } + + spin_unlock_irqrestore(&dev->drw_lock, irqflags); + + spin_lock_irqsave(&dev_priv->swaps_lock, irqflags); + + list_for_each(list, &dev_priv->vbl_swaps.head) { + vbl_swap = list_entry(list, drm_i915_vbl_swap_t, head); + + if (vbl_swap->drw_id == swap.drawable && + vbl_swap->pipe == swap.pipe && + vbl_swap->sequence == swap.sequence) { + spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags); + DRM_DEBUG("Already scheduled\n"); + return 0; + } + } + + spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags); + + vbl_swap = drm_calloc(1, sizeof(vbl_swap), DRM_MEM_DRIVER); + + if (!vbl_swap) { + DRM_ERROR("Failed to allocate memory to queue swap\n"); + return DRM_ERR(ENOMEM); + } + + DRM_DEBUG("\n"); + + vbl_swap->drw_id = swap.drawable; + vbl_swap->pipe = swap.pipe; + vbl_swap->sequence = swap.sequence; + + spin_lock_irqsave(&dev_priv->swaps_lock, irqflags); + + list_add_tail((struct list_head *)vbl_swap, &dev_priv->vbl_swaps.head); + dev_priv->swaps_pending++; + + spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags); + + return 0; +} + /* drm_dma.h hooks */ void i915_driver_irq_preinstall(drm_device_t * dev) @@ -286,6 +465,10 @@ void i915_driver_irq_postinstall(drm_device_t * dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + dev_priv->swaps_lock = SPIN_LOCK_UNLOCKED; + INIT_LIST_HEAD(&dev_priv->vbl_swaps.head); + dev_priv->swaps_pending = 0; + i915_enable_interrupt(dev); DRM_INIT_WAITQUEUE(&dev_priv->irq_queue); } -- cgit v0.10.2 From 541f29aad766b6c7b911a7d900d952744369bf53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=3D=3Futf-8=3Fq=3FMichel=5FD=3DC3=3DA4nzer=3F=3D?= Date: Tue, 24 Oct 2006 23:38:54 +1000 Subject: drm: DRM_I915_VBLANK_SWAP ioctl: Take drm_vblank_seq_type_t instead of pipe number. Handle relative as well as absolute target sequence numbers. Return error if target sequence has already passed, so userspace can deal with this situation as it sees fit. On success, return the sequence number of the vertical blank when the buffer swap is expected to take place. Also add DRM_IOCTL_I915_VBLANK_SWAP definition for userspace code that may want to use ioctl() instead of drmCommandWriteRead(). Signed-off-by: Dave Airlie diff --git a/drivers/char/drm/i915_drm.h b/drivers/char/drm/i915_drm.h index 8926beb..4ce3d16 100644 --- a/drivers/char/drm/i915_drm.h +++ b/drivers/char/drm/i915_drm.h @@ -149,6 +149,7 @@ typedef struct _drm_i915_sarea { #define DRM_IOCTL_I915_DESTROY_HEAP DRM_IOW( DRM_COMMAND_BASE + DRM_I915_DESTROY_HEAP, drm_i915_mem_destroy_heap_t) #define DRM_IOCTL_I915_SET_VBLANK_PIPE DRM_IOW( DRM_COMMAND_BASE + DRM_I915_SET_VBLANK_PIPE, drm_i915_vblank_pipe_t) #define DRM_IOCTL_I915_GET_VBLANK_PIPE DRM_IOR( DRM_COMMAND_BASE + DRM_I915_GET_VBLANK_PIPE, drm_i915_vblank_pipe_t) +#define DRM_IOCTL_I915_VBLANK_SWAP DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_VBLANK_SWAP, drm_i915_vblank_swap_t) /* Allow drivers to submit batchbuffers directly to hardware, relying * on the security mechanisms provided by hardware. @@ -248,7 +249,7 @@ typedef struct drm_i915_vblank_pipe { */ typedef struct drm_i915_vblank_swap { drm_drawable_t drawable; - unsigned int pipe; + drm_vblank_seq_type_t seqtype; unsigned int sequence; } drm_i915_vblank_swap_t; diff --git a/drivers/char/drm/i915_irq.c b/drivers/char/drm/i915_irq.c index a93f1f3..d564556 100644 --- a/drivers/char/drm/i915_irq.c +++ b/drivers/char/drm/i915_irq.c @@ -375,7 +375,7 @@ int i915_vblank_swap(DRM_IOCTL_ARGS) drm_i915_private_t *dev_priv = dev->dev_private; drm_i915_vblank_swap_t swap; drm_i915_vbl_swap_t *vbl_swap; - unsigned int irqflags; + unsigned int pipe, seqtype, irqflags, curseq; struct list_head *list; if (!dev_priv) { @@ -396,8 +396,23 @@ int i915_vblank_swap(DRM_IOCTL_ARGS) DRM_COPY_FROM_USER_IOCTL(swap, (drm_i915_vblank_swap_t __user *) data, sizeof(swap)); - if (swap.pipe > 1 || !(dev_priv->vblank_pipe & (1 << swap.pipe))) { - DRM_ERROR("Invalid pipe %d\n", swap.pipe); + if (swap.seqtype & ~(_DRM_VBLANK_RELATIVE | _DRM_VBLANK_ABSOLUTE | + _DRM_VBLANK_SECONDARY)) { + DRM_ERROR("Invalid sequence type 0x%x\n", swap.seqtype); + return DRM_ERR(EINVAL); + } + + pipe = (swap.seqtype & _DRM_VBLANK_SECONDARY) ? 1 : 0; + + seqtype = swap.seqtype & (_DRM_VBLANK_RELATIVE | _DRM_VBLANK_ABSOLUTE); + + if (seqtype == _DRM_VBLANK_RELATIVE && swap.sequence == 0) { + DRM_DEBUG("Not scheduling swap for current sequence\n"); + return DRM_ERR(EINVAL); + } + + if (!(dev_priv->vblank_pipe & (1 << pipe))) { + DRM_ERROR("Invalid pipe %d\n", pipe); return DRM_ERR(EINVAL); } @@ -411,13 +426,28 @@ int i915_vblank_swap(DRM_IOCTL_ARGS) spin_unlock_irqrestore(&dev->drw_lock, irqflags); + curseq = atomic_read(pipe ? &dev->vbl_received2 : &dev->vbl_received); + spin_lock_irqsave(&dev_priv->swaps_lock, irqflags); + switch (seqtype) { + case _DRM_VBLANK_RELATIVE: + swap.sequence += curseq; + break; + case _DRM_VBLANK_ABSOLUTE: + if ((curseq - swap.sequence) > (1<<23)) { + spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags); + DRM_DEBUG("Missed target sequence\n"); + return DRM_ERR(EINVAL); + } + break; + } + list_for_each(list, &dev_priv->vbl_swaps.head) { vbl_swap = list_entry(list, drm_i915_vbl_swap_t, head); if (vbl_swap->drw_id == swap.drawable && - vbl_swap->pipe == swap.pipe && + vbl_swap->pipe == pipe && vbl_swap->sequence == swap.sequence) { spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags); DRM_DEBUG("Already scheduled\n"); @@ -437,7 +467,7 @@ int i915_vblank_swap(DRM_IOCTL_ARGS) DRM_DEBUG("\n"); vbl_swap->drw_id = swap.drawable; - vbl_swap->pipe = swap.pipe; + vbl_swap->pipe = pipe; vbl_swap->sequence = swap.sequence; spin_lock_irqsave(&dev_priv->swaps_lock, irqflags); @@ -447,6 +477,9 @@ int i915_vblank_swap(DRM_IOCTL_ARGS) spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags); + DRM_COPY_TO_USER_IOCTL((drm_i915_vblank_swap_t __user *) data, swap, + sizeof(swap)); + return 0; } -- cgit v0.10.2 From 5b51694aff705c465ef5941a99073036f3e444d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=3D=3Futf-8=3Fq=3FMichel=5FD=3DC3=3DA4nzer=3F=3D?= Date: Wed, 25 Oct 2006 00:08:23 +1000 Subject: drm: Make handling of dev_priv->vblank_pipe more robust. Initialize it to default value if it hasn't been set by the X server yet. In i915_vblank_pipe_set(), only update dev_priv->vblank_pipe and call i915_enable_interrupt() if the argument passed from userspace is valid to avoid corrupting dev_priv->vblank_pipe on invalid arguments. Signed-off-by: Dave Airlie diff --git a/drivers/char/drm/i915_irq.c b/drivers/char/drm/i915_irq.c index d564556..e9e46c4 100644 --- a/drivers/char/drm/i915_irq.c +++ b/drivers/char/drm/i915_irq.c @@ -304,7 +304,7 @@ int i915_irq_wait(DRM_IOCTL_ARGS) return i915_wait_irq(dev, irqwait.irq_seq); } -static int i915_enable_interrupt (drm_device_t *dev) +static void i915_enable_interrupt (drm_device_t *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; u16 flag; @@ -314,13 +314,8 @@ static int i915_enable_interrupt (drm_device_t *dev) flag |= VSYNC_PIPEA_FLAG; if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_B) flag |= VSYNC_PIPEB_FLAG; - if (dev_priv->vblank_pipe & ~(DRM_I915_VBLANK_PIPE_A|DRM_I915_VBLANK_PIPE_B)) { - DRM_ERROR("%s called with invalid pipe 0x%x\n", - __FUNCTION__, dev_priv->vblank_pipe); - return DRM_ERR(EINVAL); - } + I915_WRITE16(I915REG_INT_ENABLE_R, USER_INT_FLAG | flag); - return 0; } /* Set the vblank monitor pipe @@ -339,8 +334,17 @@ int i915_vblank_pipe_set(DRM_IOCTL_ARGS) DRM_COPY_FROM_USER_IOCTL(pipe, (drm_i915_vblank_pipe_t __user *) data, sizeof(pipe)); + if (pipe.pipe & ~(DRM_I915_VBLANK_PIPE_A|DRM_I915_VBLANK_PIPE_B)) { + DRM_ERROR("%s called with invalid pipe 0x%x\n", + __FUNCTION__, pipe.pipe); + return DRM_ERR(EINVAL); + } + dev_priv->vblank_pipe = pipe.pipe; - return i915_enable_interrupt (dev); + + i915_enable_interrupt (dev); + + return 0; } int i915_vblank_pipe_get(DRM_IOCTL_ARGS) @@ -502,6 +506,8 @@ void i915_driver_irq_postinstall(drm_device_t * dev) INIT_LIST_HEAD(&dev_priv->vbl_swaps.head); dev_priv->swaps_pending = 0; + if (!dev_priv->vblank_pipe) + dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A; i915_enable_interrupt(dev); DRM_INIT_WAITQUEUE(&dev_priv->irq_queue); } -- cgit v0.10.2 From 376642cf2eb0f32d8502b0a2c4efd96a3f13a8b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=3D=3Futf-8=3Fq=3FMichel=5FD=3DC3=3DA4nzer=3F=3D?= Date: Wed, 25 Oct 2006 00:09:35 +1000 Subject: drm: i915: Add SAREA fileds for determining which pipe to sync window buffer swaps to. Signed-off-by: Dave Airlie diff --git a/drivers/char/drm/i915_drm.h b/drivers/char/drm/i915_drm.h index 4ce3d16..96a46888 100644 --- a/drivers/char/drm/i915_drm.h +++ b/drivers/char/drm/i915_drm.h @@ -104,6 +104,15 @@ typedef struct _drm_i915_sarea { unsigned int depth_tiled; unsigned int rotated_tiled; unsigned int rotated2_tiled; + + int pipeA_x; + int pipeA_y; + int pipeA_w; + int pipeA_h; + int pipeB_x; + int pipeB_y; + int pipeB_w; + int pipeB_h; } drm_i915_sarea_t; /* Flags for perf_boxes -- cgit v0.10.2 From 2dbb232c4d6b6c89fc367f7566c7c87dd3b56cd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=3D=3Futf-8=3Fq=3FMichel=5FD=3DC3=3DA4nzer=3F=3D?= Date: Wed, 25 Oct 2006 00:10:24 +1000 Subject: drm: i915: Fix 'sequence has passed' condition in i915_vblank_swap(). Signed-off-by: Dave Airlie diff --git a/drivers/char/drm/i915_irq.c b/drivers/char/drm/i915_irq.c index e9e46c4..df28bc1 100644 --- a/drivers/char/drm/i915_irq.c +++ b/drivers/char/drm/i915_irq.c @@ -439,7 +439,7 @@ int i915_vblank_swap(DRM_IOCTL_ARGS) swap.sequence += curseq; break; case _DRM_VBLANK_ABSOLUTE: - if ((curseq - swap.sequence) > (1<<23)) { + if ((curseq - swap.sequence) <= (1<<23)) { spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags); DRM_DEBUG("Missed target sequence\n"); return DRM_ERR(EINVAL); -- cgit v0.10.2 From 21fa60ed4eab5b3b28d05930bb086615ecc191b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=3D=3Futf-8=3Fq=3FMichel=5FD=3DC3=3DA4nzer=3F=3D?= Date: Wed, 25 Oct 2006 00:10:59 +1000 Subject: drm: i915: Only return EBUSY after we've established we need to schedule a new swap. Signed-off-by: Dave Airlie diff --git a/drivers/char/drm/i915_irq.c b/drivers/char/drm/i915_irq.c index df28bc1..40c55ed 100644 --- a/drivers/char/drm/i915_irq.c +++ b/drivers/char/drm/i915_irq.c @@ -392,11 +392,6 @@ int i915_vblank_swap(DRM_IOCTL_ARGS) return DRM_ERR(EINVAL); } - if (dev_priv->swaps_pending >= 100) { - DRM_DEBUG("Too many swaps queued\n"); - return DRM_ERR(EBUSY); - } - DRM_COPY_FROM_USER_IOCTL(swap, (drm_i915_vblank_swap_t __user *) data, sizeof(swap)); @@ -461,6 +456,11 @@ int i915_vblank_swap(DRM_IOCTL_ARGS) spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags); + if (dev_priv->swaps_pending >= 100) { + DRM_DEBUG("Too many swaps queued\n"); + return DRM_ERR(EBUSY); + } + vbl_swap = drm_calloc(1, sizeof(vbl_swap), DRM_MEM_DRIVER); if (!vbl_swap) { -- cgit v0.10.2 From a0b136bb696cfa744a79c4dbbbbd0c8f9f30fe3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=3D=3Futf-8=3Fq=3FMichel=5FD=3DC3=3DA4nzer=3F=3D?= Date: Wed, 25 Oct 2006 00:12:52 +1000 Subject: drm: i915: fix up irqflags arg Signed-off-by: Dave Airlie diff --git a/drivers/char/drm/i915_irq.c b/drivers/char/drm/i915_irq.c index 40c55ed..1a5edec 100644 --- a/drivers/char/drm/i915_irq.c +++ b/drivers/char/drm/i915_irq.c @@ -45,7 +45,7 @@ static void i915_vblank_tasklet(drm_device_t *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - unsigned int irqflags; + unsigned long irqflags; struct list_head *list, *tmp; DRM_DEBUG("\n"); @@ -379,7 +379,8 @@ int i915_vblank_swap(DRM_IOCTL_ARGS) drm_i915_private_t *dev_priv = dev->dev_private; drm_i915_vblank_swap_t swap; drm_i915_vbl_swap_t *vbl_swap; - unsigned int pipe, seqtype, irqflags, curseq; + unsigned int pipe, seqtype, curseq; + unsigned long irqflags; struct list_head *list; if (!dev_priv) { -- cgit v0.10.2 From 2228ed67223f3f22ea09df8854e6a31ea06d5619 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=3D=3Futf-8=3Fq=3FMichel=5FD=3DC3=3DA4nzer=3F=3D?= Date: Wed, 25 Oct 2006 01:05:09 +1000 Subject: drm: i915 updates Add support for DRM_VBLANK_NEXTONMISS. Bump minor for swap scheduling ioctl and secondary vblank support. Avoid mis-counting vblank interrupts when they're only enabled for pipe A. Only schedule vblank tasklet if there are scheduled swaps pending. Signed-off-by: Dave Airlie diff --git a/drivers/char/drm/i915_drv.h b/drivers/char/drm/i915_drv.h index 334b0ce..93cdcfe 100644 --- a/drivers/char/drm/i915_drv.h +++ b/drivers/char/drm/i915_drv.h @@ -46,9 +46,11 @@ * 1.3: Add vblank support * 1.4: Fix cmdbuffer path, add heap destroy * 1.5: Add vblank pipe configuration + * 1.6: - New ioctl for scheduling buffer swaps on vertical blank + * - Support vertical blank on secondary display pipe */ #define DRIVER_MAJOR 1 -#define DRIVER_MINOR 5 +#define DRIVER_MINOR 6 #define DRIVER_PATCHLEVEL 0 typedef struct _drm_i915_ring_buffer { diff --git a/drivers/char/drm/i915_irq.c b/drivers/char/drm/i915_irq.c index 1a5edec..e5463b1 100644 --- a/drivers/char/drm/i915_irq.c +++ b/drivers/char/drm/i915_irq.c @@ -153,20 +153,26 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) DRM_WAKEUP(&dev_priv->irq_queue); if (temp & (VSYNC_PIPEA_FLAG | VSYNC_PIPEB_FLAG)) { - if ((dev_priv->vblank_pipe & + int vblank_pipe = dev_priv->vblank_pipe; + + if ((vblank_pipe & (DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B)) == (DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B)) { if (temp & VSYNC_PIPEA_FLAG) atomic_inc(&dev->vbl_received); if (temp & VSYNC_PIPEB_FLAG) atomic_inc(&dev->vbl_received2); - } else + } else if (((temp & VSYNC_PIPEA_FLAG) && + (vblank_pipe & DRM_I915_VBLANK_PIPE_A)) || + ((temp & VSYNC_PIPEB_FLAG) && + (vblank_pipe & DRM_I915_VBLANK_PIPE_B))) atomic_inc(&dev->vbl_received); DRM_WAKEUP(&dev->vbl_queue); drm_vbl_send_signals(dev); - drm_locked_tasklet(dev, i915_vblank_tasklet); + if (dev_priv->swaps_pending > 0) + drm_locked_tasklet(dev, i915_vblank_tasklet); } return IRQ_HANDLED; @@ -397,7 +403,7 @@ int i915_vblank_swap(DRM_IOCTL_ARGS) sizeof(swap)); if (swap.seqtype & ~(_DRM_VBLANK_RELATIVE | _DRM_VBLANK_ABSOLUTE | - _DRM_VBLANK_SECONDARY)) { + _DRM_VBLANK_SECONDARY | _DRM_VBLANK_NEXTONMISS)) { DRM_ERROR("Invalid sequence type 0x%x\n", swap.seqtype); return DRM_ERR(EINVAL); } @@ -406,11 +412,6 @@ int i915_vblank_swap(DRM_IOCTL_ARGS) seqtype = swap.seqtype & (_DRM_VBLANK_RELATIVE | _DRM_VBLANK_ABSOLUTE); - if (seqtype == _DRM_VBLANK_RELATIVE && swap.sequence == 0) { - DRM_DEBUG("Not scheduling swap for current sequence\n"); - return DRM_ERR(EINVAL); - } - if (!(dev_priv->vblank_pipe & (1 << pipe))) { DRM_ERROR("Invalid pipe %d\n", pipe); return DRM_ERR(EINVAL); @@ -428,21 +429,20 @@ int i915_vblank_swap(DRM_IOCTL_ARGS) curseq = atomic_read(pipe ? &dev->vbl_received2 : &dev->vbl_received); - spin_lock_irqsave(&dev_priv->swaps_lock, irqflags); - - switch (seqtype) { - case _DRM_VBLANK_RELATIVE: + if (seqtype == _DRM_VBLANK_RELATIVE) swap.sequence += curseq; - break; - case _DRM_VBLANK_ABSOLUTE: - if ((curseq - swap.sequence) <= (1<<23)) { - spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags); + + if ((curseq - swap.sequence) <= (1<<23)) { + if (swap.seqtype & _DRM_VBLANK_NEXTONMISS) { + swap.sequence = curseq + 1; + } else { DRM_DEBUG("Missed target sequence\n"); return DRM_ERR(EINVAL); } - break; } + spin_lock_irqsave(&dev_priv->swaps_lock, irqflags); + list_for_each(list, &dev_priv->vbl_swaps.head) { vbl_swap = list_entry(list, drm_i915_vbl_swap_t, head); -- cgit v0.10.2 From 5c2df2bfb121a77d925dba580f53da08b4020528 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Tue, 24 Oct 2006 11:36:59 -0700 Subject: drm: fix up irqflags in drm_lock.c Signed-off-by: Dave Airlie diff --git a/drivers/char/drm/drm_lock.c b/drivers/char/drm/drm_lock.c index e0abe5c..116ed0f 100644 --- a/drivers/char/drm/drm_lock.c +++ b/drivers/char/drm/drm_lock.c @@ -155,7 +155,7 @@ int drm_unlock(struct inode *inode, struct file *filp, drm_file_t *priv = filp->private_data; drm_device_t *dev = priv->head->dev; drm_lock_t lock; - unsigned int irqflags; + unsigned long irqflags; if (copy_from_user(&lock, (drm_lock_t __user *) arg, sizeof(lock))) return -EFAULT; -- cgit v0.10.2 From 3417f33e762bf7d4277031a655e3ad07e73ce0be Mon Sep 17 00:00:00 2001 From: George Sapountzis Date: Tue, 24 Oct 2006 12:03:04 -0700 Subject: drm: add flag for mapping PCI DMA buffers read-only. Add DRM_PCI_BUFFER_RO flag for mapping PCI DMA buffer read-only. An additional flag is needed, since PCI DMA buffers do not have an associated map. Signed-off-by: Dave Airlie diff --git a/drivers/char/drm/drm.h b/drivers/char/drm/drm.h index 3f28a15..8db9041 100644 --- a/drivers/char/drm/drm.h +++ b/drivers/char/drm/drm.h @@ -356,7 +356,8 @@ typedef struct drm_buf_desc { _DRM_PAGE_ALIGN = 0x01, /**< Align on page boundaries for DMA */ _DRM_AGP_BUFFER = 0x02, /**< Buffer is in AGP space */ _DRM_SG_BUFFER = 0x04, /**< Scatter/gather memory buffer */ - _DRM_FB_BUFFER = 0x08 /**< Buffer is in frame buffer */ + _DRM_FB_BUFFER = 0x08, /**< Buffer is in frame buffer */ + _DRM_PCI_BUFFER_RO = 0x10 /**< Map PCI DMA buffer read-only */ } flags; unsigned long agp_start; /**< * Start address of where the AGP buffers are diff --git a/drivers/char/drm/drmP.h b/drivers/char/drm/drmP.h index 2f18329..0bbb04f 100644 --- a/drivers/char/drm/drmP.h +++ b/drivers/char/drm/drmP.h @@ -431,7 +431,8 @@ typedef struct drm_device_dma { enum { _DRM_DMA_USE_AGP = 0x01, _DRM_DMA_USE_SG = 0x02, - _DRM_DMA_USE_FB = 0x04 + _DRM_DMA_USE_FB = 0x04, + _DRM_DMA_USE_PCI_RO = 0x08 } flags; } drm_device_dma_t; diff --git a/drivers/char/drm/drm_bufs.c b/drivers/char/drm/drm_bufs.c index 6eafff1..9f65f56 100644 --- a/drivers/char/drm/drm_bufs.c +++ b/drivers/char/drm/drm_bufs.c @@ -887,6 +887,9 @@ int drm_addbufs_pci(drm_device_t * dev, drm_buf_desc_t * request) request->count = entry->buf_count; request->size = size; + if (request->flags & _DRM_PCI_BUFFER_RO) + dma->flags = _DRM_DMA_USE_PCI_RO; + atomic_dec(&dev->buf_alloc); return 0; @@ -1471,9 +1474,10 @@ int drm_freebufs(struct inode *inode, struct file *filp, * \param arg pointer to a drm_buf_map structure. * \return zero on success or a negative number on failure. * - * Maps the AGP or SG buffer region with do_mmap(), and copies information - * about each buffer into user space. The PCI buffers are already mapped on the - * addbufs_pci() call. + * Maps the AGP, SG or PCI buffer region with do_mmap(), and copies information + * about each buffer into user space. For PCI buffers, it calls do_mmap() with + * offset equal to 0, which drm_mmap() interpretes as PCI buffers and calls + * drm_mmap_dma(). */ int drm_mapbufs(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) diff --git a/drivers/char/drm/drm_vm.c b/drivers/char/drm/drm_vm.c index b40ae43..74686e9 100644 --- a/drivers/char/drm/drm_vm.c +++ b/drivers/char/drm/drm_vm.c @@ -473,6 +473,22 @@ static int drm_mmap_dma(struct file *filp, struct vm_area_struct *vma) } unlock_kernel(); + if (!capable(CAP_SYS_ADMIN) && + (dma->flags & _DRM_DMA_USE_PCI_RO)) { + vma->vm_flags &= ~(VM_WRITE | VM_MAYWRITE); +#if defined(__i386__) || defined(__x86_64__) + pgprot_val(vma->vm_page_prot) &= ~_PAGE_RW; +#else + /* Ye gads this is ugly. With more thought + we could move this up higher and use + `protection_map' instead. */ + vma->vm_page_prot = + __pgprot(pte_val + (pte_wrprotect + (__pte(pgprot_val(vma->vm_page_prot))))); +#endif + } + vma->vm_ops = &drm_vm_dma_ops; vma->vm_flags |= VM_RESERVED; /* Don't swap */ -- cgit v0.10.2 From d942625c2d5f5d29cd3bb4fad8a4aadd59024317 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Thu, 7 Dec 2006 16:11:44 +1100 Subject: Fix http://bugzilla.kernel.org/show_bug.cgi?id=7606 WARNING: "drm_sman_set_manager" [drivers/char/drm/sis.ko] undefined! Cc: Cc: Signed-off-by: Andrew Morton Signed-off-by: Dave Airlie diff --git a/drivers/char/drm/drm_sman.c b/drivers/char/drm/drm_sman.c index 425c823..19c81d2 100644 --- a/drivers/char/drm/drm_sman.c +++ b/drivers/char/drm/drm_sman.c @@ -162,6 +162,7 @@ drm_sman_set_manager(drm_sman_t * sman, unsigned int manager, return 0; } +EXPORT_SYMBOL(drm_sman_set_manager); static drm_owner_item_t *drm_sman_get_owner_item(drm_sman_t * sman, unsigned long owner) -- cgit v0.10.2 From 3dfd35cd214f7874c4917dfedff81f107d845c15 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 24 Nov 2006 10:32:32 -0200 Subject: ACPI: ibm-acpi: do not use / in driver names ibm-acpi uses sub-device names like ibm/hotkey, which get in the way of a sysfs conversion. Fix it to use ibm_hotkey instead. Thanks to Zhang Rui for noticing this. Signed-off-by: Henrique de Moraes Holschuh diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index cd8722e..ce69f75 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -1854,7 +1854,7 @@ static int __init register_driver(struct ibm_struct *ibm) } memset(ibm->driver, 0, sizeof(struct acpi_driver)); - sprintf(ibm->driver->name, "%s/%s", IBM_NAME, ibm->name); + sprintf(ibm->driver->name, "%s_%s", IBM_NAME, ibm->name); ibm->driver->ids = ibm->hid; ibm->driver->ops.add = &ibm_device_add; -- cgit v0.10.2 From 8d29726434697a5ed08d4e0dfba9399a098922f4 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 24 Nov 2006 11:47:07 -0200 Subject: ACPI: ibm-acpi: trivial Lindent cleanups This patch just makes drives/acpi/ibm-acpi.c Lindent-clean, as requested by Len Brown. Signed-off-by: Henrique de Moraes Holschuh diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index ce69f75..b81a95d 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -584,8 +584,7 @@ static int wan_status(void) { int status; - if (!wan_supported || - !acpi_evalf(hkey_handle, &status, "GWAN", "d")) + if (!wan_supported || !acpi_evalf(hkey_handle, &status, "GWAN", "d")) status = 0; return status; @@ -910,6 +909,7 @@ static int _sta(acpi_handle handle) return status; } + #ifdef CONFIG_ACPI_IBM_DOCK #define dock_docked() (_sta(dock_handle) & 1) @@ -1386,12 +1386,12 @@ static int brightness_offset = 0x31; static int brightness_get(struct backlight_device *bd) { - u8 level; - if (!acpi_ec_read(brightness_offset, &level)) - return -EIO; + u8 level; + if (!acpi_ec_read(brightness_offset, &level)) + return -EIO; - level &= 0x7; - return level; + level &= 0x7; + return level; } static int brightness_read(char *p) @@ -1993,10 +1993,10 @@ IBM_PARAM(volume); IBM_PARAM(fan); static struct backlight_properties ibm_backlight_data = { - .owner = THIS_MODULE, - .get_brightness = brightness_get, - .update_status = brightness_update_status, - .max_brightness = 7, + .owner = THIS_MODULE, + .get_brightness = brightness_get, + .update_status = brightness_update_status, + .max_brightness = 7, }; static void acpi_ibm_exit(void) @@ -2075,7 +2075,7 @@ static int __init acpi_ibm_init(void) ibm_backlight_device = backlight_device_register("ibm", NULL, &ibm_backlight_data); - if (IS_ERR(ibm_backlight_device)) { + if (IS_ERR(ibm_backlight_device)) { printk(IBM_ERR "Could not register ibm backlight device\n"); ibm_backlight_device = NULL; acpi_ibm_exit(); -- cgit v0.10.2 From a26f878abcd0491906b5bbac8dd174f27019e907 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 24 Nov 2006 11:47:08 -0200 Subject: ACPI: ibm-acpi: Use a enum to select the thermal sensor reading strategy This patch consolidades all decisions regarding the strategy to be used to read thinkpad thermal sensors into a single enum, and refactors the thermal sensor reading code to use a much more readable (and easier to extend) switch() construct, in a separate function. Signed-off-by: Henrique de Moraes Holschuh diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index b81a95d..baf9492 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -217,6 +217,17 @@ IBM_HANDLE(sfan, ec, "SFAN", /* 570 */ #define IBM_HKEY_HID "IBM0068" #define IBM_PCI_HID "PNP0A03" +enum thermal_access_mode { + IBMACPI_THERMAL_NONE = 0, /* No thermal support */ + IBMACPI_THERMAL_ACPI_TMP07, /* Use ACPI TMP0-7 */ + IBMACPI_THERMAL_ACPI_UPDT, /* Use ACPI TMP0-7 with UPDT */ +}; + +#define IBMACPI_MAX_THERMAL_SENSORS 8 /* Max thermal sensors supported */ +struct ibm_thermal_sensors_struct { + s32 temp[IBMACPI_MAX_THERMAL_SENSORS]; +}; + struct ibm_struct { char *name; char param[32]; @@ -1275,50 +1286,79 @@ static int acpi_ec_write(int i, u8 v) return 1; } -static int thermal_tmp_supported; -static int thermal_updt_supported; +static enum thermal_access_mode thermal_read_mode; static int thermal_init(void) { - /* temperatures not supported on 570, G4x, R30, R31, R32 */ - thermal_tmp_supported = acpi_evalf(ec_handle, NULL, "TMP7", "qv"); - - /* 600e/x, 770e, 770x */ - thermal_updt_supported = acpi_evalf(ec_handle, NULL, "UPDT", "qv"); + if (acpi_evalf(ec_handle, NULL, "TMP7", "qv")) { + if (acpi_evalf(ec_handle, NULL, "UPDT", "qv")) { + /* 600e/x, 770e, 770x */ + thermal_read_mode = IBMACPI_THERMAL_ACPI_UPDT; + } else { + /* Standard ACPI TMPx access, max 8 sensors */ + thermal_read_mode = IBMACPI_THERMAL_ACPI_TMP07; + } + } else { + /* temperatures not supported on 570, G4x, R30, R31, R32 */ + thermal_read_mode = IBMACPI_THERMAL_NONE; + } return 0; } -static int thermal_read(char *p) +static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s) { - int len = 0; + int i, t; + char tmpi[] = "TMPi"; - if (!thermal_tmp_supported) - len += sprintf(p + len, "temperatures:\tnot supported\n"); - else { - int i, t; - char tmpi[] = "TMPi"; - s8 tmp[8]; + if (!s) + return -EINVAL; - if (thermal_updt_supported) - if (!acpi_evalf(ec_handle, NULL, "UPDT", "v")) + switch (thermal_read_mode) { + case IBMACPI_THERMAL_ACPI_UPDT: + if (!acpi_evalf(ec_handle, NULL, "UPDT", "v")) + return -EIO; + for (i = 0; i < 8; i++) { + tmpi[3] = '0' + i; + if (!acpi_evalf(ec_handle, &t, tmpi, "d")) return -EIO; + s->temp[i] = (t - 2732) * 100; + } + return 8; + case IBMACPI_THERMAL_ACPI_TMP07: for (i = 0; i < 8; i++) { tmpi[3] = '0' + i; if (!acpi_evalf(ec_handle, &t, tmpi, "d")) return -EIO; - if (thermal_updt_supported) - tmp[i] = (t - 2732 + 5) / 10; - else - tmp[i] = t; + s->temp[i] = t * 1000; } + return 8; - len += sprintf(p + len, - "temperatures:\t%d %d %d %d %d %d %d %d\n", - tmp[0], tmp[1], tmp[2], tmp[3], - tmp[4], tmp[5], tmp[6], tmp[7]); + case IBMACPI_THERMAL_NONE: + default: + return 0; } +} + +static int thermal_read(char *p) +{ + int len = 0; + int n, i; + struct ibm_thermal_sensors_struct t; + + n = thermal_get_sensors(&t); + if (unlikely(n < 0)) + return n; + + len += sprintf(p + len, "temperatures:\t"); + + if (n > 0) { + for (i = 0; i < (n - 1); i++) + len += sprintf(p + len, "%d ", t.temp[i] / 1000); + len += sprintf(p + len, "%d\n", t.temp[i] / 1000); + } else + len += sprintf(p + len, "not supported\n"); return len; } -- cgit v0.10.2 From 60eb0b35a9cc3400251cb4028d100e350649cf8a Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 24 Nov 2006 11:47:08 -0200 Subject: ACPI: ibm-acpi: Implement direct-ec-access thermal reading modes for up to 16 sensors This patch extends ibm-acpi to support reading thermal sensors directly through ACPI EC register access. It uses a DMI match to detect ThinkPads with a new-style embedded controller, that are known to have forward- compatible register maps and use 0x00 to fill in non-used registers and export thermal sensors at EC offsets 0x78-7F and 0xC0-C7. Direct ACPI EC register access is implemented for 8-sensor and 16-sensor new-style ThinkPad controller firmwares as an experimental feature. The code does some limited sanity checks on the temperatures read through EC access, and will default to the old ACPI TMP0-7 mode if anything is amiss. Userspace ABI is not changed for 8 sensors, but /proc/acpi/ibm/thermal is extended for 16 sensors if the firmware supports 16 sensors. A documentation update is also provided. The information about the ThinkPad register map was determined by studying ibm-acpi "ecdump" output from various ThinkPad models, submitted by subscribers of the linux-thinkpad mailinglist. Futher information was gathered from the DSDT tables, as they describe the EC register map in recent ThinkPads. DSDT source shows that TMP0-7 access and direct register access are actually the same thing on these firmwares, but unfortunately IBM never did update their DSDT EC register map to export TMP8-TMP15 for the second range of sensors. Signed-off-by: Henrique de Moraes Holschuh diff --git a/Documentation/ibm-acpi.txt b/Documentation/ibm-acpi.txt index e50595b..30f09e7 100644 --- a/Documentation/ibm-acpi.txt +++ b/Documentation/ibm-acpi.txt @@ -398,25 +398,56 @@ Temperature sensors -- /proc/acpi/ibm/thermal Most ThinkPads include six or more separate temperature sensors but only expose the CPU temperature through the standard ACPI methods. -This feature shows readings from up to eight different sensors. Some -readings may not be valid, e.g. may show large negative values. For -example, on the X40, a typical output may be: +This feature shows readings from up to eight different sensors on older +ThinkPads, and it has experimental support for up to sixteen different +sensors on newer ThinkPads. Readings from sensors that are not available +return -128. +No commands can be written to this file. + +EXPERIMENTAL: The 16-sensors feature is marked EXPERIMENTAL because the +implementation directly accesses hardware registers and may not work as +expected. USE WITH CAUTION! To use this feature, you need to supply the +experimental=1 parameter when loading the module. When EXPERIMENTAL +mode is enabled, reading the first 8 sensors on newer ThinkPads will +also use an new experimental thermal sensor access mode. + +For example, on the X40, a typical output may be: temperatures: 42 42 45 41 36 -128 33 -128 -Thomas Gruber took his R51 apart and traced all six active sensors in -his laptop (the location of sensors may vary on other models): +EXPERIMENTAL: On the T43/p, a typical output may be: +temperatures: 48 48 36 52 38 -128 31 -128 48 52 48 -128 -128 -128 -128 -128 + +The mapping of thermal sensors to physical locations varies depending on +system-board model (and thus, on ThinkPad model). + +http://thinkwiki.org/wiki/Thermal_Sensors is a public wiki page that +tries to track down these locations for various models. + +Most (newer?) models seem to follow this pattern: 1: CPU -2: Mini PCI Module -3: HDD +2: (depends on model) +3: (depends on model) 4: GPU -5: Battery -6: N/A -7: Battery -8: N/A +5: Main battery: main sensor +6: Bay battery: main sensor +7: Main battery: secondary sensor +8: Bay battery: secondary sensor +9-15: (depends on model) + +For the R51 (source: Thomas Gruber): +2: Mini-PCI +3: Internal HDD + +For the T43, T43/p (source: Shmidoax/Thinkwiki.org) +http://thinkwiki.org/wiki/Thermal_Sensors#ThinkPad_T43.2C_T43p +2: System board, left side (near PCMCIA slot), reported as HDAPS temp +3: PCMCIA slot +9: MCH (northbridge) to DRAM Bus +10: ICH (southbridge), under Mini-PCI card, under touchpad +11: Power regulator, underside of system board, below F2 key -No commands can be written to this file. EXPERIMENTAL: Embedded controller register dump -- /proc/acpi/ibm/ecdump ------------------------------------------------------------------------ diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index baf9492..1703c61 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -80,6 +80,7 @@ #include #include #include +#include #include #include @@ -221,13 +222,17 @@ enum thermal_access_mode { IBMACPI_THERMAL_NONE = 0, /* No thermal support */ IBMACPI_THERMAL_ACPI_TMP07, /* Use ACPI TMP0-7 */ IBMACPI_THERMAL_ACPI_UPDT, /* Use ACPI TMP0-7 with UPDT */ + IBMACPI_THERMAL_TPEC_8, /* Use ACPI EC regs, 8 sensors */ + IBMACPI_THERMAL_TPEC_16, /* Use ACPI EC regs, 16 sensors */ }; -#define IBMACPI_MAX_THERMAL_SENSORS 8 /* Max thermal sensors supported */ +#define IBMACPI_MAX_THERMAL_SENSORS 16 /* Max thermal sensors supported */ struct ibm_thermal_sensors_struct { s32 temp[IBMACPI_MAX_THERMAL_SENSORS]; }; +static int ibm_thinkpad_ec_found; + struct ibm_struct { char *name; char param[32]; @@ -1290,7 +1295,52 @@ static enum thermal_access_mode thermal_read_mode; static int thermal_init(void) { - if (acpi_evalf(ec_handle, NULL, "TMP7", "qv")) { + u8 t, ta1, ta2; + int i; + int acpi_tmp7 = acpi_evalf(ec_handle, NULL, "TMP7", "qv"); + + if (ibm_thinkpad_ec_found && experimental) { + /* + * Direct EC access mode: sensors at registers + * 0x78-0x7F, 0xC0-0xC7. Registers return 0x00 for + * non-implemented, thermal sensors return 0x80 when + * not available + */ + + ta1 = ta2 = 0; + for (i = 0; i < 8; i++) { + if (likely(acpi_ec_read(0x78 + i, &t))) { + ta1 |= t; + } else { + ta1 = 0; + break; + } + if (likely(acpi_ec_read(0xC0 + i, &t))) { + ta2 |= t; + } else { + ta1 = 0; + break; + } + } + if (ta1 == 0) { + /* This is sheer paranoia, but we handle it anyway */ + if (acpi_tmp7) { + printk(IBM_ERR + "ThinkPad ACPI EC access misbehaving, " + "falling back to ACPI TMPx access mode\n"); + thermal_read_mode = IBMACPI_THERMAL_ACPI_TMP07; + } else { + printk(IBM_ERR + "ThinkPad ACPI EC access misbehaving, " + "disabling thermal sensors access\n"); + thermal_read_mode = IBMACPI_THERMAL_NONE; + } + } else { + thermal_read_mode = + (ta2 != 0) ? + IBMACPI_THERMAL_TPEC_16 : IBMACPI_THERMAL_TPEC_8; + } + } else if (acpi_tmp7) { if (acpi_evalf(ec_handle, NULL, "UPDT", "qv")) { /* 600e/x, 770e, 770x */ thermal_read_mode = IBMACPI_THERMAL_ACPI_UPDT; @@ -1309,12 +1359,30 @@ static int thermal_init(void) static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s) { int i, t; + s8 tmp; char tmpi[] = "TMPi"; if (!s) return -EINVAL; switch (thermal_read_mode) { +#if IBMACPI_MAX_THERMAL_SENSORS >= 16 + case IBMACPI_THERMAL_TPEC_16: + for (i = 0; i < 8; i++) { + if (!acpi_ec_read(0xC0 + i, &tmp)) + return -EIO; + s->temp[i + 8] = tmp * 1000; + } + /* fallthrough */ +#endif + case IBMACPI_THERMAL_TPEC_8: + for (i = 0; i < 8; i++) { + if (!acpi_ec_read(0x78 + i, &tmp)) + return -EIO; + s->temp[i] = tmp * 1000; + } + return (thermal_read_mode == IBMACPI_THERMAL_TPEC_16) ? 16 : 8; + case IBMACPI_THERMAL_ACPI_UPDT: if (!acpi_evalf(ec_handle, NULL, "UPDT", "v")) return -EIO; @@ -2052,6 +2120,24 @@ static void acpi_ibm_exit(void) remove_proc_entry(IBM_DIR, acpi_root_dir); } +static int __init check_dmi_for_ec(void) +{ + struct dmi_device *dev = NULL; + + /* + * ThinkPad T23 or newer, A31 or newer, R50e or newer, + * X32 or newer, all Z series; Some models must have an + * up-to-date BIOS or they will not be detected. + * + * See http://thinkwiki.org/wiki/List_of_DMI_IDs + */ + while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) { + if (strstr(dev->name, "IBM ThinkPad Embedded Controller")) + return 1; + } + return 0; +} + static int __init acpi_ibm_init(void) { int ret, i; @@ -2071,6 +2157,9 @@ static int __init acpi_ibm_init(void) return -ENODEV; } + /* Models with newer firmware report the EC in DMI */ + ibm_thinkpad_ec_found = check_dmi_for_ec(); + /* these handles are not required */ IBM_HANDLE_INIT(vid); IBM_HANDLE_INIT(vid2); -- cgit v0.10.2 From 88679a15b3a84366e90cee2a84973abef962b727 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 24 Nov 2006 11:47:09 -0200 Subject: ACPI: ibm-acpi: document thermal sensor locations for the A31 The A31 has a very atypical layout, so I separated its thermal sensors location in a separate patch. Signed-off-by: Henrique de Moraes Holschuh diff --git a/Documentation/ibm-acpi.txt b/Documentation/ibm-acpi.txt index 30f09e7..333b8eb 100644 --- a/Documentation/ibm-acpi.txt +++ b/Documentation/ibm-acpi.txt @@ -448,6 +448,17 @@ http://thinkwiki.org/wiki/Thermal_Sensors#ThinkPad_T43.2C_T43p 10: ICH (southbridge), under Mini-PCI card, under touchpad 11: Power regulator, underside of system board, below F2 key +The A31 has a very atypical layout for the thermal sensors +(source: Milos Popovic, http://thinkwiki.org/wiki/Thermal_Sensors#ThinkPad_A31) +1: CPU +2: Main Battery: main sensor +3: Power Converter +4: Bay Battery: main sensor +5: MCH (northbridge) +6: PCMCIA/ambient +7: Main Battery: secondary sensor +8: Bay Battery: secondary sensor + EXPERIMENTAL: Embedded controller register dump -- /proc/acpi/ibm/ecdump ------------------------------------------------------------------------ -- cgit v0.10.2 From 69ba91cbd6d79aa197adbdd10a44e71c84044b44 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 24 Nov 2006 11:47:09 -0200 Subject: ACPI: ibm-acpi: prepare to cleanup fan_read and fan_write This patch lays some groundwork for a fan_read and fan_write cleanup in the next patches. To do so, it provides a new fan_init initializer, and also some constants (through enums). Signed-off-by: Henrique de Moraes Holschuh diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index 1703c61..7d0bd61 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -231,6 +231,31 @@ struct ibm_thermal_sensors_struct { s32 temp[IBMACPI_MAX_THERMAL_SENSORS]; }; +enum fan_status_access_mode { + IBMACPI_FAN_NONE = 0, /* No fan status or control */ + IBMACPI_FAN_RD_ACPI_GFAN, /* Use ACPI GFAN */ + IBMACPI_FAN_RD_TPEC, /* Use ACPI EC regs 0x2f, 0x84-0x85 */ +}; + +enum fan_control_access_mode { + IBMACPI_FAN_WR_NONE = 0, /* No fan control */ + IBMACPI_FAN_WR_ACPI_SFAN, /* Use ACPI SFAN */ + IBMACPI_FAN_WR_TPEC, /* Use ACPI EC reg 0x2f */ + IBMACPI_FAN_WR_ACPI_FANS, /* Use ACPI FANS and EC reg 0x2f */ +}; + +enum fan_control_commands { + IBMACPI_FAN_CMD_SPEED = 0x0001, /* speed command */ + IBMACPI_FAN_CMD_LEVEL = 0x0002, /* level command */ + IBMACPI_FAN_CMD_ENABLE = 0x0004, /* enable/disable cmd */ +}; + +enum { /* Fan control constants */ + fan_status_offset = 0x2f, /* EC register 0x2f */ + fan_rpm_offset = 0x84, /* EC register 0x84: LSB, 0x85 MSB (RPM) + * 0x84 must be read before 0x85 */ +}; + static int ibm_thinkpad_ec_found; struct ibm_struct { @@ -1659,8 +1684,59 @@ static int volume_write(char *buf) return 0; } -static int fan_status_offset = 0x2f; -static int fan_rpm_offset = 0x84; +static enum fan_status_access_mode fan_status_access_mode; +static enum fan_control_access_mode fan_control_access_mode; +static enum fan_control_commands fan_control_commands; + +static int fan_init(void) +{ + u8 status; + + fan_status_access_mode = IBMACPI_FAN_NONE; + fan_control_access_mode = IBMACPI_FAN_WR_NONE; + fan_control_commands = 0; + + if (gfan_handle) { + /* 570, 600e/x, 770e, 770x */ + fan_status_access_mode = IBMACPI_FAN_RD_ACPI_GFAN; + } else { + /* all other ThinkPads: note that even old-style + * ThinkPad ECs supports the fan control register */ + if (likely(acpi_ec_read(fan_status_offset, &status))) { + fan_status_access_mode = IBMACPI_FAN_RD_TPEC; + } else { + printk(IBM_ERR + "ThinkPad ACPI EC access misbehaving, " + "fan status and control unavailable\n"); + return 0; + } + } + + if (sfan_handle) { + /* 570, 770x-JL */ + fan_control_access_mode = IBMACPI_FAN_WR_ACPI_SFAN; + fan_control_commands |= IBMACPI_FAN_CMD_LEVEL; + } else { + if (!gfan_handle) { + /* gfan without sfan means no fan control */ + /* all other models implement TP EC 0x2f control */ + + if (fans_handle) { + /* X31, X40 */ + fan_control_access_mode = + IBMACPI_FAN_WR_ACPI_FANS; + fan_control_commands |= + IBMACPI_FAN_CMD_SPEED | + IBMACPI_FAN_CMD_ENABLE; + } else { + fan_control_access_mode = IBMACPI_FAN_WR_TPEC; + fan_control_commands |= IBMACPI_FAN_CMD_ENABLE; + } + } + } + + return 0; +} static int fan_read(char *p) { @@ -1849,6 +1925,7 @@ static struct ibm_struct ibms[] = { .name = "fan", .read = fan_read, .write = fan_write, + .init = fan_init, .experimental = 1, }, }; -- cgit v0.10.2 From 3ef8a6096ca98d45a54999a97c7c8e14977d2e3e Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 24 Nov 2006 11:47:10 -0200 Subject: ACPI: ibm-acpi: clean up fan_read This patch cleans up fan_read so that it is much easier to read and extend. The patch fixes the userspace ABI to return "status: not supported" (like all other ibm-acpi functions) when neither fan status or fan control are possible. It also fixes the userspace ABI to return EIO if ACPI access to the EC fails, instead of returning "status: unreadable" or "speed: unreadable". Signed-off-by: Henrique de Moraes Holschuh diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index 7d0bd61..e8de5e3 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -1741,40 +1741,48 @@ static int fan_init(void) static int fan_read(char *p) { int len = 0; - int s; u8 lo, hi, status; - if (gfan_handle) { + switch (fan_status_access_mode) { + case IBMACPI_FAN_RD_ACPI_GFAN: /* 570, 600e/x, 770e, 770x */ - if (!acpi_evalf(gfan_handle, &s, NULL, "d")) + if (unlikely(!acpi_evalf(gfan_handle, &status, NULL, "d"))) return -EIO; - len += sprintf(p + len, "level:\t\t%d\n", s); - } else { + len += sprintf(p + len, "level:\t\t%d\n", status); + + break; + + case IBMACPI_FAN_RD_TPEC: /* all except 570, 600e/x, 770e, 770x */ - if (!acpi_ec_read(fan_status_offset, &status)) - len += sprintf(p + len, "status:\t\tunreadable\n"); + if (unlikely(!acpi_ec_read(fan_status_offset, &status))) + return -EIO; else len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 7)); - if (!acpi_ec_read(fan_rpm_offset, &lo) || - !acpi_ec_read(fan_rpm_offset + 1, &hi)) - len += sprintf(p + len, "speed:\t\tunreadable\n"); + if (unlikely(!acpi_ec_read(fan_rpm_offset, &lo) || + !acpi_ec_read(fan_rpm_offset + 1, &hi))) + return -EIO; else len += sprintf(p + len, "speed:\t\t%d\n", (hi << 8) + lo); + + break; + + case IBMACPI_FAN_NONE: + default: + len += sprintf(p + len, "status:\t\tnot supported\n"); } - if (sfan_handle) - /* 570, 770x-JL */ + if (fan_control_commands & IBMACPI_FAN_CMD_LEVEL) len += sprintf(p + len, "commands:\tlevel " " ( is 0-7)\n"); - if (!gfan_handle) - /* all except 570, 600e/x, 770e, 770x */ + + if (fan_control_commands & IBMACPI_FAN_CMD_ENABLE) len += sprintf(p + len, "commands:\tenable, disable\n"); - if (fans_handle) - /* X31, X40 */ + + if (fan_control_commands & IBMACPI_FAN_CMD_SPEED) len += sprintf(p + len, "commands:\tspeed " " ( is 0-65535)\n"); -- cgit v0.10.2 From c52f0aa574246f133a0bc2041e9468a06d34da7b Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 24 Nov 2006 11:47:10 -0200 Subject: ACPI: ibm-acpi: break fan_read into separate functions This patch breaks fan_read mechanics into a generic function to get fan status and speed, and leaves only the procfs interface code in fan_read. Signed-off-by: Henrique de Moraes Holschuh diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index e8de5e3..9275dfc 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -1738,36 +1738,90 @@ static int fan_init(void) return 0; } -static int fan_read(char *p) +static int fan_get_status(u8 *status) { - int len = 0; - u8 lo, hi, status; + u8 s; switch (fan_status_access_mode) { case IBMACPI_FAN_RD_ACPI_GFAN: /* 570, 600e/x, 770e, 770x */ - if (unlikely(!acpi_evalf(gfan_handle, &status, NULL, "d"))) + + if (unlikely(!acpi_evalf(gfan_handle, &s, NULL, "d"))) return -EIO; - len += sprintf(p + len, "level:\t\t%d\n", status); + if (likely(status)) + *status = s & 0x07; break; case IBMACPI_FAN_RD_TPEC: /* all except 570, 600e/x, 770e, 770x */ - if (unlikely(!acpi_ec_read(fan_status_offset, &status))) + if (unlikely(!acpi_ec_read(fan_status_offset, &s))) return -EIO; - else - len += sprintf(p + len, "status:\t\t%s\n", - enabled(status, 7)); + if (likely(status)) + *status = s; + + break; + + default: + return -ENXIO; + } + + return 0; +} + +static int fan_get_speed(unsigned int *speed) +{ + u8 hi, lo; + + switch (fan_status_access_mode) { + case IBMACPI_FAN_RD_TPEC: + /* all except 570, 600e/x, 770e, 770x */ if (unlikely(!acpi_ec_read(fan_rpm_offset, &lo) || !acpi_ec_read(fan_rpm_offset + 1, &hi))) return -EIO; - else - len += sprintf(p + len, "speed:\t\t%d\n", - (hi << 8) + lo); + if (likely(speed)) + *speed = (hi << 8) | lo; + + break; + + default: + return -ENXIO; + } + + return 0; +} + +static int fan_read(char *p) +{ + int len = 0; + int rc; + u8 status; + unsigned int speed = 0; + + switch (fan_status_access_mode) { + case IBMACPI_FAN_RD_ACPI_GFAN: + /* 570, 600e/x, 770e, 770x */ + if ((rc = fan_get_status(&status)) < 0) + return rc; + + len += sprintf(p + len, "level:\t\t%d\n", status); + + break; + + case IBMACPI_FAN_RD_TPEC: + /* all except 570, 600e/x, 770e, 770x */ + if ((rc = fan_get_status(&status)) < 0) + return rc; + + len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 7)); + + if ((rc = fan_get_speed(&speed)) < 0) + return rc; + + len += sprintf(p + len, "speed:\t\t%d\n", speed); break; case IBMACPI_FAN_NONE: -- cgit v0.10.2 From 18ad7996e17649d81c15a2c9dae03c75050a05a8 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 24 Nov 2006 11:47:11 -0200 Subject: ACPI: ibm-acpi: cleanup fan_write This patch cleans up fan_write so that it is much easier to read and extend. It separates the proc api handling from the operations themselves. Signed-off-by: Henrique de Moraes Holschuh diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index 9275dfc..187af1b 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -1843,40 +1843,143 @@ static int fan_read(char *p) return len; } -static int fan_write(char *buf) +static int fan_set_level(int level) { - char *cmd; - int level, speed; - - while ((cmd = next_cmd(&buf))) { - if (sfan_handle && - sscanf(cmd, "level %d", &level) == 1 && - level >= 0 && level <= 7) { - /* 570, 770x-JL */ + switch (fan_control_access_mode) { + case IBMACPI_FAN_WR_ACPI_SFAN: + if (level >= 0 && level <= 7) { if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", level)) return -EIO; - } else if (!gfan_handle && strlencmp(cmd, "enable") == 0) { - /* all except 570, 600e/x, 770e, 770x */ - if (!acpi_ec_write(fan_status_offset, 0x80)) - return -EIO; - } else if (!gfan_handle && strlencmp(cmd, "disable") == 0) { - /* all except 570, 600e/x, 770e, 770x */ - if (!acpi_ec_write(fan_status_offset, 0x00)) - return -EIO; - } else if (fans_handle && - sscanf(cmd, "speed %d", &speed) == 1 && - speed >= 0 && speed <= 65535) { - /* X31, X40 */ + } else + return -EINVAL; + break; + + default: + return -ENXIO; + } + return 0; +} + +static int fan_set_enable(void) +{ + switch (fan_control_access_mode) { + case IBMACPI_FAN_WR_ACPI_FANS: + case IBMACPI_FAN_WR_TPEC: + if (!acpi_ec_write(fan_status_offset, 0x80)) + return -EIO; + break; + + default: + return -ENXIO; + } + return 0; +} + +static int fan_set_disable(void) +{ + switch (fan_control_access_mode) { + case IBMACPI_FAN_WR_ACPI_FANS: + case IBMACPI_FAN_WR_TPEC: + if (!acpi_ec_write(fan_status_offset, 0x00)) + return -EIO; + break; + + default: + return -ENXIO; + } + return 0; +} + +static int fan_set_speed(int speed) +{ + switch (fan_control_access_mode) { + case IBMACPI_FAN_WR_ACPI_FANS: + if (speed >= 0 && speed <= 65535) { if (!acpi_evalf(fans_handle, NULL, NULL, "vddd", speed, speed, speed)) return -EIO; } else return -EINVAL; - } + break; + default: + return -ENXIO; + } return 0; } +static int fan_write_cmd_level(const char *cmd, int *rc) +{ + int level; + + if (sscanf(cmd, "level %d", &level) != 1) + return 0; + + if ((*rc = fan_set_level(level)) == -ENXIO) + printk(IBM_ERR "level command accepted for unsupported " + "access mode %d", fan_control_access_mode); + + return 1; +} + +static int fan_write_cmd_enable(const char *cmd, int *rc) +{ + if (strlencmp(cmd, "enable") != 0) + return 0; + + if ((*rc = fan_set_enable()) == -ENXIO) + printk(IBM_ERR "enable command accepted for unsupported " + "access mode %d", fan_control_access_mode); + + return 1; +} + +static int fan_write_cmd_disable(const char *cmd, int *rc) +{ + if (strlencmp(cmd, "disable") != 0) + return 0; + + if ((*rc = fan_set_disable()) == -ENXIO) + printk(IBM_ERR "disable command accepted for unsupported " + "access mode %d", fan_control_access_mode); + + return 1; +} + +static int fan_write_cmd_speed(const char *cmd, int *rc) +{ + int speed; + + if (sscanf(cmd, "speed %d", &speed) != 1) + return 0; + + if ((*rc = fan_set_speed(speed)) == -ENXIO) + printk(IBM_ERR "speed command accepted for unsupported " + "access mode %d", fan_control_access_mode); + + return 1; +} + +static int fan_write(char *buf) +{ + char *cmd; + int rc = 0; + + while (!rc && (cmd = next_cmd(&buf))) { + if (!((fan_control_commands & IBMACPI_FAN_CMD_LEVEL) && + fan_write_cmd_level(cmd, &rc)) && + !((fan_control_commands & IBMACPI_FAN_CMD_ENABLE) && + (fan_write_cmd_enable(cmd, &rc) || + fan_write_cmd_disable(cmd, &rc))) && + !((fan_control_commands & IBMACPI_FAN_CMD_SPEED) && + fan_write_cmd_speed(cmd, &rc)) + ) + rc = -EINVAL; + } + + return rc; +} + static struct ibm_struct ibms[] = { { .name = "driver", -- cgit v0.10.2 From a8b7a6626d7605a795b33317cd730b7d76da3d0a Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 24 Nov 2006 11:47:11 -0200 Subject: ACPI: ibm-acpi: document fan control This patch documents the ThinkPad fan control strategies. Source of the data: 0. ibm-acpi source 1. DSDTs for various ThinkPads (770, X31, X40, X41, T43, A21m, T22) 2. http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues 3. http://thinkwiki.org/wiki/How_to_control_fan_speed 4. Various threads about windows fan control utilities in thinkpads.com Signed-off-by: Henrique de Moraes Holschuh diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index 187af1b..faf78d3 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -205,7 +205,7 @@ IBM_HANDLE(led, ec, "SLED", /* 570 */ IBM_HANDLE(beep, ec, "BEEP"); /* all except R30, R31 */ IBM_HANDLE(ecrd, ec, "ECRD"); /* 570 */ IBM_HANDLE(ecwr, ec, "ECWR"); /* 570 */ -IBM_HANDLE(fans, ec, "FANS"); /* X31, X40 */ +IBM_HANDLE(fans, ec, "FANS"); /* X31, X40, X41 */ IBM_HANDLE(gfan, ec, "GFAN", /* 570 */ "\\FSPD", /* 600e/x, 770e, 770x */ @@ -231,6 +231,106 @@ struct ibm_thermal_sensors_struct { s32 temp[IBMACPI_MAX_THERMAL_SENSORS]; }; +/* + * FAN ACCESS MODES + * + * IBMACPI_FAN_RD_ACPI_GFAN: + * ACPI GFAN method: returns fan level + * + * see IBMACPI_FAN_WR_ACPI_SFAN + * EC 0x2f not available if GFAN exists + * + * IBMACPI_FAN_WR_ACPI_SFAN: + * ACPI SFAN method: sets fan level, 0 (stop) to 7 (max) + * + * EC 0x2f might be available *for reading*, but never for writing. + * + * IBMACPI_FAN_WR_TPEC: + * ThinkPad EC register 0x2f (HFSP): fan control loop mode Supported + * on almost all ThinkPads + * + * Fan speed changes of any sort (including those caused by the + * disengaged mode) are usually done slowly by the firmware as the + * maximum ammount of fan duty cycle change per second seems to be + * limited. + * + * Reading is not available if GFAN exists. + * Writing is not available if SFAN exists. + * + * Bits + * 7 automatic mode engaged; + * (default operation mode of the ThinkPad) + * fan level is ignored in this mode. + * 6 disengage mode (takes precedence over bit 7); + * not available on all thinkpads. May disable + * the tachometer, and speeds up fan to 100% duty-cycle, + * which speeds it up far above the standard RPM + * levels. It is not impossible that it could cause + * hardware damage. + * 5-3 unused in some models. Extra bits for fan level + * in others, but still useless as all values above + * 7 map to the same speed as level 7 in these models. + * 2-0 fan level (0..7 usually) + * 0x00 = stop + * 0x07 = max (set when temperatures critical) + * Some ThinkPads may have other levels, see + * IBMACPI_FAN_WR_ACPI_FANS (X31/X40/X41) + * + * FIRMWARE BUG: on some models, EC 0x2f might not be initialized at + * boot. Apparently the EC does not intialize it, so unless ACPI DSDT + * does so, its initial value is meaningless (0x07). + * + * For firmware bugs, refer to: + * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues + * + * ---- + * + * ThinkPad EC register 0x84 (LSB), 0x85 (MSB): + * Main fan tachometer reading (in RPM) + * + * This register is present on all ThinkPads with a new-style EC, and + * it is known not to be present on the A21m/e, and T22, as there is + * something else in offset 0x84 according to the ACPI DSDT. Other + * ThinkPads from this same time period (and earlier) probably lack the + * tachometer as well. + * + * Unfortunately a lot of ThinkPads with new-style ECs but whose firwmare + * was never fixed by IBM to report the EC firmware version string + * probably support the tachometer (like the early X models), so + * detecting it is quite hard. We need more data to know for sure. + * + * FIRMWARE BUG: always read 0x84 first, otherwise incorrect readings + * might result. + * + * FIRMWARE BUG: when EC 0x2f bit 6 is set (disengaged mode), this + * register is not invalidated in ThinkPads that disable tachometer + * readings. Thus, the tachometer readings go stale. + * + * For firmware bugs, refer to: + * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues + * + * IBMACPI_FAN_WR_ACPI_FANS: + * ThinkPad X31, X40, X41. Not available in the X60. + * + * FANS ACPI handle: takes three arguments: low speed, medium speed, + * high speed. ACPI DSDT seems to map these three speeds to levels + * as follows: STOP LOW LOW MED MED HIGH HIGH HIGH HIGH + * (this map is stored on FAN0..FAN8 as "0,1,1,2,2,3,3,3,3") + * + * The speeds are stored on handles + * (FANA:FAN9), (FANC:FANB), (FANE:FAND). + * + * There are three default speed sets, acessible as handles: + * FS1L,FS1M,FS1H; FS2L,FS2M,FS2H; FS3L,FS3M,FS3H + * + * ACPI DSDT switches which set is in use depending on various + * factors. + * + * IBMACPI_FAN_WR_TPEC is also available and should be used to + * command the fan. The X31/X40/X41 seems to have 8 fan levels, + * but the ACPI tables just mention level 7. + */ + enum fan_status_access_mode { IBMACPI_FAN_NONE = 0, /* No fan status or control */ IBMACPI_FAN_RD_ACPI_GFAN, /* Use ACPI GFAN */ @@ -1722,7 +1822,7 @@ static int fan_init(void) /* all other models implement TP EC 0x2f control */ if (fans_handle) { - /* X31, X40 */ + /* X31, X40, X41 */ fan_control_access_mode = IBMACPI_FAN_WR_ACPI_FANS; fan_control_commands |= @@ -1742,6 +1842,9 @@ static int fan_get_status(u8 *status) { u8 s; + /* TODO: + * Add IBMACPI_FAN_RD_ACPI_FANS ? */ + switch (fan_status_access_mode) { case IBMACPI_FAN_RD_ACPI_GFAN: /* 570, 600e/x, 770e, 770x */ @@ -1950,6 +2053,9 @@ static int fan_write_cmd_speed(const char *cmd, int *rc) { int speed; + /* TODO: + * Support speed ? */ + if (sscanf(cmd, "speed %d", &speed) != 1) return 0; -- cgit v0.10.2 From bab812a329cc244ca63c2675b0e05016518855ce Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 24 Nov 2006 11:47:12 -0200 Subject: ACPI: ibm-acpi: extend fan status functions This patch fixes fan_read to return correct values for all fan access modes. It also implements some fan access mode status output that was missing, and normalizes the proc fan abi to return consistent data across all fan read/write modes. Userspace ABI changes and extensions: 1. Return status: enable/disable for *all* modes (this actually improves compatibility with userspace utils!) 2. Return level: auto and level: disengaged for EC 2f access mode 3. Return level: for EC 0x2f access mode 4. Return level 0 as well as "disabled" in level-aware modes Signed-off-by: Henrique de Moraes Holschuh diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index faf78d3..b6ad2ed 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -354,6 +354,11 @@ enum { /* Fan control constants */ fan_status_offset = 0x2f, /* EC register 0x2f */ fan_rpm_offset = 0x84, /* EC register 0x84: LSB, 0x85 MSB (RPM) * 0x84 must be read before 0x85 */ + + IBMACPI_FAN_EC_DISENGAGED = 0x40, /* EC mode: tachometer + * disengaged */ + IBMACPI_FAN_EC_AUTO = 0x80, /* EC mode: auto fan + * control */ }; static int ibm_thinkpad_ec_found; @@ -1910,8 +1915,9 @@ static int fan_read(char *p) if ((rc = fan_get_status(&status)) < 0) return rc; - len += sprintf(p + len, "level:\t\t%d\n", status); - + len += sprintf(p + len, "status:\t\t%s\n" + "level:\t\t%d\n", + (status != 0) ? "enabled" : "disabled", status); break; case IBMACPI_FAN_RD_TPEC: @@ -1919,12 +1925,21 @@ static int fan_read(char *p) if ((rc = fan_get_status(&status)) < 0) return rc; - len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 7)); + len += sprintf(p + len, "status:\t\t%s\n", + (status != 0) ? "enabled" : "disabled"); if ((rc = fan_get_speed(&speed)) < 0) return rc; len += sprintf(p + len, "speed:\t\t%d\n", speed); + + if (status & IBMACPI_FAN_EC_DISENGAGED) + /* Disengaged mode takes precedence */ + len += sprintf(p + len, "level:\t\tdisengaged\n"); + else if (status & IBMACPI_FAN_EC_AUTO) + len += sprintf(p + len, "level:\t\tauto\n"); + else + len += sprintf(p + len, "level:\t\t%d\n", status); break; case IBMACPI_FAN_NONE: -- cgit v0.10.2 From 1c6a334e9c028c2b72c5350650cb14e6d5fdc232 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 24 Nov 2006 11:47:12 -0200 Subject: ACPI: ibm-acpi: fix and extend fan enable This patch fix fan enable to attempt to do the right thing and not slow down the fan if it is forced to the maximum speed. It also extends fan enable to work on older thinkpads. ABI changes: 1. Support enable/disable for all level-based write access modes Signed-off-by: Henrique de Moraes Holschuh diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index b6ad2ed..ecb5ece 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -1820,7 +1820,8 @@ static int fan_init(void) if (sfan_handle) { /* 570, 770x-JL */ fan_control_access_mode = IBMACPI_FAN_WR_ACPI_SFAN; - fan_control_commands |= IBMACPI_FAN_CMD_LEVEL; + fan_control_commands |= + IBMACPI_FAN_CMD_LEVEL | IBMACPI_FAN_CMD_ENABLE; } else { if (!gfan_handle) { /* gfan without sfan means no fan control */ @@ -1980,10 +1981,34 @@ static int fan_set_level(int level) static int fan_set_enable(void) { + u8 s; + int rc; + switch (fan_control_access_mode) { case IBMACPI_FAN_WR_ACPI_FANS: case IBMACPI_FAN_WR_TPEC: - if (!acpi_ec_write(fan_status_offset, 0x80)) + if ((rc = fan_get_status(&s)) < 0) + return rc; + + /* Don't go out of emergency fan mode */ + if (s != 7) + s = IBMACPI_FAN_EC_AUTO; + + if (!acpi_ec_write(fan_status_offset, s)) + return -EIO; + break; + + case IBMACPI_FAN_WR_ACPI_SFAN: + if ((rc = fan_get_status(&s)) < 0) + return rc; + + s &= 0x07; + + /* Set fan to at least level 4 */ + if (s < 4) + s = 4; + + if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", s)) return -EIO; break; @@ -2002,6 +2027,11 @@ static int fan_set_disable(void) return -EIO; break; + case IBMACPI_FAN_WR_ACPI_SFAN: + if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", 0x00)) + return -EIO; + break; + default: return -ENXIO; } -- cgit v0.10.2 From a12095c2b50c8a7c80517e37c00d6e6c863d43c5 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 24 Nov 2006 11:47:13 -0200 Subject: ACPI: ibm-acpi: fix and extend fan control functions This patch extend fan control functions, implementing enable/disable for all write access modes, implementing level control for all level-capable write access modes. The patch also updates the documentation, explaining levels auto and disengaged. ABI changes: 1. Support level 0 as an equivalent to disable 2. Add support for level auto and level disengaged when doing EC 0x2f fan control 3. Support enable/disable for all level-based write access modes 4. Add support for level command on FANS thinkpads, as per thinkwiki reports Signed-off-by: Henrique de Moraes Holschuh diff --git a/Documentation/ibm-acpi.txt b/Documentation/ibm-acpi.txt index 333b8eb..cbd3a60 100644 --- a/Documentation/ibm-acpi.txt +++ b/Documentation/ibm-acpi.txt @@ -571,27 +571,57 @@ directly accesses hardware registers and may not work as expected. USE WITH CAUTION! To use this feature, you need to supply the experimental=1 parameter when loading the module. -This feature attempts to show the current fan speed. The speed is read -directly from the hardware registers of the embedded controller. This -is known to work on later R, T and X series ThinkPads but may show a -bogus value on other models. +This feature attempts to show the current fan speed, control mode and +other fan data that might be available. The speed is read directly +from the hardware registers of the embedded controller. This is known +to work on later R, T and X series ThinkPads but may show a bogus +value on other models. + +Most ThinkPad fans work in "levels". Level 0 stops the fan. The higher +the level, the higher the fan speed, although adjacent levels often map +to the same fan speed. 7 is the highest level, where the fan reaches +the maximum recommended speed. Level "auto" means the EC changes the +fan level according to some internal algorithm, usually based on +readings from the thermal sensors. Level "disengaged" means the EC +disables the speed-locked closed-loop fan control, and drives the fan as +fast as it can go, which might exceed hardware limits, so use this level +with caution. + +The fan usually ramps up or down slowly from one speed to another, +and it is normal for the EC to take several seconds to react to fan +commands. The fan may be enabled or disabled with the following commands: echo enable >/proc/acpi/ibm/fan echo disable >/proc/acpi/ibm/fan +Placing a fan on level 0 is the same as disabling it. Enabling a fan +will try to place it in a safe level if it is too slow or disabled. + WARNING WARNING WARNING: do not leave the fan disabled unless you are -monitoring the temperature sensor readings and you are ready to enable -it if necessary to avoid overheating. +monitoring all of the temperature sensor readings and you are ready to +enable it if necessary to avoid overheating. + +An enabled fan in level "auto" may stop spinning if the EC decides the +ThinkPad is cool enough and doesn't need the extra airflow. This is +normal, and the EC will spin the fan up if the varios thermal readings +rise too much. + +On the X40, this seems to depend on the CPU and HDD temperatures. +Specifically, the fan is turned on when either the CPU temperature +climbs to 56 degrees or the HDD temperature climbs to 46 degrees. The +fan is turned off when the CPU temperature drops to 49 degrees and the +HDD temperature drops to 41 degrees. These thresholds cannot +currently be controlled. + +The fan level can be controlled with the command: -The fan only runs if it's enabled *and* the various temperature -sensors which control it read high enough. On the X40, this seems to -depend on the CPU and HDD temperatures. Specifically, the fan is -turned on when either the CPU temperature climbs to 56 degrees or the -HDD temperature climbs to 46 degrees. The fan is turned off when the -CPU temperature drops to 49 degrees and the HDD temperature drops to -41 degrees. These thresholds cannot currently be controlled. + echo 'level ' > /proc/acpi/ibm/thermal + +Where is an integer from 0 to 7, or one of the words "auto" +or "disengaged" (without the quotes). Not all ThinkPads support the +"auto" and "disengaged" levels. On the X31 and X40 (and ONLY on those models), the fan speed can be controlled to a certain degree. Once the fan is running, it can be @@ -604,12 +634,9 @@ about 3700 to about 7350. Values outside this range either do not have any effect or the fan speed eventually settles somewhere in that range. The fan cannot be stopped or started with this command. -On the 570, temperature readings are not available through this -feature and the fan control works a little differently. The fan speed -is reported in levels from 0 (off) to 7 (max) and can be controlled -with the following command: - - echo 'level ' > /proc/acpi/ibm/thermal +The ThinkPad's ACPI DSDT code will reprogram the fan on its own when +certain conditions are met. It will override any fan programming done +through ibm-acpi. EXPERIMENTAL: WAN -- /proc/acpi/ibm/wan --------------------------------------- diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index ecb5ece..4001ad1 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -1833,10 +1833,13 @@ static int fan_init(void) IBMACPI_FAN_WR_ACPI_FANS; fan_control_commands |= IBMACPI_FAN_CMD_SPEED | + IBMACPI_FAN_CMD_LEVEL | IBMACPI_FAN_CMD_ENABLE; } else { fan_control_access_mode = IBMACPI_FAN_WR_TPEC; - fan_control_commands |= IBMACPI_FAN_CMD_ENABLE; + fan_control_commands |= + IBMACPI_FAN_CMD_LEVEL | + IBMACPI_FAN_CMD_ENABLE; } } } @@ -1948,9 +1951,20 @@ static int fan_read(char *p) len += sprintf(p + len, "status:\t\tnot supported\n"); } - if (fan_control_commands & IBMACPI_FAN_CMD_LEVEL) - len += sprintf(p + len, "commands:\tlevel " - " ( is 0-7)\n"); + if (fan_control_commands & IBMACPI_FAN_CMD_LEVEL) { + len += sprintf(p + len, "commands:\tlevel "); + + switch (fan_control_access_mode) { + case IBMACPI_FAN_WR_ACPI_SFAN: + len += sprintf(p + len, " ( is 0-7)\n"); + break; + + default: + len += sprintf(p + len, " ( is 0-7, " + "auto, disengaged)\n"); + break; + } + } if (fan_control_commands & IBMACPI_FAN_CMD_ENABLE) len += sprintf(p + len, "commands:\tenable, disable\n"); @@ -1973,6 +1987,17 @@ static int fan_set_level(int level) return -EINVAL; break; + case IBMACPI_FAN_WR_ACPI_FANS: + case IBMACPI_FAN_WR_TPEC: + if ((level != IBMACPI_FAN_EC_AUTO) && + (level != IBMACPI_FAN_EC_DISENGAGED) && + ((level < 0) || (level > 7))) + return -EINVAL; + + if (!acpi_ec_write(fan_status_offset, level)) + return -EIO; + break; + default: return -ENXIO; } @@ -2060,7 +2085,11 @@ static int fan_write_cmd_level(const char *cmd, int *rc) { int level; - if (sscanf(cmd, "level %d", &level) != 1) + if (strlencmp(cmd, "level auto") == 0) + level = IBMACPI_FAN_EC_AUTO; + else if (strlencmp(cmd, "level disengaged") == 0) + level = IBMACPI_FAN_EC_DISENGAGED; + else if (sscanf(cmd, "level %d", &level) != 1) return 0; if ((*rc = fan_set_level(level)) == -ENXIO) -- cgit v0.10.2 From 49a13cd6a2acd284ee106eaea7eeea8f2cc6796a Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 24 Nov 2006 11:47:13 -0200 Subject: ACPI: ibm-acpi: store embedded controller firmware version for matching This patch changes the ThinkPad Embedded Controller DMI matching code to store the firmware version of the EC for later usage, e.g. for quirks. It also prints the firmware version when starting up. Signed-off-by: Henrique de Moraes Holschuh diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index 4001ad1..3c091c4 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -77,6 +77,7 @@ #include #include #include +#include #include #include #include @@ -361,7 +362,7 @@ enum { /* Fan control constants */ * control */ }; -static int ibm_thinkpad_ec_found; +static char* ibm_thinkpad_ec_found = NULL; struct ibm_struct { char *name; @@ -2540,11 +2541,15 @@ static void acpi_ibm_exit(void) ibm_exit(&ibms[i]); remove_proc_entry(IBM_DIR, acpi_root_dir); + + if (ibm_thinkpad_ec_found) + kfree(ibm_thinkpad_ec_found); } -static int __init check_dmi_for_ec(void) +static char* __init check_dmi_for_ec(void) { struct dmi_device *dev = NULL; + char ec_fw_string[18]; /* * ThinkPad T23 or newer, A31 or newer, R50e or newer, @@ -2554,10 +2559,15 @@ static int __init check_dmi_for_ec(void) * See http://thinkwiki.org/wiki/List_of_DMI_IDs */ while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) { - if (strstr(dev->name, "IBM ThinkPad Embedded Controller")) - return 1; + if (sscanf(dev->name, + "IBM ThinkPad Embedded Controller -[%17c", + ec_fw_string) == 1) { + ec_fw_string[sizeof(ec_fw_string) - 1] = 0; + ec_fw_string[strcspn(ec_fw_string, " ]")] = 0; + return kstrdup(ec_fw_string, GFP_KERNEL); + } } - return 0; + return NULL; } static int __init acpi_ibm_init(void) @@ -2581,6 +2591,9 @@ static int __init acpi_ibm_init(void) /* Models with newer firmware report the EC in DMI */ ibm_thinkpad_ec_found = check_dmi_for_ec(); + if (ibm_thinkpad_ec_found) + printk(IBM_INFO "ThinkPad EC firmware %s\n", + ibm_thinkpad_ec_found); /* these handles are not required */ IBM_HANDLE_INIT(vid); -- cgit v0.10.2 From 778b4d742b210b9cac31f223527f30f1fc70312b Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 24 Nov 2006 11:47:14 -0200 Subject: ACPI: ibm-acpi: workaround for EC 0x2f initialization bug A few ThinkPads fail to initialize EC register 0x2f both in the EC firmware and ACPI DSDT. If the BIOS and the ACPI DSDT also do not initialize it, then the initial status of that register does not correspond to reality. On all reported buggy machines, EC 0x2f will read 0x07 (fan level 7) upon cold boot, when the EC is actually in mode 0x80 (auto mode). Since returning a text string ("unknown") would break a number of userspace programs, instead we correct the reading for the most probably correct answer, and return it is in auto mode. The workaround flags the status and level as unknown on module load/kernel boot, until we are certain at least one fan control command was issued, either by us, or by something else. We don't work around the bug by doing a "fan enable" at module load/startup (which would initialize the EC register) because it is not known if these ThinkPad ACPI DSDT might have set the fan to level 7 instead of "auto" (we don't know if they can do this or not) due to a thermal condition, and we don't want to override that, should they be capable of it. We should be setting the workaround flag to "status known" upon resume, as both reports and a exaustive search on the DSDT tables at acpi.sf.net show that the DSDTs always enable the fan on resume, thus working around the bug. But since we don't have suspend/resume handlers in ibm-acpi yet and the "EC register 0x2f was modified" logic is likely to catch the change anyway, we don't. Signed-off-by: Henrique de Moraes Holschuh diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index 3c091c4..56743c5 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -362,7 +362,7 @@ enum { /* Fan control constants */ * control */ }; -static char* ibm_thinkpad_ec_found = NULL; +static char *ibm_thinkpad_ec_found = NULL; struct ibm_struct { char *name; @@ -1794,13 +1794,15 @@ static enum fan_status_access_mode fan_status_access_mode; static enum fan_control_access_mode fan_control_access_mode; static enum fan_control_commands fan_control_commands; +static int fan_control_status_known; +static u8 fan_control_initial_status; + static int fan_init(void) { - u8 status; - fan_status_access_mode = IBMACPI_FAN_NONE; fan_control_access_mode = IBMACPI_FAN_WR_NONE; fan_control_commands = 0; + fan_control_status_known = 1; if (gfan_handle) { /* 570, 600e/x, 770e, 770x */ @@ -1808,8 +1810,33 @@ static int fan_init(void) } else { /* all other ThinkPads: note that even old-style * ThinkPad ECs supports the fan control register */ - if (likely(acpi_ec_read(fan_status_offset, &status))) { + if (likely(acpi_ec_read(fan_status_offset, + &fan_control_initial_status))) { fan_status_access_mode = IBMACPI_FAN_RD_TPEC; + + /* In some ThinkPads, neither the EC nor the ACPI + * DSDT initialize the fan status, and it ends up + * being set to 0x07 when it *could* be either + * 0x07 or 0x80. + * + * Enable for TP-1Y (T43), TP-78 (R51e), + * TP-76 (R52), TP-70 (T43, R52), which are known + * to be buggy. */ + if (fan_control_initial_status == 0x07 && + ibm_thinkpad_ec_found && + ((ibm_thinkpad_ec_found[0] == '1' && + ibm_thinkpad_ec_found[1] == 'Y') || + (ibm_thinkpad_ec_found[0] == '7' && + (ibm_thinkpad_ec_found[1] == '6' || + ibm_thinkpad_ec_found[1] == '8' || + ibm_thinkpad_ec_found[1] == '0')) + )) { + printk(IBM_NOTICE + "fan_init: initial fan status is " + "unknown, assuming it is in auto " + "mode\n"); + fan_control_status_known = 0; + } } else { printk(IBM_ERR "ThinkPad ACPI EC access misbehaving, " @@ -1930,9 +1957,21 @@ static int fan_read(char *p) if ((rc = fan_get_status(&status)) < 0) return rc; + if (unlikely(!fan_control_status_known)) { + if (status != fan_control_initial_status) + fan_control_status_known = 1; + else + /* Return most likely status. In fact, it + * might be the only possible status */ + status = IBMACPI_FAN_EC_AUTO; + } + len += sprintf(p + len, "status:\t\t%s\n", (status != 0) ? "enabled" : "disabled"); + /* No ThinkPad boots on disengaged mode, we can safely + * assume the tachometer is online if fan control status + * was unknown */ if ((rc = fan_get_speed(&speed)) < 0) return rc; @@ -1997,6 +2036,8 @@ static int fan_set_level(int level) if (!acpi_ec_write(fan_status_offset, level)) return -EIO; + else + fan_control_status_known = 1; break; default: @@ -2022,6 +2063,8 @@ static int fan_set_enable(void) if (!acpi_ec_write(fan_status_offset, s)) return -EIO; + else + fan_control_status_known = 1; break; case IBMACPI_FAN_WR_ACPI_SFAN: @@ -2051,6 +2094,8 @@ static int fan_set_disable(void) case IBMACPI_FAN_WR_TPEC: if (!acpi_ec_write(fan_status_offset, 0x00)) return -EIO; + else + fan_control_status_known = 1; break; case IBMACPI_FAN_WR_ACPI_SFAN: -- cgit v0.10.2 From 16663a87ad1df7022661bc8813b7a2e84e7f5e66 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 24 Nov 2006 11:47:14 -0200 Subject: ACPI: ibm-acpi: implement fan watchdog command This patch implements a fan control safety watchdog, by request of the authors of userspace fan control scripts. When the watchdog timer expires, the equivalent action of a "fan enable" command is executed. The watchdog timer is reset at every reception of a fan control command that could change the state of the fan itself. This command is meant to be used by userspace fan control daemons, to make sure the fan is never left set to an unsafe level because of userspace problems. Users of the X31/X40/X41 "speed" command are on their own, the current implementation of "speed" is just too incomplete to be used safely, anyway. Better to never use it, and just use the "level" command instead. The watchdog is programmed using echo "watchdog " > fan, where number is the number of seconds to wait before doing an "enable", and zero disables the watchdog. Signed-off-by: Henrique de Moraes Holschuh diff --git a/Documentation/ibm-acpi.txt b/Documentation/ibm-acpi.txt index cbd3a60..0132d36 100644 --- a/Documentation/ibm-acpi.txt +++ b/Documentation/ibm-acpi.txt @@ -670,6 +670,26 @@ example: modprobe ibm_acpi hotkey=enable,0xffff video=auto_disable +The ibm-acpi kernel driver can be programmed to revert the fan level +to a safe setting if userspace does not issue one of the fan commands: +"enable", "disable", "level" or "watchdog" within a configurable +ammount of time. To do this, use the "watchdog" command. + + echo 'watchdog ' > /proc/acpi/ibm/fan + +Interval is the ammount of time in seconds to wait for one of the +above mentioned fan commands before reseting the fan level to a safe +one. If set to zero, the watchdog is disabled (default). When the +watchdog timer runs out, it does the exact equivalent of the "enable" +fan command. + +Note that the watchdog timer stops after it enables the fan. It will +be rearmed again automatically (using the same interval) when one of +the above mentioned fan commands is received. The fan watchdog is, +therefore, not suitable to protect against fan mode changes made +through means other than the "enable", "disable", and "level" fan +commands. + Example Configuration --------------------- diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index 56743c5..e5b8745 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -82,6 +82,8 @@ #include #include #include +#include +#include #include #include @@ -348,7 +350,8 @@ enum fan_control_access_mode { enum fan_control_commands { IBMACPI_FAN_CMD_SPEED = 0x0001, /* speed command */ IBMACPI_FAN_CMD_LEVEL = 0x0002, /* level command */ - IBMACPI_FAN_CMD_ENABLE = 0x0004, /* enable/disable cmd */ + IBMACPI_FAN_CMD_ENABLE = 0x0004, /* enable/disable cmd, + * and also watchdog cmd */ }; enum { /* Fan control constants */ @@ -1797,12 +1800,17 @@ static enum fan_control_commands fan_control_commands; static int fan_control_status_known; static u8 fan_control_initial_status; +static void fan_watchdog_fire(void *ignored); +static int fan_watchdog_maxinterval; +static DECLARE_WORK(fan_watchdog_task, fan_watchdog_fire, NULL); + static int fan_init(void) { fan_status_access_mode = IBMACPI_FAN_NONE; fan_control_access_mode = IBMACPI_FAN_WR_NONE; fan_control_commands = 0; fan_control_status_known = 1; + fan_watchdog_maxinterval = 0; if (gfan_handle) { /* 570, 600e/x, 770e, 770x */ @@ -1934,6 +1942,31 @@ static int fan_get_speed(unsigned int *speed) return 0; } +static void fan_exit(void) +{ + cancel_delayed_work(&fan_watchdog_task); + flush_scheduled_work(); +} + +static void fan_watchdog_reset(void) +{ + static int fan_watchdog_active = 0; + + if (fan_watchdog_active) + cancel_delayed_work(&fan_watchdog_task); + + if (fan_watchdog_maxinterval > 0) { + fan_watchdog_active = 1; + if (!schedule_delayed_work(&fan_watchdog_task, + msecs_to_jiffies(fan_watchdog_maxinterval + * 1000))) { + printk(IBM_ERR "failed to schedule the fan watchdog, " + "watchdog will not trigger\n"); + } + } else + fan_watchdog_active = 0; +} + static int fan_read(char *p) { int len = 0; @@ -2007,7 +2040,9 @@ static int fan_read(char *p) } if (fan_control_commands & IBMACPI_FAN_CMD_ENABLE) - len += sprintf(p + len, "commands:\tenable, disable\n"); + len += sprintf(p + len, "commands:\tenable, disable\n" + "commands:\twatchdog ( is 0 (off), " + "1-120 (seconds))\n"); if (fan_control_commands & IBMACPI_FAN_CMD_SPEED) len += sprintf(p + len, "commands:\tspeed " @@ -2186,6 +2221,21 @@ static int fan_write_cmd_speed(const char *cmd, int *rc) return 1; } +static int fan_write_cmd_watchdog(const char *cmd, int *rc) +{ + int interval; + + if (sscanf(cmd, "watchdog %d", &interval) != 1) + return 0; + + if (interval < 0 || interval > 120) + *rc = -EINVAL; + else + fan_watchdog_maxinterval = interval; + + return 1; +} + static int fan_write(char *buf) { char *cmd; @@ -2196,16 +2246,29 @@ static int fan_write(char *buf) fan_write_cmd_level(cmd, &rc)) && !((fan_control_commands & IBMACPI_FAN_CMD_ENABLE) && (fan_write_cmd_enable(cmd, &rc) || - fan_write_cmd_disable(cmd, &rc))) && + fan_write_cmd_disable(cmd, &rc) || + fan_write_cmd_watchdog(cmd, &rc))) && !((fan_control_commands & IBMACPI_FAN_CMD_SPEED) && fan_write_cmd_speed(cmd, &rc)) ) rc = -EINVAL; + else if (!rc) + fan_watchdog_reset(); } return rc; } +static void fan_watchdog_fire(void *ignored) +{ + printk(IBM_NOTICE "fan watchdog: enabling fan\n"); + if (fan_set_enable()) { + printk(IBM_ERR "fan watchdog: error while enabling fan\n"); + /* reschedule for later */ + fan_watchdog_reset(); + } +} + static struct ibm_struct ibms[] = { { .name = "driver", @@ -2317,6 +2380,7 @@ static struct ibm_struct ibms[] = { .read = fan_read, .write = fan_write, .init = fan_init, + .exit = fan_exit, .experimental = 1, }, }; -- cgit v0.10.2 From e0298997acdba929e7f5b5987d305b67b50a3969 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sat, 25 Nov 2006 16:35:09 -0200 Subject: ACPI: ibm-acpi: add support for the ultrabay on the T60,X60 This patch adds support for the ultrabay on the T60, X60 and other new ThinkPads that have a SATA ultrabay. I intend to keep bay and dock support in ibm-acpi working and updated until it finally gets deprecated and removed in favour of the generic dock and bay support. But we aren't there yet. Signed-off-by: Henrique de Moraes Holschuh diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index e5b8745..392abbb 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -174,6 +174,7 @@ IBM_HANDLE(dock, root, "\\_SB.GDCK", /* X30, X31, X40 */ #endif IBM_HANDLE(bay, root, "\\_SB.PCI.IDE.SECN.MAST", /* 570 */ "\\_SB.PCI0.IDE0.IDES.IDSM", /* 600e/x, 770e, 770x */ + "\\_SB.PCI0.SATA.SCND.MSTR", /* T60, X60, Z60 */ "\\_SB.PCI0.IDE0.SCND.MSTR", /* all others */ ); /* A21e, R30, R31 */ -- cgit v0.10.2 From 2df910b4c3edcce9a0c12394db6f5f4a6e69c712 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sat, 25 Nov 2006 16:35:09 -0200 Subject: ACPI: ibm-acpi: make non-generic bay support optional This patch makes it possible to disable ibm-acpi non-generic bay support, as generic bay support already works well for a number of ThinkPads. Signed-off-by: Henrique de Moraes Holschuh diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 59f9def..6f8c50e 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -224,6 +224,17 @@ config ACPI_IBM_DOCK If you are not sure, say N here. +config ACPI_IBM_BAY + bool "Legacy Removable Bay Support" + depends on ACPI_IBM + depends on ACPI_BAY=n + default n + ---help--- + Allows the ibm_acpi driver to handle removable bays. + This support is obsoleted by CONFIG_ACPI_BAY. + + If you are not sure, say N here. + config ACPI_TOSHIBA tristate "Toshiba Laptop Extras" depends on X86 diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index 392abbb..fbb4970 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -172,6 +172,7 @@ IBM_HANDLE(dock, root, "\\_SB.GDCK", /* X30, X31, X40 */ "\\_SB.PCI.ISA.SLCE", /* 570 */ ); /* A21e,G4x,R30,R31,R32,R40,R40e,R50e */ #endif +#ifdef CONFIG_ACPI_IBM_BAY IBM_HANDLE(bay, root, "\\_SB.PCI.IDE.SECN.MAST", /* 570 */ "\\_SB.PCI0.IDE0.IDES.IDSM", /* 600e/x, 770e, 770x */ "\\_SB.PCI0.SATA.SCND.MSTR", /* T60, X60, Z60 */ @@ -189,6 +190,7 @@ IBM_HANDLE(bay2, root, "\\_SB.PCI0.IDE0.PRIM.SLAV", /* A3x, R32 */ IBM_HANDLE(bay2_ej, bay2, "_EJ3", /* 600e/x, 770e, A3x */ "_EJ0", /* 770x */ ); /* all others */ +#endif /* don't list other alternatives as we install a notify handler on the 570 */ IBM_HANDLE(pci, root, "\\_SB.PCI"); /* 570 */ @@ -1051,6 +1053,7 @@ static int light_write(char *buf) return 0; } +#if defined(CONFIG_ACPI_IBM_DOCK) || defined(CONFIG_ACPI_IBM_BAY) static int _sta(acpi_handle handle) { int status; @@ -1060,7 +1063,7 @@ static int _sta(acpi_handle handle) return status; } - +#endif #ifdef CONFIG_ACPI_IBM_DOCK #define dock_docked() (_sta(dock_handle) & 1) @@ -1126,6 +1129,7 @@ static void dock_notify(struct ibm_struct *ibm, u32 event) } #endif +#ifdef CONFIG_ACPI_IBM_BAY static int bay_status_supported; static int bay_status2_supported; static int bay_eject_supported; @@ -1201,6 +1205,7 @@ static void bay_notify(struct ibm_struct *ibm, u32 event) { acpi_bus_generate_event(ibm->device, event, 0); } +#endif static int cmos_read(char *p) { @@ -2330,6 +2335,7 @@ static struct ibm_struct ibms[] = { .type = ACPI_SYSTEM_NOTIFY, }, #endif +#ifdef CONFIG_ACPI_IBM_BAY { .name = "bay", .init = bay_init, @@ -2339,6 +2345,7 @@ static struct ibm_struct ibms[] = { .handle = &bay_handle, .type = ACPI_SYSTEM_NOTIFY, }, +#endif { .name = "cmos", .read = cmos_read, @@ -2624,7 +2631,9 @@ IBM_PARAM(light); #ifdef CONFIG_ACPI_IBM_DOCK IBM_PARAM(dock); #endif +#ifdef CONFIG_ACPI_IBM_BAY IBM_PARAM(bay); +#endif IBM_PARAM(cmos); IBM_PARAM(led); IBM_PARAM(beep); @@ -2717,12 +2726,14 @@ static int __init acpi_ibm_init(void) IBM_HANDLE_INIT(dock); #endif IBM_HANDLE_INIT(pci); +#ifdef CONFIG_ACPI_IBM_BAY IBM_HANDLE_INIT(bay); if (bay_handle) IBM_HANDLE_INIT(bay_ej); IBM_HANDLE_INIT(bay2); if (bay2_handle) IBM_HANDLE_INIT(bay2_ej); +#endif IBM_HANDLE_INIT(beep); IBM_HANDLE_INIT(ecrd); IBM_HANDLE_INIT(ecwr); -- cgit v0.10.2 From fb87a811a4c232e2af8d746dc75330cbe5b0780c Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sat, 25 Nov 2006 16:35:09 -0200 Subject: ACPI: ibm-acpi: backlight device cleanup This patch cleans up the recently added backlight device support by Holger Macht to fit well with the rest of the code, using the ibms struct as the other "subdrivers" in ibm-acpi. Signed-off-by: Henrique de Moraes Holschuh diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index fbb4970..36e2667 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -397,7 +397,7 @@ struct ibm_struct { static struct proc_dir_entry *proc_dir = NULL; -static struct backlight_device *ibm_backlight_device; +static struct backlight_device *ibm_backlight_device = NULL; #define onoff(status,bit) ((status) & (1 << (bit)) ? "on" : "off") #define enabled(status,bit) ((status) & (1 << (bit)) ? "enabled" : "disabled") @@ -1639,6 +1639,7 @@ static int brightness_get(struct backlight_device *bd) return -EIO; level &= 0x7; + return level; } @@ -1713,6 +1714,33 @@ static int brightness_update_status(struct backlight_device *bd) return brightness_set(bd->props->brightness); } +static struct backlight_properties ibm_backlight_data = { + .owner = THIS_MODULE, + .get_brightness = brightness_get, + .update_status = brightness_update_status, + .max_brightness = 7, +}; + +static int brightness_init(void) +{ + ibm_backlight_device = backlight_device_register("ibm", NULL, + &ibm_backlight_data); + if (IS_ERR(ibm_backlight_device)) { + printk(IBM_ERR "Could not register backlight device\n"); + return PTR_ERR(ibm_backlight_device); + } + + return 0; +} + +static void brightness_exit(void) +{ + if (ibm_backlight_device) { + backlight_device_unregister(ibm_backlight_device); + ibm_backlight_device = NULL; + } +} + static int volume_offset = 0x30; static int volume_read(char *p) @@ -2377,6 +2405,8 @@ static struct ibm_struct ibms[] = { .name = "brightness", .read = brightness_read, .write = brightness_write, + .init = brightness_init, + .exit = brightness_exit, }, { .name = "volume", @@ -2642,20 +2672,10 @@ IBM_PARAM(brightness); IBM_PARAM(volume); IBM_PARAM(fan); -static struct backlight_properties ibm_backlight_data = { - .owner = THIS_MODULE, - .get_brightness = brightness_get, - .update_status = brightness_update_status, - .max_brightness = 7, -}; - static void acpi_ibm_exit(void) { int i; - if (ibm_backlight_device) - backlight_device_unregister(ibm_backlight_device); - for (i = ARRAY_SIZE(ibms) - 1; i >= 0; i--) ibm_exit(&ibms[i]); @@ -2758,14 +2778,6 @@ static int __init acpi_ibm_init(void) } } - ibm_backlight_device = backlight_device_register("ibm", NULL, - &ibm_backlight_data); - if (IS_ERR(ibm_backlight_device)) { - printk(IBM_ERR "Could not register ibm backlight device\n"); - ibm_backlight_device = NULL; - acpi_ibm_exit(); - } - return 0; } -- cgit v0.10.2 From 9a8e1738c1136a857c1fd3ae0c5019f9767427ad Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sat, 25 Nov 2006 16:36:00 -0200 Subject: ACPI: ibm-acpi: style fixes and cruft removal This patch just fixes style, move some #defines to enums, and removes some old cruft. Signed-off-by: Henrique de Moraes Holschuh diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index 36e2667..8bb41bd 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -78,9 +78,11 @@ #include #include #include + #include #include #include + #include #include #include @@ -121,28 +123,6 @@ static acpi_handle root_handle = NULL; static char *object##_path; \ static char *object##_paths[] = { paths } -/* - * The following models are supported to various degrees: - * - * 570, 600e, 600x, 770e, 770x - * A20m, A21e, A21m, A21p, A22p, A30, A30p, A31, A31p - * G40, G41 - * R30, R31, R32, R40, R40e, R50, R50e, R50p, R51 - * T20, T21, T22, T23, T30, T40, T40p, T41, T41p, T42, T42p, T43 - * X20, X21, X22, X23, X24, X30, X31, X40 - * - * The following models have no supported features: - * - * 240, 240x, i1400 - * - * Still missing DSDTs for the following models: - * - * A20p, A22e, A22m - * R52 - * S31 - * T43p - */ - IBM_HANDLE(ec, root, "\\_SB.PCI0.ISA.EC0", /* 240, 240x */ "\\_SB.PCI.ISA.EC", /* 570 */ "\\_SB.PCI0.ISA0.EC0", /* 600e/x, 770e, 770x */ @@ -785,12 +765,15 @@ static int wan_write(char *buf) return 0; } -static int video_supported; -static int video_orig_autosw; +enum video_access_mode { + IBMACPI_VIDEO_NONE = 0, + IBMACPI_VIDEO_570, /* 570 */ + IBMACPI_VIDEO_770, /* 600e/x, 770e, 770x */ + IBMACPI_VIDEO_NEW, /* all others */ +}; -#define VIDEO_570 1 -#define VIDEO_770 2 -#define VIDEO_NEW 3 +static enum video_access_mode video_supported; +static int video_orig_autosw; static int video_init(void) { @@ -802,16 +785,16 @@ static int video_init(void) if (!vid_handle) /* video switching not supported on R30, R31 */ - video_supported = 0; + video_supported = IBMACPI_VIDEO_NONE; else if (acpi_evalf(vid_handle, &video_orig_autosw, "SWIT", "qd")) /* 570 */ - video_supported = VIDEO_570; + video_supported = IBMACPI_VIDEO_570; else if (acpi_evalf(vid_handle, &video_orig_autosw, "^VADL", "qd")) /* 600e/x, 770e, 770x */ - video_supported = VIDEO_770; + video_supported = IBMACPI_VIDEO_770; else /* all others */ - video_supported = VIDEO_NEW; + video_supported = IBMACPI_VIDEO_NEW; return 0; } @@ -821,15 +804,15 @@ static int video_status(void) int status = 0; int i; - if (video_supported == VIDEO_570) { + if (video_supported == IBMACPI_VIDEO_570) { if (acpi_evalf(NULL, &i, "\\_SB.PHS", "dd", 0x87)) status = i & 3; - } else if (video_supported == VIDEO_770) { + } else if (video_supported == IBMACPI_VIDEO_770) { if (acpi_evalf(NULL, &i, "\\VCDL", "d")) status |= 0x01 * i; if (acpi_evalf(NULL, &i, "\\VCDC", "d")) status |= 0x02 * i; - } else if (video_supported == VIDEO_NEW) { + } else if (video_supported == IBMACPI_VIDEO_NEW) { acpi_evalf(NULL, NULL, "\\VUPS", "vd", 1); if (acpi_evalf(NULL, &i, "\\VCDC", "d")) status |= 0x02 * i; @@ -848,9 +831,10 @@ static int video_autosw(void) { int autosw = 0; - if (video_supported == VIDEO_570) + if (video_supported == IBMACPI_VIDEO_570) acpi_evalf(vid_handle, &autosw, "SWIT", "d"); - else if (video_supported == VIDEO_770 || video_supported == VIDEO_NEW) + else if (video_supported == IBMACPI_VIDEO_770 || + video_supported == IBMACPI_VIDEO_NEW) acpi_evalf(vid_handle, &autosw, "^VDEE", "d"); return autosw & 1; @@ -870,12 +854,12 @@ static int video_read(char *p) len += sprintf(p + len, "status:\t\tsupported\n"); len += sprintf(p + len, "lcd:\t\t%s\n", enabled(status, 0)); len += sprintf(p + len, "crt:\t\t%s\n", enabled(status, 1)); - if (video_supported == VIDEO_NEW) + if (video_supported == IBMACPI_VIDEO_NEW) len += sprintf(p + len, "dvi:\t\t%s\n", enabled(status, 3)); len += sprintf(p + len, "auto:\t\t%s\n", enabled(autosw, 0)); len += sprintf(p + len, "commands:\tlcd_enable, lcd_disable\n"); len += sprintf(p + len, "commands:\tcrt_enable, crt_disable\n"); - if (video_supported == VIDEO_NEW) + if (video_supported == IBMACPI_VIDEO_NEW) len += sprintf(p + len, "commands:\tdvi_enable, dvi_disable\n"); len += sprintf(p + len, "commands:\tauto_enable, auto_disable\n"); len += sprintf(p + len, "commands:\tvideo_switch, expand_toggle\n"); @@ -890,7 +874,7 @@ static int video_switch(void) if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1)) return -EIO; - ret = video_supported == VIDEO_570 ? + ret = video_supported == IBMACPI_VIDEO_570 ? acpi_evalf(ec_handle, NULL, "_Q16", "v") : acpi_evalf(vid_handle, NULL, "VSWT", "v"); acpi_evalf(vid_handle, NULL, "_DOS", "vd", autosw); @@ -900,9 +884,9 @@ static int video_switch(void) static int video_expand(void) { - if (video_supported == VIDEO_570) + if (video_supported == IBMACPI_VIDEO_570) return acpi_evalf(ec_handle, NULL, "_Q17", "v"); - else if (video_supported == VIDEO_770) + else if (video_supported == IBMACPI_VIDEO_770) return acpi_evalf(vid_handle, NULL, "VEXP", "v"); else return acpi_evalf(NULL, NULL, "\\VEXP", "v"); @@ -912,10 +896,10 @@ static int video_switch2(int status) { int ret; - if (video_supported == VIDEO_570) { + if (video_supported == IBMACPI_VIDEO_570) { ret = acpi_evalf(NULL, NULL, "\\_SB.PHS2", "vdd", 0x8b, status | 0x80); - } else if (video_supported == VIDEO_770) { + } else if (video_supported == IBMACPI_VIDEO_770) { int autosw = video_autosw(); if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1)) return -EIO; @@ -951,10 +935,10 @@ static int video_write(char *buf) enable |= 0x02; } else if (strlencmp(cmd, "crt_disable") == 0) { disable |= 0x02; - } else if (video_supported == VIDEO_NEW && + } else if (video_supported == IBMACPI_VIDEO_NEW && strlencmp(cmd, "dvi_enable") == 0) { enable |= 0x08; - } else if (video_supported == VIDEO_NEW && + } else if (video_supported == IBMACPI_VIDEO_NEW && strlencmp(cmd, "dvi_disable") == 0) { disable |= 0x08; } else if (strlencmp(cmd, "auto_enable") == 0) { @@ -1253,26 +1237,28 @@ static int cmos_write(char *buf) return 0; } -static int led_supported; - -#define LED_570 1 -#define LED_OLD 2 -#define LED_NEW 3 +enum led_access_mode { + IBMACPI_LED_NONE = 0, + IBMACPI_LED_570, /* 570 */ + IBMACPI_LED_OLD, /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */ + IBMACPI_LED_NEW, /* all others */ +}; +static enum led_access_mode led_supported; static int led_init(void) { if (!led_handle) /* led not supported on R30, R31 */ - led_supported = 0; + led_supported = IBMACPI_LED_NONE; else if (strlencmp(led_path, "SLED") == 0) /* 570 */ - led_supported = LED_570; + led_supported = IBMACPI_LED_570; else if (strlencmp(led_path, "SYSL") == 0) /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */ - led_supported = LED_OLD; + led_supported = IBMACPI_LED_OLD; else /* all others */ - led_supported = LED_NEW; + led_supported = IBMACPI_LED_NEW; return 0; } @@ -1289,7 +1275,7 @@ static int led_read(char *p) } len += sprintf(p + len, "status:\t\tsupported\n"); - if (led_supported == LED_570) { + if (led_supported == IBMACPI_LED_570) { /* 570 */ int i, status; for (i = 0; i < 8; i++) { @@ -1338,13 +1324,13 @@ static int led_write(char *buf) } else return -EINVAL; - if (led_supported == LED_570) { + if (led_supported == IBMACPI_LED_570) { /* 570 */ led = 1 << led; if (!acpi_evalf(led_handle, NULL, NULL, "vdd", led, led_sled_arg1[ind])) return -EIO; - } else if (led_supported == LED_OLD) { + } else if (led_supported == IBMACPI_LED_OLD) { /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */ led = 1 << led; ret = ec_write(EC_HLMS, led); -- cgit v0.10.2 From f9ff43a6268d36acf8df18a76bb881a26a42dc1e Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sat, 25 Nov 2006 16:37:38 -0200 Subject: ACPI: ibm-acpi: update version and copyright Bump up module version, add myself to copyright and MODULE_AUTHOR. Signed-off-by: Henrique de Moraes Holschuh diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index 8bb41bd..92e7b6e 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -3,6 +3,7 @@ * * * Copyright (C) 2004-2005 Borislav Deianov + * Copyright (C) 2006 Henrique de Moraes Holschuh * * 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 @@ -19,10 +20,14 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#define IBM_VERSION "0.12a" +#define IBM_VERSION "0.13" /* * Changelog: + * + * 2006-11-22 0.13 new maintainer + * changelog now lives in git commit history, and will + * not be updated further in-file. * * 2005-08-17 0.12 fix compilation on 2.6.13-rc kernels * 2005-03-17 0.11 support for 600e, 770x @@ -95,7 +100,7 @@ #define IBM_FILE "ibm_acpi" #define IBM_URL "http://ibm-acpi.sf.net/" -MODULE_AUTHOR("Borislav Deianov"); +MODULE_AUTHOR("Borislav Deianov, Henrique de Moraes Holschuh"); MODULE_DESCRIPTION(IBM_DESC); MODULE_VERSION(IBM_VERSION); MODULE_LICENSE("GPL"); -- cgit v0.10.2 From 671adbec210efc15cef81b4616adae8bcd667296 Mon Sep 17 00:00:00 2001 From: Kristen Carlson Accardi Date: Mon, 4 Dec 2006 14:49:43 -0800 Subject: ACPI: dock: Make the dock station driver a platform device driver. Make the dock station driver a platform device driver so that we can create sysfs entries under /sys/device/platform. Signed-off-by: Kristen Carlson Accardi Signed-off-by: Len Brown diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index bf5b79e..336d94c 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -39,6 +40,8 @@ MODULE_DESCRIPTION(ACPI_DOCK_DRIVER_NAME); MODULE_LICENSE("GPL"); static struct atomic_notifier_head dock_notifier_list; +static struct platform_device dock_device; +static char dock_device_name[] = "dock"; struct dock_station { acpi_handle handle; @@ -629,6 +632,15 @@ static int dock_add(acpi_handle handle) spin_lock_init(&dock_station->hp_lock); ATOMIC_INIT_NOTIFIER_HEAD(&dock_notifier_list); + /* initialize platform device stuff */ + dock_device.name = dock_device_name; + ret = platform_device_register(&dock_device); + if (ret) { + printk(KERN_ERR PREFIX "Error registering dock device\n", ret); + kfree(dock_station); + return ret; + } + /* Find dependent devices */ acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, find_dock_devices, dock_station, @@ -638,7 +650,8 @@ static int dock_add(acpi_handle handle) dd = alloc_dock_dependent_device(handle); if (!dd) { kfree(dock_station); - return -ENOMEM; + ret = -ENOMEM; + goto dock_add_err_unregister; } add_dock_dependent_device(dock_station, dd); @@ -658,8 +671,10 @@ static int dock_add(acpi_handle handle) return 0; dock_add_err: - kfree(dock_station); kfree(dd); +dock_add_err_unregister: + platform_device_unregister(&dock_device); + kfree(dock_station); return ret; } @@ -686,6 +701,9 @@ static int dock_remove(void) if (ACPI_FAILURE(status)) printk(KERN_ERR "Error removing notify handler\n"); + /* cleanup sysfs */ + platform_device_unregister(&dock_device); + /* free dock station memory */ kfree(dock_station); return 0; -- cgit v0.10.2 From e67beb37df7a9da9d5d1e59c5358654d007a97c5 Mon Sep 17 00:00:00 2001 From: Len Brown Date: Thu, 7 Dec 2006 04:17:35 -0500 Subject: ACPI: dock: fix build warning drivers/acpi/dock.c:689: warning: too many arguments for format Signed-off-by: Len Brown diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 336d94c..2c2f28d 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -636,7 +636,7 @@ static int dock_add(acpi_handle handle) dock_device.name = dock_device_name; ret = platform_device_register(&dock_device); if (ret) { - printk(KERN_ERR PREFIX "Error registering dock device\n", ret); + printk(KERN_ERR PREFIX "Error %d registering dock device\n", ret); kfree(dock_station); return ret; } -- cgit v0.10.2 From c80fdbe81a617c82e2f95233f8ddcf046ffe21b3 Mon Sep 17 00:00:00 2001 From: "brandon@ifup.org" Date: Mon, 4 Dec 2006 14:49:58 -0800 Subject: ACPI: dock: Add a docked sysfs file to the dock driver. Add 2 sysfs files for user interface. 1) docked - 1/0 (read only) - indicates whether the software believes the laptop is docked in a docking station. 2) undock - (write only) - writing to this file causes the software to initiate an undock request to the firmware. Signed-off-by: Brandon Philips Signed-off-by: Kristen Carlson Accardi Signed-off-by: Len Brown diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 2c2f28d..e4c1a4f 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -515,6 +515,37 @@ void unregister_hotplug_dock_device(acpi_handle handle) EXPORT_SYMBOL_GPL(unregister_hotplug_dock_device); /** + * handle_eject_request - handle an undock request checking for error conditions + * + * Check to make sure the dock device is still present, then undock and + * hotremove all the devices that may need removing. + */ +static int handle_eject_request(struct dock_station *ds, u32 event) +{ + if (!dock_present(ds)) + return -ENODEV; + + if (dock_in_progress(ds)) + return -EBUSY; + + /* + * here we need to generate the undock + * event prior to actually doing the undock + * so that the device struct still exists. + */ + dock_event(ds, event, UNDOCK_EVENT); + hotplug_dock_devices(ds, ACPI_NOTIFY_EJECT_REQUEST); + undock(ds); + eject_dock(ds); + if (dock_present(ds)) { + printk(KERN_ERR PREFIX "Unable to undock!\n"); + return -EBUSY; + } + + return 0; +} + +/** * dock_notify - act upon an acpi dock notification * @handle: the dock station handle * @event: the acpi event @@ -522,9 +553,7 @@ EXPORT_SYMBOL_GPL(unregister_hotplug_dock_device); * * If we are notified to dock, then check to see if the dock is * present and then dock. Notify all drivers of the dock event, - * and then hotplug and devices that may need hotplugging. For undock - * check to make sure the dock device is still present, then undock - * and hotremove all the devices that may need removing. + * and then hotplug and devices that may need hotplugging. */ static void dock_notify(acpi_handle handle, u32 event, void *data) { @@ -556,19 +585,7 @@ static void dock_notify(acpi_handle handle, u32 event, void *data) * to the driver who wish to hotplug. */ case ACPI_NOTIFY_EJECT_REQUEST: - if (!dock_in_progress(ds) && dock_present(ds)) { - /* - * here we need to generate the undock - * event prior to actually doing the undock - * so that the device struct still exists. - */ - dock_event(ds, event, UNDOCK_EVENT); - hotplug_dock_devices(ds, ACPI_NOTIFY_EJECT_REQUEST); - undock(ds); - eject_dock(ds); - if (dock_present(ds)) - printk(KERN_ERR PREFIX "Unable to undock!\n"); - } + handle_eject_request(ds, event); break; default: printk(KERN_ERR PREFIX "Unknown dock event %d\n", event); @@ -607,6 +624,33 @@ find_dock_devices(acpi_handle handle, u32 lvl, void *context, void **rv) return AE_OK; } +/* + * show_docked - read method for "docked" file in sysfs + */ +static ssize_t show_docked(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", dock_present(dock_station)); + +} +DEVICE_ATTR(docked, S_IRUGO, show_docked, NULL); + +/* + * write_undock - write method for "undock" file in sysfs + */ +static ssize_t write_undock(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + + if (!count) + return -EINVAL; + + ret = handle_eject_request(dock_station, ACPI_NOTIFY_EJECT_REQUEST); + return ret ? ret: count; +} +DEVICE_ATTR(undock, S_IWUSR, NULL, write_undock); + /** * dock_add - add a new dock station * @handle: the dock station handle @@ -640,6 +684,21 @@ static int dock_add(acpi_handle handle) kfree(dock_station); return ret; } + ret = device_create_file(&dock_device.dev, &dev_attr_docked); + if (ret) { + printk("Error %d adding sysfs file\n", ret); + platform_device_unregister(&dock_device); + kfree(dock_station); + return ret; + } + ret = device_create_file(&dock_device.dev, &dev_attr_undock); + if (ret) { + printk("Error %d adding sysfs file\n", ret); + device_remove_file(&dock_device.dev, &dev_attr_docked); + platform_device_unregister(&dock_device); + kfree(dock_station); + return ret; + } /* Find dependent devices */ acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, @@ -673,6 +732,8 @@ static int dock_add(acpi_handle handle) dock_add_err: kfree(dd); dock_add_err_unregister: + device_remove_file(&dock_device.dev, &dev_attr_docked); + device_remove_file(&dock_device.dev, &dev_attr_undock); platform_device_unregister(&dock_device); kfree(dock_station); return ret; @@ -702,6 +763,8 @@ static int dock_remove(void) printk(KERN_ERR "Error removing notify handler\n"); /* cleanup sysfs */ + device_remove_file(&dock_device.dev, &dev_attr_docked); + device_remove_file(&dock_device.dev, &dev_attr_undock); platform_device_unregister(&dock_device); /* free dock station memory */ -- cgit v0.10.2 From 2548c06b72396e28abdb5dd572ab589c3c22f4b9 Mon Sep 17 00:00:00 2001 From: Prarit Bhargava Date: Mon, 4 Dec 2006 14:50:17 -0800 Subject: ACPI: dock: Fix symbol conflict between acpiphp and dock Fix bug which will cause acpiphp to not be able to load when dock.ko cannot load. Signed-off-by: Prarit Bhargava Signed-off-by: Kristen Carlson Accardi Signed-off-by: Len Brown diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index e4c1a4f..8c6828b 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -444,6 +444,9 @@ static int dock_in_progress(struct dock_station *ds) */ int register_dock_notifier(struct notifier_block *nb) { + if (!dock_station) + return -ENODEV; + return atomic_notifier_chain_register(&dock_notifier_list, nb); } @@ -455,6 +458,9 @@ EXPORT_SYMBOL_GPL(register_dock_notifier); */ void unregister_dock_notifier(struct notifier_block *nb) { + if (!dock_station) + return; + atomic_notifier_chain_unregister(&dock_notifier_list, nb); } @@ -807,7 +813,7 @@ static int __init dock_init(void) ACPI_UINT32_MAX, find_dock, &num, NULL); if (!num) - return -ENODEV; + printk(KERN_INFO "No dock devices found.\n"); return 0; } -- cgit v0.10.2 From bec4f749cb75d30cebf9837985a06474f595f3ae Mon Sep 17 00:00:00 2001 From: Thomas Koeller Date: Wed, 6 Dec 2006 01:45:26 +0100 Subject: [WATCHDOG] rm9k_wdt: fix compilation Driver did not compile any more. Someone moved the definition of 'struct miscdevice miscdev' to a place near the end of the file, after some code that was refering to this variable. Signed-off-by: Thomas Koeller Signed-off-by: Wim Van Sebroeck diff --git a/drivers/char/watchdog/rm9k_wdt.c b/drivers/char/watchdog/rm9k_wdt.c index ec39093..bc7def4 100644 --- a/drivers/char/watchdog/rm9k_wdt.c +++ b/drivers/char/watchdog/rm9k_wdt.c @@ -94,6 +94,26 @@ module_param(nowayout, bool, 0444); MODULE_PARM_DESC(nowayout, "Watchdog cannot be disabled once started"); +/* Kernel interfaces */ +static struct file_operations fops = { + .owner = THIS_MODULE, + .open = wdt_gpi_open, + .release = wdt_gpi_release, + .write = wdt_gpi_write, + .unlocked_ioctl = wdt_gpi_ioctl, +}; + +static struct miscdevice miscdev = { + .minor = WATCHDOG_MINOR, + .name = wdt_gpi_name, + .fops = &fops, +}; + +static struct notifier_block wdt_gpi_shutdown = { + .notifier_call = wdt_gpi_notify, +}; + + /* Interrupt handler */ static irqreturn_t wdt_gpi_irqhdl(int irq, void *ctxt, struct pt_regs *regs) { @@ -312,26 +332,6 @@ wdt_gpi_notify(struct notifier_block *this, unsigned long code, void *unused) } -/* Kernel interfaces */ -static struct file_operations fops = { - .owner = THIS_MODULE, - .open = wdt_gpi_open, - .release = wdt_gpi_release, - .write = wdt_gpi_write, - .unlocked_ioctl = wdt_gpi_ioctl, -}; - -static struct miscdevice miscdev = { - .minor = WATCHDOG_MINOR, - .name = wdt_gpi_name, - .fops = &fops, -}; - -static struct notifier_block wdt_gpi_shutdown = { - .notifier_call = wdt_gpi_notify, -}; - - /* Init & exit procedures */ static const struct resource * wdt_gpi_get_resource(struct platform_device *pdv, const char *name, -- cgit v0.10.2 From 74e86ab884fb491c208905bdc32a9d95740be583 Mon Sep 17 00:00:00 2001 From: Thomas Koeller Date: Wed, 6 Dec 2006 01:45:39 +0100 Subject: [WATCHDOG] rm9k_wdt: fix interrupt handler arguments Removed 'struct pt_regs *' from interrupt handler arguments. Signed-off-by: Thomas Koeller Signed-off-by: Wim Van Sebroeck diff --git a/drivers/char/watchdog/rm9k_wdt.c b/drivers/char/watchdog/rm9k_wdt.c index bc7def4..7576a13 100644 --- a/drivers/char/watchdog/rm9k_wdt.c +++ b/drivers/char/watchdog/rm9k_wdt.c @@ -47,7 +47,7 @@ /* Function prototypes */ -static irqreturn_t wdt_gpi_irqhdl(int, void *, struct pt_regs *); +static irqreturn_t wdt_gpi_irqhdl(int, void *); static void wdt_gpi_start(void); static void wdt_gpi_stop(void); static void wdt_gpi_set_timeout(unsigned int); @@ -115,7 +115,7 @@ static struct notifier_block wdt_gpi_shutdown = { /* Interrupt handler */ -static irqreturn_t wdt_gpi_irqhdl(int irq, void *ctxt, struct pt_regs *regs) +static irqreturn_t wdt_gpi_irqhdl(int irq, void *ctxt) { if (!unlikely(__raw_readl(wd_regs + 0x0008) & 0x1)) return IRQ_NONE; -- cgit v0.10.2 From e0b79e0bc261ad320f6c218ac6823f7ca859171f Mon Sep 17 00:00:00 2001 From: Andrew Victor Date: Mon, 4 Dec 2006 15:56:18 +0200 Subject: [WATCHDOG] watchdog miscdevice patch It looks like the recent changes to 'struct miscdevice' have impacted some of the Watchdog drivers. at91rm9200_wdt.c:205: error: 'struct miscdevice' has no member named 'dev' For the AT91RM9200 driver I just replaced "miscdevice.dev" with "miscdevice.parent". The mpcore_wdt.c and omap_wdt.c seem similarly affected. Signed-off-by: Andrew Victor Signed-off-by: Wim Van Sebroeck diff --git a/drivers/char/watchdog/at91rm9200_wdt.c b/drivers/char/watchdog/at91rm9200_wdt.c index 4e7a114..c8da2b0 100644 --- a/drivers/char/watchdog/at91rm9200_wdt.c +++ b/drivers/char/watchdog/at91rm9200_wdt.c @@ -202,9 +202,9 @@ static int __init at91wdt_probe(struct platform_device *pdev) { int res; - if (at91wdt_miscdev.dev) + if (at91wdt_miscdev.parent) return -EBUSY; - at91wdt_miscdev.dev = &pdev->dev; + at91wdt_miscdev.parent = &pdev->dev; res = misc_register(&at91wdt_miscdev); if (res) @@ -220,7 +220,7 @@ static int __exit at91wdt_remove(struct platform_device *pdev) res = misc_deregister(&at91wdt_miscdev); if (!res) - at91wdt_miscdev.dev = NULL; + at91wdt_miscdev.parent = NULL; return res; } diff --git a/drivers/char/watchdog/mpcore_wdt.c b/drivers/char/watchdog/mpcore_wdt.c index 3404a9c..e88947f 100644 --- a/drivers/char/watchdog/mpcore_wdt.c +++ b/drivers/char/watchdog/mpcore_wdt.c @@ -347,7 +347,7 @@ static int __devinit mpcore_wdt_probe(struct platform_device *dev) goto err_free; } - mpcore_wdt_miscdev.dev = &dev->dev; + mpcore_wdt_miscdev.parent = &dev->dev; ret = misc_register(&mpcore_wdt_miscdev); if (ret) { dev_printk(KERN_ERR, _dev, "cannot register miscdev on minor=%d (err=%d)\n", diff --git a/drivers/char/watchdog/omap_wdt.c b/drivers/char/watchdog/omap_wdt.c index 5dbd7dc..6c6f973 100644 --- a/drivers/char/watchdog/omap_wdt.c +++ b/drivers/char/watchdog/omap_wdt.c @@ -290,7 +290,7 @@ static int __init omap_wdt_probe(struct platform_device *pdev) omap_wdt_disable(); omap_wdt_adjust_timeout(timer_margin); - omap_wdt_miscdev.dev = &pdev->dev; + omap_wdt_miscdev.parent = &pdev->dev; ret = misc_register(&omap_wdt_miscdev); if (ret) goto fail; -- cgit v0.10.2 From c271c5c22b0a7ca45fda15f1f4d258bca36a5b94 Mon Sep 17 00:00:00 2001 From: Sunil Mushran Date: Tue, 5 Dec 2006 17:56:35 -0800 Subject: ocfs2: local mounts This allows users to format an ocfs2 file system with a special flag, OCFS2_FEATURE_INCOMPAT_LOCAL_MOUNT. When the file system sees this flag, it will not use any cluster services, nor will it require a cluster configuration, thus acting like a 'local' file system. Signed-off-by: Sunil Mushran Signed-off-by: Mark Fasheh diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c index 69fba16..e622013 100644 --- a/fs/ocfs2/dlmglue.c +++ b/fs/ocfs2/dlmglue.c @@ -770,7 +770,7 @@ static int ocfs2_lock_create(struct ocfs2_super *osb, int dlm_flags) { int ret = 0; - enum dlm_status status; + enum dlm_status status = DLM_NORMAL; unsigned long flags; mlog_entry_void(); @@ -1138,6 +1138,7 @@ int ocfs2_rw_lock(struct inode *inode, int write) { int status, level; struct ocfs2_lock_res *lockres; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); BUG_ON(!inode); @@ -1147,6 +1148,9 @@ int ocfs2_rw_lock(struct inode *inode, int write) (unsigned long long)OCFS2_I(inode)->ip_blkno, write ? "EXMODE" : "PRMODE"); + if (ocfs2_mount_local(osb)) + return 0; + lockres = &OCFS2_I(inode)->ip_rw_lockres; level = write ? LKM_EXMODE : LKM_PRMODE; @@ -1164,6 +1168,7 @@ void ocfs2_rw_unlock(struct inode *inode, int write) { int level = write ? LKM_EXMODE : LKM_PRMODE; struct ocfs2_lock_res *lockres = &OCFS2_I(inode)->ip_rw_lockres; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); mlog_entry_void(); @@ -1171,7 +1176,8 @@ void ocfs2_rw_unlock(struct inode *inode, int write) (unsigned long long)OCFS2_I(inode)->ip_blkno, write ? "EXMODE" : "PRMODE"); - ocfs2_cluster_unlock(OCFS2_SB(inode->i_sb), lockres, level); + if (!ocfs2_mount_local(osb)) + ocfs2_cluster_unlock(OCFS2_SB(inode->i_sb), lockres, level); mlog_exit_void(); } @@ -1182,6 +1188,7 @@ int ocfs2_data_lock_full(struct inode *inode, { int status = 0, level; struct ocfs2_lock_res *lockres; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); BUG_ON(!inode); @@ -1201,6 +1208,9 @@ int ocfs2_data_lock_full(struct inode *inode, goto out; } + if (ocfs2_mount_local(osb)) + goto out; + lockres = &OCFS2_I(inode)->ip_data_lockres; level = write ? LKM_EXMODE : LKM_PRMODE; @@ -1269,6 +1279,7 @@ void ocfs2_data_unlock(struct inode *inode, { int level = write ? LKM_EXMODE : LKM_PRMODE; struct ocfs2_lock_res *lockres = &OCFS2_I(inode)->ip_data_lockres; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); mlog_entry_void(); @@ -1276,7 +1287,8 @@ void ocfs2_data_unlock(struct inode *inode, (unsigned long long)OCFS2_I(inode)->ip_blkno, write ? "EXMODE" : "PRMODE"); - if (!ocfs2_is_hard_readonly(OCFS2_SB(inode->i_sb))) + if (!ocfs2_is_hard_readonly(OCFS2_SB(inode->i_sb)) && + !ocfs2_mount_local(osb)) ocfs2_cluster_unlock(OCFS2_SB(inode->i_sb), lockres, level); mlog_exit_void(); @@ -1467,8 +1479,9 @@ static int ocfs2_meta_lock_update(struct inode *inode, { int status = 0; struct ocfs2_inode_info *oi = OCFS2_I(inode); - struct ocfs2_lock_res *lockres; + struct ocfs2_lock_res *lockres = NULL; struct ocfs2_dinode *fe; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); mlog_entry_void(); @@ -1483,10 +1496,12 @@ static int ocfs2_meta_lock_update(struct inode *inode, } spin_unlock(&oi->ip_lock); - lockres = &oi->ip_meta_lockres; + if (!ocfs2_mount_local(osb)) { + lockres = &oi->ip_meta_lockres; - if (!ocfs2_should_refresh_lock_res(lockres)) - goto bail; + if (!ocfs2_should_refresh_lock_res(lockres)) + goto bail; + } /* This will discard any caching information we might have had * for the inode metadata. */ @@ -1496,7 +1511,7 @@ static int ocfs2_meta_lock_update(struct inode *inode, * map (directories, bitmap files, etc) */ ocfs2_extent_map_trunc(inode, 0); - if (ocfs2_meta_lvb_is_trustable(inode, lockres)) { + if (lockres && ocfs2_meta_lvb_is_trustable(inode, lockres)) { mlog(0, "Trusting LVB on inode %llu\n", (unsigned long long)oi->ip_blkno); ocfs2_refresh_inode_from_lvb(inode); @@ -1543,7 +1558,8 @@ static int ocfs2_meta_lock_update(struct inode *inode, status = 0; bail_refresh: - ocfs2_complete_lock_res_refresh(lockres, status); + if (lockres) + ocfs2_complete_lock_res_refresh(lockres, status); bail: mlog_exit(status); return status; @@ -1585,7 +1601,7 @@ int ocfs2_meta_lock_full(struct inode *inode, int arg_flags) { int status, level, dlm_flags, acquired; - struct ocfs2_lock_res *lockres; + struct ocfs2_lock_res *lockres = NULL; struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); struct buffer_head *local_bh = NULL; @@ -1607,6 +1623,9 @@ int ocfs2_meta_lock_full(struct inode *inode, goto bail; } + if (ocfs2_mount_local(osb)) + goto local; + if (!(arg_flags & OCFS2_META_LOCK_RECOVERY)) wait_event(osb->recovery_event, ocfs2_node_map_is_empty(osb, &osb->recovery_map)); @@ -1636,6 +1655,7 @@ int ocfs2_meta_lock_full(struct inode *inode, wait_event(osb->recovery_event, ocfs2_node_map_is_empty(osb, &osb->recovery_map)); +local: /* * We only see this flag if we're being called from * ocfs2_read_locked_inode(). It means we're locking an inode @@ -1644,7 +1664,8 @@ int ocfs2_meta_lock_full(struct inode *inode, */ if (inode->i_state & I_NEW) { status = 0; - ocfs2_complete_lock_res_refresh(lockres, 0); + if (lockres) + ocfs2_complete_lock_res_refresh(lockres, 0); goto bail; } @@ -1767,6 +1788,7 @@ void ocfs2_meta_unlock(struct inode *inode, { int level = ex ? LKM_EXMODE : LKM_PRMODE; struct ocfs2_lock_res *lockres = &OCFS2_I(inode)->ip_meta_lockres; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); mlog_entry_void(); @@ -1774,7 +1796,8 @@ void ocfs2_meta_unlock(struct inode *inode, (unsigned long long)OCFS2_I(inode)->ip_blkno, ex ? "EXMODE" : "PRMODE"); - if (!ocfs2_is_hard_readonly(OCFS2_SB(inode->i_sb))) + if (!ocfs2_is_hard_readonly(OCFS2_SB(inode->i_sb)) && + !ocfs2_mount_local(osb)) ocfs2_cluster_unlock(OCFS2_SB(inode->i_sb), lockres, level); mlog_exit_void(); @@ -1783,7 +1806,7 @@ void ocfs2_meta_unlock(struct inode *inode, int ocfs2_super_lock(struct ocfs2_super *osb, int ex) { - int status; + int status = 0; int level = ex ? LKM_EXMODE : LKM_PRMODE; struct ocfs2_lock_res *lockres = &osb->osb_super_lockres; struct buffer_head *bh; @@ -1794,6 +1817,9 @@ int ocfs2_super_lock(struct ocfs2_super *osb, if (ocfs2_is_hard_readonly(osb)) return -EROFS; + if (ocfs2_mount_local(osb)) + goto bail; + status = ocfs2_cluster_lock(osb, lockres, level, 0, 0); if (status < 0) { mlog_errno(status); @@ -1832,7 +1858,8 @@ void ocfs2_super_unlock(struct ocfs2_super *osb, int level = ex ? LKM_EXMODE : LKM_PRMODE; struct ocfs2_lock_res *lockres = &osb->osb_super_lockres; - ocfs2_cluster_unlock(osb, lockres, level); + if (!ocfs2_mount_local(osb)) + ocfs2_cluster_unlock(osb, lockres, level); } int ocfs2_rename_lock(struct ocfs2_super *osb) @@ -1843,6 +1870,9 @@ int ocfs2_rename_lock(struct ocfs2_super *osb) if (ocfs2_is_hard_readonly(osb)) return -EROFS; + if (ocfs2_mount_local(osb)) + return 0; + status = ocfs2_cluster_lock(osb, lockres, LKM_EXMODE, 0, 0); if (status < 0) mlog_errno(status); @@ -1854,7 +1884,8 @@ void ocfs2_rename_unlock(struct ocfs2_super *osb) { struct ocfs2_lock_res *lockres = &osb->osb_rename_lockres; - ocfs2_cluster_unlock(osb, lockres, LKM_EXMODE); + if (!ocfs2_mount_local(osb)) + ocfs2_cluster_unlock(osb, lockres, LKM_EXMODE); } int ocfs2_dentry_lock(struct dentry *dentry, int ex) @@ -1869,6 +1900,9 @@ int ocfs2_dentry_lock(struct dentry *dentry, int ex) if (ocfs2_is_hard_readonly(osb)) return -EROFS; + if (ocfs2_mount_local(osb)) + return 0; + ret = ocfs2_cluster_lock(osb, &dl->dl_lockres, level, 0, 0); if (ret < 0) mlog_errno(ret); @@ -1882,7 +1916,8 @@ void ocfs2_dentry_unlock(struct dentry *dentry, int ex) struct ocfs2_dentry_lock *dl = dentry->d_fsdata; struct ocfs2_super *osb = OCFS2_SB(dentry->d_sb); - ocfs2_cluster_unlock(osb, &dl->dl_lockres, level); + if (!ocfs2_mount_local(osb)) + ocfs2_cluster_unlock(osb, &dl->dl_lockres, level); } /* Reference counting of the dlm debug structure. We want this because @@ -2145,12 +2180,15 @@ static void ocfs2_dlm_shutdown_debug(struct ocfs2_super *osb) int ocfs2_dlm_init(struct ocfs2_super *osb) { - int status; + int status = 0; u32 dlm_key; - struct dlm_ctxt *dlm; + struct dlm_ctxt *dlm = NULL; mlog_entry_void(); + if (ocfs2_mount_local(osb)) + goto local; + status = ocfs2_dlm_init_debug(osb); if (status < 0) { mlog_errno(status); @@ -2178,11 +2216,12 @@ int ocfs2_dlm_init(struct ocfs2_super *osb) goto bail; } + dlm_register_eviction_cb(dlm, &osb->osb_eviction_cb); + +local: ocfs2_super_lock_res_init(&osb->osb_super_lockres, osb); ocfs2_rename_lock_res_init(&osb->osb_rename_lockres, osb); - dlm_register_eviction_cb(dlm, &osb->osb_eviction_cb); - osb->dlm = dlm; status = 0; diff --git a/fs/ocfs2/heartbeat.c b/fs/ocfs2/heartbeat.c index cbfd45a..8fc52d6 100644 --- a/fs/ocfs2/heartbeat.c +++ b/fs/ocfs2/heartbeat.c @@ -154,6 +154,9 @@ int ocfs2_register_hb_callbacks(struct ocfs2_super *osb) { int status; + if (ocfs2_mount_local(osb)) + return 0; + status = o2hb_register_callback(&osb->osb_hb_down); if (status < 0) { mlog_errno(status); @@ -172,6 +175,9 @@ void ocfs2_clear_hb_callbacks(struct ocfs2_super *osb) { int status; + if (ocfs2_mount_local(osb)) + return; + status = o2hb_unregister_callback(&osb->osb_hb_down); if (status < 0) mlog_errno(status); @@ -186,6 +192,9 @@ void ocfs2_stop_heartbeat(struct ocfs2_super *osb) int ret; char *argv[5], *envp[3]; + if (ocfs2_mount_local(osb)) + return; + if (!osb->uuid_str) { /* This can happen if we don't get far enough in mount... */ mlog(0, "No UUID with which to stop heartbeat!\n\n"); diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c index 42e361f..e4d9149 100644 --- a/fs/ocfs2/inode.c +++ b/fs/ocfs2/inode.c @@ -423,7 +423,8 @@ static int ocfs2_read_locked_inode(struct inode *inode, * cluster lock before trusting anything anyway. */ can_lock = !(args->fi_flags & OCFS2_FI_FLAG_SYSFILE) - && !(args->fi_flags & OCFS2_FI_FLAG_NOLOCK); + && !(args->fi_flags & OCFS2_FI_FLAG_NOLOCK) + && !ocfs2_mount_local(osb); /* * To maintain backwards compatibility with older versions of diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c index 1d7f4ab..825cb0a 100644 --- a/fs/ocfs2/journal.c +++ b/fs/ocfs2/journal.c @@ -144,8 +144,10 @@ handle_t *ocfs2_start_trans(struct ocfs2_super *osb, int max_buffs) ocfs2_abort(osb->sb, "Detected aborted journal"); handle = ERR_PTR(-EROFS); } - } else - atomic_inc(&(osb->journal->j_num_trans)); + } else { + if (!ocfs2_mount_local(osb)) + atomic_inc(&(osb->journal->j_num_trans)); + } return handle; } @@ -507,9 +509,23 @@ void ocfs2_journal_shutdown(struct ocfs2_super *osb) BUG_ON(atomic_read(&(osb->journal->j_num_trans)) != 0); - status = ocfs2_journal_toggle_dirty(osb, 0); - if (status < 0) - mlog_errno(status); + if (ocfs2_mount_local(osb)) { + journal_lock_updates(journal->j_journal); + status = journal_flush(journal->j_journal); + journal_unlock_updates(journal->j_journal); + if (status < 0) + mlog_errno(status); + } + + if (status == 0) { + /* + * Do not toggle if flush was unsuccessful otherwise + * will leave dirty metadata in a "clean" journal + */ + status = ocfs2_journal_toggle_dirty(osb, 0); + if (status < 0) + mlog_errno(status); + } /* Shutdown the kernel journal system */ journal_destroy(journal->j_journal); @@ -549,7 +565,7 @@ static void ocfs2_clear_journal_error(struct super_block *sb, } } -int ocfs2_journal_load(struct ocfs2_journal *journal) +int ocfs2_journal_load(struct ocfs2_journal *journal, int local) { int status = 0; struct ocfs2_super *osb; @@ -576,14 +592,18 @@ int ocfs2_journal_load(struct ocfs2_journal *journal) } /* Launch the commit thread */ - osb->commit_task = kthread_run(ocfs2_commit_thread, osb, "ocfs2cmt"); - if (IS_ERR(osb->commit_task)) { - status = PTR_ERR(osb->commit_task); + if (!local) { + osb->commit_task = kthread_run(ocfs2_commit_thread, osb, + "ocfs2cmt"); + if (IS_ERR(osb->commit_task)) { + status = PTR_ERR(osb->commit_task); + osb->commit_task = NULL; + mlog(ML_ERROR, "unable to launch ocfs2commit thread, " + "error=%d", status); + goto done; + } + } else osb->commit_task = NULL; - mlog(ML_ERROR, "unable to launch ocfs2commit thread, error=%d", - status); - goto done; - } done: mlog_exit(status); diff --git a/fs/ocfs2/journal.h b/fs/ocfs2/journal.h index 899112a..e121636 100644 --- a/fs/ocfs2/journal.h +++ b/fs/ocfs2/journal.h @@ -157,7 +157,7 @@ int ocfs2_journal_init(struct ocfs2_journal *journal, void ocfs2_journal_shutdown(struct ocfs2_super *osb); int ocfs2_journal_wipe(struct ocfs2_journal *journal, int full); -int ocfs2_journal_load(struct ocfs2_journal *journal); +int ocfs2_journal_load(struct ocfs2_journal *journal, int local); int ocfs2_check_journals_nolocks(struct ocfs2_super *osb); void ocfs2_recovery_thread(struct ocfs2_super *osb, int node_num); @@ -174,6 +174,9 @@ static inline void ocfs2_checkpoint_inode(struct inode *inode) { struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + if (ocfs2_mount_local(osb)) + return; + if (!ocfs2_inode_fully_checkpointed(inode)) { /* WARNING: This only kicks off a single * checkpoint. If someone races you and adds more diff --git a/fs/ocfs2/mmap.c b/fs/ocfs2/mmap.c index 69f85ae..51b0204 100644 --- a/fs/ocfs2/mmap.c +++ b/fs/ocfs2/mmap.c @@ -83,10 +83,12 @@ static struct vm_operations_struct ocfs2_file_vm_ops = { int ocfs2_mmap(struct file *file, struct vm_area_struct *vma) { int ret = 0, lock_level = 0; + struct ocfs2_super *osb = OCFS2_SB(file->f_dentry->d_inode->i_sb); /* We don't want to support shared writable mappings yet. */ - if (((vma->vm_flags & VM_SHARED) || (vma->vm_flags & VM_MAYSHARE)) - && ((vma->vm_flags & VM_WRITE) || (vma->vm_flags & VM_MAYWRITE))) { + if (!ocfs2_mount_local(osb) && + ((vma->vm_flags & VM_SHARED) || (vma->vm_flags & VM_MAYSHARE)) && + ((vma->vm_flags & VM_WRITE) || (vma->vm_flags & VM_MAYWRITE))) { mlog(0, "disallow shared writable mmaps %lx\n", vma->vm_flags); /* This is -EINVAL because generic_file_readonly_mmap * returns it in a similar situation. */ diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c index 21db45d..9637039 100644 --- a/fs/ocfs2/namei.c +++ b/fs/ocfs2/namei.c @@ -587,9 +587,11 @@ static int ocfs2_mknod_locked(struct ocfs2_super *osb, } ocfs2_inode_set_new(osb, inode); - status = ocfs2_create_new_inode_locks(inode); - if (status < 0) - mlog_errno(status); + if (!ocfs2_mount_local(osb)) { + status = ocfs2_create_new_inode_locks(inode); + if (status < 0) + mlog_errno(status); + } status = 0; /* error in ocfs2_create_new_inode_locks is not * critical */ diff --git a/fs/ocfs2/ocfs2.h b/fs/ocfs2/ocfs2.h index b767fd7..db8e77c 100644 --- a/fs/ocfs2/ocfs2.h +++ b/fs/ocfs2/ocfs2.h @@ -349,6 +349,11 @@ static inline int ocfs2_is_soft_readonly(struct ocfs2_super *osb) return ret; } +static inline int ocfs2_mount_local(struct ocfs2_super *osb) +{ + return (osb->s_feature_incompat & OCFS2_FEATURE_INCOMPAT_LOCAL_MOUNT); +} + #define OCFS2_IS_VALID_DINODE(ptr) \ (!strcmp((ptr)->i_signature, OCFS2_INODE_SIGNATURE)) diff --git a/fs/ocfs2/ocfs2_fs.h b/fs/ocfs2/ocfs2_fs.h index 3330a5d..2e90e89 100644 --- a/fs/ocfs2/ocfs2_fs.h +++ b/fs/ocfs2/ocfs2_fs.h @@ -86,7 +86,7 @@ OCFS2_SB(sb)->s_feature_incompat &= ~(mask) #define OCFS2_FEATURE_COMPAT_SUPP 0 -#define OCFS2_FEATURE_INCOMPAT_SUPP 0 +#define OCFS2_FEATURE_INCOMPAT_SUPP OCFS2_FEATURE_INCOMPAT_LOCAL_MOUNT #define OCFS2_FEATURE_RO_COMPAT_SUPP 0 /* @@ -97,6 +97,9 @@ #define OCFS2_FEATURE_INCOMPAT_HEARTBEAT_DEV 0x0002 +/* Used to denote a non-clustered volume */ +#define OCFS2_FEATURE_INCOMPAT_LOCAL_MOUNT 0x0008 + /* * Flags on ocfs2_dinode.i_flags */ diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index 4bf3954..a6d2f8c 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c @@ -508,6 +508,27 @@ bail: return status; } +static int ocfs2_verify_heartbeat(struct ocfs2_super *osb) +{ + if (ocfs2_mount_local(osb)) { + if (osb->s_mount_opt & OCFS2_MOUNT_HB_LOCAL) { + mlog(ML_ERROR, "Cannot heartbeat on a locally " + "mounted device.\n"); + return -EINVAL; + } + } + + if (!(osb->s_mount_opt & OCFS2_MOUNT_HB_LOCAL)) { + if (!ocfs2_mount_local(osb) && !ocfs2_is_hard_readonly(osb)) { + mlog(ML_ERROR, "Heartbeat has to be started to mount " + "a read-write clustered device.\n"); + return -EINVAL; + } + } + + return 0; +} + static int ocfs2_fill_super(struct super_block *sb, void *data, int silent) { struct dentry *root; @@ -516,16 +537,24 @@ static int ocfs2_fill_super(struct super_block *sb, void *data, int silent) struct inode *inode = NULL; struct ocfs2_super *osb = NULL; struct buffer_head *bh = NULL; + char nodestr[8]; mlog_entry("%p, %p, %i", sb, data, silent); - /* for now we only have one cluster/node, make sure we see it - * in the heartbeat universe */ - if (!o2hb_check_local_node_heartbeating()) { + if (!ocfs2_parse_options(sb, data, &parsed_opt, 0)) { status = -EINVAL; goto read_super_error; } + /* for now we only have one cluster/node, make sure we see it + * in the heartbeat universe */ + if (parsed_opt & OCFS2_MOUNT_HB_LOCAL) { + if (!o2hb_check_local_node_heartbeating()) { + status = -EINVAL; + goto read_super_error; + } + } + /* probe for superblock */ status = ocfs2_sb_probe(sb, &bh, §or_size); if (status < 0) { @@ -541,11 +570,6 @@ static int ocfs2_fill_super(struct super_block *sb, void *data, int silent) } brelse(bh); bh = NULL; - - if (!ocfs2_parse_options(sb, data, &parsed_opt, 0)) { - status = -EINVAL; - goto read_super_error; - } osb->s_mount_opt = parsed_opt; sb->s_magic = OCFS2_SUPER_MAGIC; @@ -588,21 +612,16 @@ static int ocfs2_fill_super(struct super_block *sb, void *data, int silent) } if (!ocfs2_is_hard_readonly(osb)) { - /* If this isn't a hard readonly mount, then we need - * to make sure that heartbeat is in a valid state, - * and that we mark ourselves soft readonly is -oro - * was specified. */ - if (!(osb->s_mount_opt & OCFS2_MOUNT_HB_LOCAL)) { - mlog(ML_ERROR, "No heartbeat for device (%s)\n", - sb->s_id); - status = -EINVAL; - goto read_super_error; - } - if (sb->s_flags & MS_RDONLY) ocfs2_set_ro_flag(osb, 0); } + status = ocfs2_verify_heartbeat(osb); + if (status < 0) { + mlog_errno(status); + goto read_super_error; + } + osb->osb_debug_root = debugfs_create_dir(osb->uuid_str, ocfs2_debugfs_root); if (!osb->osb_debug_root) { @@ -635,9 +654,14 @@ static int ocfs2_fill_super(struct super_block *sb, void *data, int silent) ocfs2_complete_mount_recovery(osb); - printk(KERN_INFO "ocfs2: Mounting device (%s) on (node %d, slot %d) " + if (ocfs2_mount_local(osb)) + snprintf(nodestr, sizeof(nodestr), "local"); + else + snprintf(nodestr, sizeof(nodestr), "%d", osb->node_num); + + printk(KERN_INFO "ocfs2: Mounting device (%s) on (node %s, slot %d) " "with %s data mode.\n", - osb->dev_str, osb->node_num, osb->slot_num, + osb->dev_str, nodestr, osb->slot_num, osb->s_mount_opt & OCFS2_MOUNT_DATA_WRITEBACK ? "writeback" : "ordered"); @@ -999,7 +1023,11 @@ static int ocfs2_fill_local_node_info(struct ocfs2_super *osb) /* XXX hold a ref on the node while mounte? easy enough, if * desirable. */ - osb->node_num = o2nm_this_node(); + if (ocfs2_mount_local(osb)) + osb->node_num = 0; + else + osb->node_num = o2nm_this_node(); + if (osb->node_num == O2NM_MAX_NODES) { mlog(ML_ERROR, "could not find this host's node number\n"); status = -ENOENT; @@ -1084,6 +1112,9 @@ static int ocfs2_mount_volume(struct super_block *sb) goto leave; } + if (ocfs2_mount_local(osb)) + goto leave; + /* This should be sent *after* we recovered our journal as it * will cause other nodes to unmark us as needing * recovery. However, we need to send it *before* dropping the @@ -1114,6 +1145,7 @@ static void ocfs2_dismount_volume(struct super_block *sb, int mnt_err) { int tmp; struct ocfs2_super *osb = NULL; + char nodestr[8]; mlog_entry("(0x%p)\n", sb); @@ -1177,8 +1209,13 @@ static void ocfs2_dismount_volume(struct super_block *sb, int mnt_err) atomic_set(&osb->vol_state, VOLUME_DISMOUNTED); - printk(KERN_INFO "ocfs2: Unmounting device (%s) on (node %d)\n", - osb->dev_str, osb->node_num); + if (ocfs2_mount_local(osb)) + snprintf(nodestr, sizeof(nodestr), "local"); + else + snprintf(nodestr, sizeof(nodestr), "%d", osb->node_num); + + printk(KERN_INFO "ocfs2: Unmounting device (%s) on (node %s)\n", + osb->dev_str, nodestr); ocfs2_delete_osb(osb); kfree(osb); @@ -1536,6 +1573,7 @@ static int ocfs2_check_volume(struct ocfs2_super *osb) { int status = 0; int dirty; + int local; struct ocfs2_dinode *local_alloc = NULL; /* only used if we * recover * ourselves. */ @@ -1563,8 +1601,10 @@ static int ocfs2_check_volume(struct ocfs2_super *osb) "recovering volume.\n"); } + local = ocfs2_mount_local(osb); + /* will play back anything left in the journal. */ - ocfs2_journal_load(osb->journal); + ocfs2_journal_load(osb->journal, local); if (dirty) { /* recover my local alloc if we didn't unmount cleanly. */ diff --git a/fs/ocfs2/vote.c b/fs/ocfs2/vote.c index 5b4dca7..0315a8b 100644 --- a/fs/ocfs2/vote.c +++ b/fs/ocfs2/vote.c @@ -1000,6 +1000,9 @@ int ocfs2_register_net_handlers(struct ocfs2_super *osb) { int status = 0; + if (ocfs2_mount_local(osb)) + return 0; + status = o2net_register_handler(OCFS2_MESSAGE_TYPE_RESPONSE, osb->net_key, sizeof(struct ocfs2_response_msg), -- cgit v0.10.2 From bcd5625bffe103efa7413bb6432fc871074a19c2 Mon Sep 17 00:00:00 2001 From: Tiger Yang Date: Tue, 5 Dec 2006 10:09:17 +0800 Subject: ocfs2: update mount option documentation We forgot to document the atime_quantum mount option in ocfs2.txt. This adds a proper description of how it works. Signed-off-by: Tiger Yang Signed-off-by: Mark Fasheh diff --git a/Documentation/filesystems/ocfs2.txt b/Documentation/filesystems/ocfs2.txt index af6defd..8ccf0c1 100644 --- a/Documentation/filesystems/ocfs2.txt +++ b/Documentation/filesystems/ocfs2.txt @@ -54,3 +54,6 @@ errors=panic Panic and halt the machine if an error occurs. intr (*) Allow signals to interrupt cluster operations. nointr Do not allow signals to interrupt cluster operations. +atime_quantum=60(*) OCFS2 will not update atime unless this number + of seconds has passed since the last update. + Set to zero to always update atime. -- cgit v0.10.2 From 8903901dbf46bbdf1f70ffe7bc09cb6b97e6728a Mon Sep 17 00:00:00 2001 From: Mark Fasheh Date: Thu, 7 Dec 2006 18:05:37 -0800 Subject: ocfs2: Synchronize feature incompat flags in ocfs2_fs.h These got a little bit out of date with ocfs2-tools, make things consistent again. We reserve a flag for sparse allocation code as that's pretty close to testable at this point. Signed-off-by: Mark Fasheh diff --git a/fs/ocfs2/ocfs2_fs.h b/fs/ocfs2/ocfs2_fs.h index 2e90e89..b5c6856 100644 --- a/fs/ocfs2/ocfs2_fs.h +++ b/fs/ocfs2/ocfs2_fs.h @@ -96,10 +96,19 @@ */ #define OCFS2_FEATURE_INCOMPAT_HEARTBEAT_DEV 0x0002 +/* + * tunefs sets this incompat flag before starting the resize and clears it + * at the end. This flag protects users from inadvertently mounting the fs + * after an aborted run without fsck-ing. + */ +#define OCFS2_FEATURE_INCOMPAT_RESIZE_INPROG 0x0004 /* Used to denote a non-clustered volume */ #define OCFS2_FEATURE_INCOMPAT_LOCAL_MOUNT 0x0008 +/* Support for sparse allocation in b-trees */ +#define OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC 0x0010 + /* * Flags on ocfs2_dinode.i_flags */ -- cgit v0.10.2 From 296b75ed6a3b35f613961cefe4962ce1cf586d77 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 4 Dec 2006 14:04:53 +0100 Subject: [patch 1/3] OCFS2 - Expose struct o2nm_cluster Subsequent patches (namely userspace heartbeat and configurable timeouts) require access to the o2nm_cluster struct. This patch does the necessary shuffling. Signed-off-by: Andrew Beekhof Signed-off-by: Mark Fasheh diff --git a/fs/ocfs2/cluster/nodemanager.c b/fs/ocfs2/cluster/nodemanager.c index d11753c..dd4aefa 100644 --- a/fs/ocfs2/cluster/nodemanager.c +++ b/fs/ocfs2/cluster/nodemanager.c @@ -35,7 +35,7 @@ /* for now we operate under the assertion that there can be only one * cluster active at a time. Changing this will require trickling * cluster references throughout where nodes are looked up */ -static struct o2nm_cluster *o2nm_single_cluster = NULL; +struct o2nm_cluster *o2nm_single_cluster = NULL; #define OCFS2_MAX_HB_CTL_PATH 256 static char ocfs2_hb_ctl_path[OCFS2_MAX_HB_CTL_PATH] = "/sbin/ocfs2_hb_ctl"; @@ -97,17 +97,6 @@ const char *o2nm_get_hb_ctl_path(void) } EXPORT_SYMBOL_GPL(o2nm_get_hb_ctl_path); -struct o2nm_cluster { - struct config_group cl_group; - unsigned cl_has_local:1; - u8 cl_local_node; - rwlock_t cl_nodes_lock; - struct o2nm_node *cl_nodes[O2NM_MAX_NODES]; - struct rb_root cl_node_ip_tree; - /* this bitmap is part of a hack for disk bitmap.. will go eventually. - zab */ - unsigned long cl_nodes_bitmap[BITS_TO_LONGS(O2NM_MAX_NODES)]; -}; - struct o2nm_node *o2nm_get_node_by_num(u8 node_num) { struct o2nm_node *node = NULL; diff --git a/fs/ocfs2/cluster/nodemanager.h b/fs/ocfs2/cluster/nodemanager.h index fce8033..b571cda 100644 --- a/fs/ocfs2/cluster/nodemanager.h +++ b/fs/ocfs2/cluster/nodemanager.h @@ -53,6 +53,20 @@ struct o2nm_node { unsigned long nd_set_attributes; }; +struct o2nm_cluster { + struct config_group cl_group; + unsigned cl_has_local:1; + u8 cl_local_node; + rwlock_t cl_nodes_lock; + struct o2nm_node *cl_nodes[O2NM_MAX_NODES]; + struct rb_root cl_node_ip_tree; + + /* this bitmap is part of a hack for disk bitmap.. will go eventually. - zab */ + unsigned long cl_nodes_bitmap[BITS_TO_LONGS(O2NM_MAX_NODES)]; +}; + +extern struct o2nm_cluster *o2nm_single_cluster; + u8 o2nm_this_node(void); int o2nm_configured_node_map(unsigned long *map, unsigned bytes); -- cgit v0.10.2 From b5dd80304da482d77b2320e1a01a189e656b9770 Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Mon, 4 Dec 2006 14:04:54 +0100 Subject: [patch 2/3] OCFS2 Configurable timeouts Allow configuration of OCFS2 timeouts from userspace via configfs Signed-off-by: Andrew Beekhof Signed-off-by: Mark Fasheh diff --git a/fs/ocfs2/cluster/nodemanager.c b/fs/ocfs2/cluster/nodemanager.c index dd4aefa..234f83f 100644 --- a/fs/ocfs2/cluster/nodemanager.c +++ b/fs/ocfs2/cluster/nodemanager.c @@ -532,6 +532,161 @@ static struct o2nm_node_group *to_o2nm_node_group(struct config_group *group) } #endif +struct o2nm_cluster_attribute { + struct configfs_attribute attr; + ssize_t (*show)(struct o2nm_cluster *, char *); + ssize_t (*store)(struct o2nm_cluster *, const char *, size_t); +}; + +static ssize_t o2nm_cluster_attr_write(const char *page, ssize_t count, + unsigned int *val) +{ + unsigned long tmp; + char *p = (char *)page; + + tmp = simple_strtoul(p, &p, 0); + if (!p || (*p && (*p != '\n'))) + return -EINVAL; + + if (tmp == 0) + return -EINVAL; + if (tmp >= (u32)-1) + return -ERANGE; + + *val = tmp; + + return count; +} + +static ssize_t o2nm_cluster_attr_idle_timeout_ms_read( + struct o2nm_cluster *cluster, char *page) +{ + return sprintf(page, "%u\n", cluster->cl_idle_timeout_ms); +} + +static ssize_t o2nm_cluster_attr_idle_timeout_ms_write( + struct o2nm_cluster *cluster, const char *page, size_t count) +{ + ssize_t ret; + unsigned int val; + + ret = o2nm_cluster_attr_write(page, count, &val); + + if (ret > 0) { + if (val <= cluster->cl_keepalive_delay_ms) { + mlog(ML_NOTICE, "o2net: idle timeout must be larger " + "than keepalive delay\n"); + return -EINVAL; + } + cluster->cl_idle_timeout_ms = val; + } + + return ret; +} + +static ssize_t o2nm_cluster_attr_keepalive_delay_ms_read( + struct o2nm_cluster *cluster, char *page) +{ + return sprintf(page, "%u\n", cluster->cl_keepalive_delay_ms); +} + +static ssize_t o2nm_cluster_attr_keepalive_delay_ms_write( + struct o2nm_cluster *cluster, const char *page, size_t count) +{ + ssize_t ret; + unsigned int val; + + ret = o2nm_cluster_attr_write(page, count, &val); + + if (ret > 0) { + if (val >= cluster->cl_idle_timeout_ms) { + mlog(ML_NOTICE, "o2net: keepalive delay must be " + "smaller than idle timeout\n"); + return -EINVAL; + } + cluster->cl_keepalive_delay_ms = val; + } + + return ret; +} + +static ssize_t o2nm_cluster_attr_reconnect_delay_ms_read( + struct o2nm_cluster *cluster, char *page) +{ + return sprintf(page, "%u\n", cluster->cl_reconnect_delay_ms); +} + +static ssize_t o2nm_cluster_attr_reconnect_delay_ms_write( + struct o2nm_cluster *cluster, const char *page, size_t count) +{ + return o2nm_cluster_attr_write(page, count, + &cluster->cl_reconnect_delay_ms); +} +static struct o2nm_cluster_attribute o2nm_cluster_attr_idle_timeout_ms = { + .attr = { .ca_owner = THIS_MODULE, + .ca_name = "idle_timeout_ms", + .ca_mode = S_IRUGO | S_IWUSR }, + .show = o2nm_cluster_attr_idle_timeout_ms_read, + .store = o2nm_cluster_attr_idle_timeout_ms_write, +}; + +static struct o2nm_cluster_attribute o2nm_cluster_attr_keepalive_delay_ms = { + .attr = { .ca_owner = THIS_MODULE, + .ca_name = "keepalive_delay_ms", + .ca_mode = S_IRUGO | S_IWUSR }, + .show = o2nm_cluster_attr_keepalive_delay_ms_read, + .store = o2nm_cluster_attr_keepalive_delay_ms_write, +}; + +static struct o2nm_cluster_attribute o2nm_cluster_attr_reconnect_delay_ms = { + .attr = { .ca_owner = THIS_MODULE, + .ca_name = "reconnect_delay_ms", + .ca_mode = S_IRUGO | S_IWUSR }, + .show = o2nm_cluster_attr_reconnect_delay_ms_read, + .store = o2nm_cluster_attr_reconnect_delay_ms_write, +}; + +static struct configfs_attribute *o2nm_cluster_attrs[] = { + &o2nm_cluster_attr_idle_timeout_ms.attr, + &o2nm_cluster_attr_keepalive_delay_ms.attr, + &o2nm_cluster_attr_reconnect_delay_ms.attr, + NULL, +}; +static ssize_t o2nm_cluster_show(struct config_item *item, + struct configfs_attribute *attr, + char *page) +{ + struct o2nm_cluster *cluster = to_o2nm_cluster(item); + struct o2nm_cluster_attribute *o2nm_cluster_attr = + container_of(attr, struct o2nm_cluster_attribute, attr); + ssize_t ret = 0; + + if (o2nm_cluster_attr->show) + ret = o2nm_cluster_attr->show(cluster, page); + return ret; +} + +static ssize_t o2nm_cluster_store(struct config_item *item, + struct configfs_attribute *attr, + const char *page, size_t count) +{ + struct o2nm_cluster *cluster = to_o2nm_cluster(item); + struct o2nm_cluster_attribute *o2nm_cluster_attr = + container_of(attr, struct o2nm_cluster_attribute, attr); + ssize_t ret; + + if (o2nm_cluster_attr->store == NULL) { + ret = -EINVAL; + goto out; + } + + ret = o2nm_cluster_attr->store(cluster, page, count); + if (ret < count) + goto out; +out: + return ret; +} + static struct config_item *o2nm_node_group_make_item(struct config_group *group, const char *name) { @@ -613,10 +768,13 @@ static void o2nm_cluster_release(struct config_item *item) static struct configfs_item_operations o2nm_cluster_item_ops = { .release = o2nm_cluster_release, + .show_attribute = o2nm_cluster_show, + .store_attribute = o2nm_cluster_store, }; static struct config_item_type o2nm_cluster_type = { .ct_item_ops = &o2nm_cluster_item_ops, + .ct_attrs = o2nm_cluster_attrs, .ct_owner = THIS_MODULE, }; @@ -667,6 +825,9 @@ static struct config_group *o2nm_cluster_group_make_group(struct config_group *g cluster->cl_group.default_groups[2] = NULL; rwlock_init(&cluster->cl_nodes_lock); cluster->cl_node_ip_tree = RB_ROOT; + cluster->cl_reconnect_delay_ms = O2NET_RECONNECT_DELAY_MS_DEFAULT; + cluster->cl_idle_timeout_ms = O2NET_IDLE_TIMEOUT_MS_DEFAULT; + cluster->cl_keepalive_delay_ms = O2NET_KEEPALIVE_DELAY_MS_DEFAULT; ret = &cluster->cl_group; o2nm_single_cluster = cluster; diff --git a/fs/ocfs2/cluster/nodemanager.h b/fs/ocfs2/cluster/nodemanager.h index b571cda..8fb23ca 100644 --- a/fs/ocfs2/cluster/nodemanager.h +++ b/fs/ocfs2/cluster/nodemanager.h @@ -60,6 +60,9 @@ struct o2nm_cluster { rwlock_t cl_nodes_lock; struct o2nm_node *cl_nodes[O2NM_MAX_NODES]; struct rb_root cl_node_ip_tree; + unsigned int cl_idle_timeout_ms; + unsigned int cl_keepalive_delay_ms; + unsigned int cl_reconnect_delay_ms; /* this bitmap is part of a hack for disk bitmap.. will go eventually. - zab */ unsigned long cl_nodes_bitmap[BITS_TO_LONGS(O2NM_MAX_NODES)]; diff --git a/fs/ocfs2/cluster/tcp.c b/fs/ocfs2/cluster/tcp.c index 9b3209d..ebbaee6 100644 --- a/fs/ocfs2/cluster/tcp.c +++ b/fs/ocfs2/cluster/tcp.c @@ -147,6 +147,28 @@ static void o2net_listen_data_ready(struct sock *sk, int bytes); static void o2net_sc_send_keep_req(struct work_struct *work); static void o2net_idle_timer(unsigned long data); static void o2net_sc_postpone_idle(struct o2net_sock_container *sc); +static void o2net_sc_reset_idle_timer(struct o2net_sock_container *sc); + +/* + * FIXME: These should use to_o2nm_cluster_from_node(), but we end up + * losing our parent link to the cluster during shutdown. This can be + * solved by adding a pre-removal callback to configfs, or passing + * around the cluster with the node. -jeffm + */ +static inline int o2net_reconnect_delay(struct o2nm_node *node) +{ + return o2nm_single_cluster->cl_reconnect_delay_ms; +} + +static inline int o2net_keepalive_delay(struct o2nm_node *node) +{ + return o2nm_single_cluster->cl_keepalive_delay_ms; +} + +static inline int o2net_idle_timeout(struct o2nm_node *node) +{ + return o2nm_single_cluster->cl_idle_timeout_ms; +} static inline int o2net_sys_err_to_errno(enum o2net_system_error err) { @@ -271,6 +293,8 @@ static void sc_kref_release(struct kref *kref) { struct o2net_sock_container *sc = container_of(kref, struct o2net_sock_container, sc_kref); + BUG_ON(timer_pending(&sc->sc_idle_timeout)); + sclog(sc, "releasing\n"); if (sc->sc_sock) { @@ -424,9 +448,9 @@ static void o2net_set_nn_state(struct o2net_node *nn, /* delay if we're withing a RECONNECT_DELAY of the * last attempt */ delay = (nn->nn_last_connect_attempt + - msecs_to_jiffies(O2NET_RECONNECT_DELAY_MS)) + msecs_to_jiffies(o2net_reconnect_delay(sc->sc_node))) - jiffies; - if (delay > msecs_to_jiffies(O2NET_RECONNECT_DELAY_MS)) + if (delay > msecs_to_jiffies(o2net_reconnect_delay(sc->sc_node))) delay = 0; mlog(ML_CONN, "queueing conn attempt in %lu jiffies\n", delay); queue_delayed_work(o2net_wq, &nn->nn_connect_work, delay); @@ -1105,7 +1129,7 @@ static int o2net_check_handshake(struct o2net_sock_container *sc) /* set valid and queue the idle timers only if it hasn't been * shut down already */ if (nn->nn_sc == sc) { - o2net_sc_postpone_idle(sc); + o2net_sc_reset_idle_timer(sc); o2net_set_nn_state(nn, sc, 1, 0); } spin_unlock(&nn->nn_lock); @@ -1287,8 +1311,10 @@ static void o2net_idle_timer(unsigned long data) do_gettimeofday(&now); - printk(KERN_INFO "o2net: connection to " SC_NODEF_FMT " has been idle for 10 " - "seconds, shutting it down.\n", SC_NODEF_ARGS(sc)); + printk(KERN_INFO "o2net: connection to " SC_NODEF_FMT " has been idle for %u.%u " + "seconds, shutting it down.\n", SC_NODEF_ARGS(sc), + o2net_idle_timeout(sc->sc_node) / 1000, + o2net_idle_timeout(sc->sc_node) % 1000); mlog(ML_NOTICE, "here are some times that might help debug the " "situation: (tmr %ld.%ld now %ld.%ld dr %ld.%ld adv " "%ld.%ld:%ld.%ld func (%08x:%u) %ld.%ld:%ld.%ld)\n", @@ -1306,14 +1332,21 @@ static void o2net_idle_timer(unsigned long data) o2net_sc_queue_work(sc, &sc->sc_shutdown_work); } -static void o2net_sc_postpone_idle(struct o2net_sock_container *sc) +static void o2net_sc_reset_idle_timer(struct o2net_sock_container *sc) { o2net_sc_cancel_delayed_work(sc, &sc->sc_keepalive_work); o2net_sc_queue_delayed_work(sc, &sc->sc_keepalive_work, - O2NET_KEEPALIVE_DELAY_SECS * HZ); + msecs_to_jiffies(o2net_keepalive_delay(sc->sc_node))); do_gettimeofday(&sc->sc_tv_timer); mod_timer(&sc->sc_idle_timeout, - jiffies + (O2NET_IDLE_TIMEOUT_SECS * HZ)); + jiffies + msecs_to_jiffies(o2net_idle_timeout(sc->sc_node))); +} + +static void o2net_sc_postpone_idle(struct o2net_sock_container *sc) +{ + /* Only push out an existing timer */ + if (timer_pending(&sc->sc_idle_timeout)) + o2net_sc_reset_idle_timer(sc); } /* this work func is kicked whenever a path sets the nn state which doesn't @@ -1435,9 +1468,12 @@ static void o2net_connect_expired(struct work_struct *work) spin_lock(&nn->nn_lock); if (!nn->nn_sc_valid) { + struct o2nm_node *node = nn->nn_sc->sc_node; mlog(ML_ERROR, "no connection established with node %u after " - "%u seconds, giving up and returning errors.\n", - o2net_num_from_nn(nn), O2NET_IDLE_TIMEOUT_SECS); + "%u.%u seconds, giving up and returning errors.\n", + o2net_num_from_nn(nn), + o2net_idle_timeout(node) / 1000, + o2net_idle_timeout(node) % 1000); o2net_set_nn_state(nn, NULL, 0, -ENOTCONN); } @@ -1489,14 +1525,14 @@ static void o2net_hb_node_up_cb(struct o2nm_node *node, int node_num, /* ensure an immediate connect attempt */ nn->nn_last_connect_attempt = jiffies - - (msecs_to_jiffies(O2NET_RECONNECT_DELAY_MS) + 1); + (msecs_to_jiffies(o2net_reconnect_delay(node)) + 1); if (node_num != o2nm_this_node()) { /* heartbeat doesn't work unless a local node number is * configured and doing so brings up the o2net_wq, so we can * use it.. */ queue_delayed_work(o2net_wq, &nn->nn_connect_expired, - O2NET_IDLE_TIMEOUT_SECS * HZ); + msecs_to_jiffies(o2net_idle_timeout(node))); /* believe it or not, accept and node hearbeating testing * can succeed for this node before we got here.. so diff --git a/fs/ocfs2/cluster/tcp.h b/fs/ocfs2/cluster/tcp.h index 616ff2b..2e08976 100644 --- a/fs/ocfs2/cluster/tcp.h +++ b/fs/ocfs2/cluster/tcp.h @@ -54,6 +54,13 @@ typedef int (o2net_msg_handler_func)(struct o2net_msg *msg, u32 len, void *data) #define O2NET_MAX_PAYLOAD_BYTES (4096 - sizeof(struct o2net_msg)) +/* same as hb delay, we're waiting for another node to recognize our hb */ +#define O2NET_RECONNECT_DELAY_MS_DEFAULT 2000 + +#define O2NET_KEEPALIVE_DELAY_MS_DEFAULT 5000 +#define O2NET_IDLE_TIMEOUT_MS_DEFAULT 10000 + + /* TODO: figure this out.... */ static inline int o2net_link_down(int err, struct socket *sock) { diff --git a/fs/ocfs2/cluster/tcp_internal.h b/fs/ocfs2/cluster/tcp_internal.h index daebbd3..56f7ee1 100644 --- a/fs/ocfs2/cluster/tcp_internal.h +++ b/fs/ocfs2/cluster/tcp_internal.h @@ -27,17 +27,11 @@ #define O2NET_MSG_KEEP_REQ_MAGIC ((u16)0xfa57) #define O2NET_MSG_KEEP_RESP_MAGIC ((u16)0xfa58) -/* same as hb delay, we're waiting for another node to recognize our hb */ -#define O2NET_RECONNECT_DELAY_MS O2HB_REGION_TIMEOUT_MS - /* we're delaying our quorum decision so that heartbeat will have timed * out truly dead nodes by the time we come around to making decisions * on their number */ #define O2NET_QUORUM_DELAY_MS ((o2hb_dead_threshold + 2) * O2HB_REGION_TIMEOUT_MS) -#define O2NET_KEEPALIVE_DELAY_SECS 5 -#define O2NET_IDLE_TIMEOUT_SECS 10 - /* * This version number represents quite a lot, unfortunately. It not * only represents the raw network message protocol on the wire but also -- cgit v0.10.2 From 33ec32fae0e2c4433bfd1e74cbde6cb16604a719 Mon Sep 17 00:00:00 2001 From: Steve French Date: Fri, 8 Dec 2006 04:14:28 +0000 Subject: [CIFS] Fix NTLMv2 mounts to Windows servers Windows servers are pickier about NTLMv2 than Samba. This enables more secure mounts to Windows (not just Samba) ie when "sec=ntlmv2" is specified on the mount. Signed-off-by: Steve French diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index 0b3c37e..3539d6e 100644 --- a/fs/cifs/CHANGES +++ b/fs/cifs/CHANGES @@ -5,7 +5,8 @@ Allow null user to be specified on mount ("username="). Do not return EINVAL on readdir when filldir fails due to overwritten blocksize (fixes FC problem). Return error in rename 2nd attempt retry (ie report if rename by handle also fails, after rename by path fails, we were -not reporting whether the retry worked or not). +not reporting whether the retry worked or not). Fix NTLMv2 to +work to Windows servers (mount with option "sec=ntlmv2"). Version 1.45 ------------ diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index 4bc250b..fdeda51 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c @@ -372,8 +372,10 @@ void setup_ntlmv2_rsp(struct cifsSesInfo * ses, char * resp_buf, buf->time = cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); get_random_bytes(&buf->client_chal, sizeof(buf->client_chal)); buf->reserved2 = 0; - buf->names[0].type = 0; + buf->names[0].type = cpu_to_le16(NTLMSSP_DOMAIN_TYPE); buf->names[0].length = 0; + buf->names[1].type = 0; + buf->names[1].length = 0; /* calculate buf->ntlmv2_hash */ rc = calc_ntlmv2_hash(ses, nls_cp); diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index 6df9dad..068ef51e 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -580,6 +580,12 @@ typedef union smb_com_session_setup_andx { /* format of NLTMv2 Response ie "case sensitive password" hash when NTLMv2 */ +#define NTLMSSP_SERVER_TYPE 1 +#define NTLMSSP_DOMAIN_TYPE 2 +#define NTLMSSP_FQ_DOMAIN_TYPE 3 +#define NTLMSSP_DNS_DOMAIN_TYPE 4 +#define NTLMSSP_DNS_PARENT_TYPE 5 + struct ntlmssp2_name { __le16 type; __le16 length; @@ -593,7 +599,7 @@ struct ntlmv2_resp { __le64 time; __u64 client_chal; /* random */ __u32 reserved2; - struct ntlmssp2_name names[1]; + struct ntlmssp2_name names[2]; /* array of name entries could follow ending in minimum 4 byte struct */ } __attribute__((packed)); -- cgit v0.10.2 From a081e126e12f78651499ba0f9944a7df1e9b06b6 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Tue, 5 Dec 2006 15:54:14 +1100 Subject: [POWERPC] Fix cell pmu initialisation Make sure that the pmu is not initialised unless we are running on a cell. Also make the init routine static. Signed-off-by: Stephen Rothwell Signed-off-by: Paul Mackerras diff --git a/arch/powerpc/platforms/cell/pmu.c b/arch/powerpc/platforms/cell/pmu.c index 99c6120..d04ae16 100644 --- a/arch/powerpc/platforms/cell/pmu.c +++ b/arch/powerpc/platforms/cell/pmu.c @@ -382,11 +382,14 @@ static irqreturn_t cbe_pm_irq(int irq, void *dev_id) return IRQ_HANDLED; } -int __init cbe_init_pm_irq(void) +static int __init cbe_init_pm_irq(void) { unsigned int irq; int rc, node; + if (!machine_is(cell)) + return 0; + for_each_node(node) { irq = irq_create_mapping(NULL, IIC_IRQ_IOEX_PMI | (node << IIC_IRQ_NODE_SHIFT)); -- cgit v0.10.2 From 0332c2d447a7a20a4d744ba3814a349d0c1c6405 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Tue, 5 Dec 2006 17:52:36 +1100 Subject: [POWERPC] Move rtas_stop_self() into platforms/pseries/hotplug-cpu.c As the first step in consolidating the pseries hotplug cpu code, create platforms/pseries/hotplug-cpu.c and move rtas_stop_self() into it. Do the rtas token initialisation in a new initcall, rather than rtas_initialize(). Signed-off-by: Michael Ellerman Acked-by: Linas Vepstas Signed-off-by: Paul Mackerras diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c index 387ed0d..952f4c2 100644 --- a/arch/powerpc/kernel/rtas.c +++ b/arch/powerpc/kernel/rtas.c @@ -810,32 +810,6 @@ asmlinkage int ppc_rtas(struct rtas_args __user *uargs) return 0; } -#ifdef CONFIG_HOTPLUG_CPU -/* This version can't take the spinlock, because it never returns */ -static struct rtas_args rtas_stop_self_args = { - /* The token is initialized for real in setup_system() */ - .token = RTAS_UNKNOWN_SERVICE, - .nargs = 0, - .nret = 1, - .rets = &rtas_stop_self_args.args[0], -}; - -void rtas_stop_self(void) -{ - struct rtas_args *rtas_args = &rtas_stop_self_args; - - local_irq_disable(); - - BUG_ON(rtas_args->token == RTAS_UNKNOWN_SERVICE); - - printk("cpu %u (hwid %u) Ready to die...\n", - smp_processor_id(), hard_smp_processor_id()); - enter_rtas(__pa(rtas_args)); - - panic("Alas, I survived.\n"); -} -#endif - /* * Call early during boot, before mem init or bootmem, to retrieve the RTAS * informations from the device-tree and allocate the RMO buffer for userland @@ -880,9 +854,6 @@ void __init rtas_initialize(void) #endif rtas_rmo_buf = lmb_alloc_base(RTAS_RMOBUF_MAX, PAGE_SIZE, rtas_region); -#ifdef CONFIG_HOTPLUG_CPU - rtas_stop_self_args.token = rtas_token("stop-self"); -#endif /* CONFIG_HOTPLUG_CPU */ #ifdef CONFIG_RTAS_ERROR_LOGGING rtas_last_error_token = rtas_token("rtas-last-error"); #endif diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile index 997243a..69590fb 100644 --- a/arch/powerpc/platforms/pseries/Makefile +++ b/arch/powerpc/platforms/pseries/Makefile @@ -10,6 +10,8 @@ obj-$(CONFIG_XICS) += xics.o obj-$(CONFIG_SCANLOG) += scanlog.o obj-$(CONFIG_EEH) += eeh.o eeh_cache.o eeh_driver.o eeh_event.o +obj-$(CONFIG_HOTPLUG_CPU) += hotplug-cpu.o + obj-$(CONFIG_HVC_CONSOLE) += hvconsole.o obj-$(CONFIG_HVCS) += hvcserver.o obj-$(CONFIG_HCALL_STATS) += hvCall_inst.o diff --git a/arch/powerpc/platforms/pseries/hotplug-cpu.c b/arch/powerpc/platforms/pseries/hotplug-cpu.c new file mode 100644 index 0000000..6e21ebd --- /dev/null +++ b/arch/powerpc/platforms/pseries/hotplug-cpu.c @@ -0,0 +1,58 @@ +/* + * pseries CPU Hotplug infrastructure. + * + * Split out from arch/powerpc/kernel/rtas.c + * + * Peter Bergner, IBM March 2001. + * Copyright (C) 2001 IBM. + * + * Copyright (C) 2006 Michael Ellerman, IBM Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "xics.h" + +/* This version can't take the spinlock, because it never returns */ +static struct rtas_args rtas_stop_self_args = { + .token = RTAS_UNKNOWN_SERVICE, + .nargs = 0, + .nret = 1, + .rets = &rtas_stop_self_args.args[0], +}; + +void rtas_stop_self(void) +{ + struct rtas_args *args = &rtas_stop_self_args; + + local_irq_disable(); + + BUG_ON(args->token == RTAS_UNKNOWN_SERVICE); + + printk("cpu %u (hwid %u) Ready to die...\n", + smp_processor_id(), hard_smp_processor_id()); + enter_rtas(__pa(args)); + + panic("Alas, I survived.\n"); +} + +static int __init pseries_cpu_hotplug_init(void) +{ + rtas_stop_self_args.token = rtas_token("stop-self"); + + return 0; +} +arch_initcall(pseries_cpu_hotplug_init); -- cgit v0.10.2 From 04da6af960194ecdee4c29cd3f86e766903418ca Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Tue, 5 Dec 2006 17:52:37 +1100 Subject: [POWERPC] Move pSeries_mach_cpu_die() into platforms/pseries/hotplug-cpu.c Move pSeries_mach_cpu_die() into platforms/pseries/hotplug-cpu.c, this allows rtas_stop_self() to be static so remove the prototype. Wire up pSeries_mach_cpu_die() in the initcall, rather than statically in setup.c, the initcall will still run prior to the cpu hotplug code being callable, so there should be no change in behaviour. Signed-off-by: Michael Ellerman Signed-off-by: Paul Mackerras diff --git a/arch/powerpc/platforms/pseries/hotplug-cpu.c b/arch/powerpc/platforms/pseries/hotplug-cpu.c index 6e21ebd..9e9b6b1 100644 --- a/arch/powerpc/platforms/pseries/hotplug-cpu.c +++ b/arch/powerpc/platforms/pseries/hotplug-cpu.c @@ -1,7 +1,8 @@ /* * pseries CPU Hotplug infrastructure. * - * Split out from arch/powerpc/kernel/rtas.c + * Split out from arch/powerpc/platforms/pseries/setup.c and + * arch/powerpc/kernel/rtas.c * * Peter Bergner, IBM March 2001. * Copyright (C) 2001 IBM. @@ -34,7 +35,7 @@ static struct rtas_args rtas_stop_self_args = { .rets = &rtas_stop_self_args.args[0], }; -void rtas_stop_self(void) +static void rtas_stop_self(void) { struct rtas_args *args = &rtas_stop_self_args; @@ -49,10 +50,23 @@ void rtas_stop_self(void) panic("Alas, I survived.\n"); } +static void pSeries_mach_cpu_die(void) +{ + local_irq_disable(); + idle_task_exit(); + xics_teardown_cpu(0); + rtas_stop_self(); + /* Should never get here... */ + BUG(); + for(;;); +} + static int __init pseries_cpu_hotplug_init(void) { rtas_stop_self_args.token = rtas_token("stop-self"); + ppc_md.cpu_die = pSeries_mach_cpu_die; + return 0; } arch_initcall(pseries_cpu_hotplug_init); diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index 0dc2548..6090d75 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -347,21 +347,6 @@ static int __init pSeries_init_panel(void) } arch_initcall(pSeries_init_panel); -#ifdef CONFIG_HOTPLUG_CPU -static void pSeries_mach_cpu_die(void) -{ - local_irq_disable(); - idle_task_exit(); - xics_teardown_cpu(0); - rtas_stop_self(); - /* Should never get here... */ - BUG(); - for(;;); -} -#else -#define pSeries_mach_cpu_die NULL -#endif - static int pseries_set_dabr(unsigned long dabr) { return plpar_hcall_norets(H_SET_DABR, dabr); @@ -561,7 +546,6 @@ define_machine(pseries) { .power_off = rtas_power_off, .halt = rtas_halt, .panic = rtas_os_term, - .cpu_die = pSeries_mach_cpu_die, .get_boot_time = rtas_get_boot_time, .get_rtc_time = rtas_get_rtc_time, .set_rtc_time = rtas_set_rtc_time, diff --git a/include/asm-powerpc/rtas.h b/include/asm-powerpc/rtas.h index 5a0c136..031ef57 100644 --- a/include/asm-powerpc/rtas.h +++ b/include/asm-powerpc/rtas.h @@ -221,8 +221,6 @@ extern int rtas_get_error_log_max(void); extern spinlock_t rtas_data_buf_lock; extern char rtas_data_buf[RTAS_DATA_BUF_SIZE]; -extern void rtas_stop_self(void); - /* RMO buffer reserved for user-space RTAS use */ extern unsigned long rtas_rmo_buf; -- cgit v0.10.2 From 413f7c405a342b0b9370ea7a652b9f0270183bf3 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Tue, 5 Dec 2006 17:52:38 +1100 Subject: [POWERPC] Move the rest of the hotplug cpu code into platforms/pseries/hotplug-cpu.c Move the rest of the hotplug cpu code from platforms/pseries/smp.c into platforms/pseries/hotplug-cpu.c. Wire up the smp_ops callbacks and the notifier in the hotplug cpu initcall, rather than in smp_init_pseries(). No change in behaviour. Signed-off-by: Michael Ellerman Signed-off-by: Paul Mackerras diff --git a/arch/powerpc/platforms/pseries/hotplug-cpu.c b/arch/powerpc/platforms/pseries/hotplug-cpu.c index 9e9b6b1..12864d7 100644 --- a/arch/powerpc/platforms/pseries/hotplug-cpu.c +++ b/arch/powerpc/platforms/pseries/hotplug-cpu.c @@ -1,11 +1,14 @@ /* * pseries CPU Hotplug infrastructure. * - * Split out from arch/powerpc/platforms/pseries/setup.c and - * arch/powerpc/kernel/rtas.c + * Split out from arch/powerpc/platforms/pseries/setup.c + * arch/powerpc/kernel/rtas.c, and arch/powerpc/platforms/pseries/smp.c * * Peter Bergner, IBM March 2001. * Copyright (C) 2001 IBM. + * Dave Engebretsen, Peter Bergner, and + * Mike Corrigan {engebret|bergner|mikec}@us.ibm.com + * Plus various changes from other IBM teams... * * Copyright (C) 2006 Michael Ellerman, IBM Corporation * @@ -61,12 +64,206 @@ static void pSeries_mach_cpu_die(void) for(;;); } +/* Get state of physical CPU. + * Return codes: + * 0 - The processor is in the RTAS stopped state + * 1 - stop-self is in progress + * 2 - The processor is not in the RTAS stopped state + * -1 - Hardware Error + * -2 - Hardware Busy, Try again later. + */ +static int query_cpu_stopped(unsigned int pcpu) +{ + int cpu_status; + int status, qcss_tok; + + qcss_tok = rtas_token("query-cpu-stopped-state"); + if (qcss_tok == RTAS_UNKNOWN_SERVICE) + return -1; + status = rtas_call(qcss_tok, 1, 2, &cpu_status, pcpu); + if (status != 0) { + printk(KERN_ERR + "RTAS query-cpu-stopped-state failed: %i\n", status); + return status; + } + + return cpu_status; +} + +static int pSeries_cpu_disable(void) +{ + int cpu = smp_processor_id(); + + cpu_clear(cpu, cpu_online_map); + vdso_data->processorCount--; + + /*fix boot_cpuid here*/ + if (cpu == boot_cpuid) + boot_cpuid = any_online_cpu(cpu_online_map); + + /* FIXME: abstract this to not be platform specific later on */ + xics_migrate_irqs_away(); + return 0; +} + +static void pSeries_cpu_die(unsigned int cpu) +{ + int tries; + int cpu_status; + unsigned int pcpu = get_hard_smp_processor_id(cpu); + + for (tries = 0; tries < 25; tries++) { + cpu_status = query_cpu_stopped(pcpu); + if (cpu_status == 0 || cpu_status == -1) + break; + msleep(200); + } + if (cpu_status != 0) { + printk("Querying DEAD? cpu %i (%i) shows %i\n", + cpu, pcpu, cpu_status); + } + + /* Isolation and deallocation are definatly done by + * drslot_chrp_cpu. If they were not they would be + * done here. Change isolate state to Isolate and + * change allocation-state to Unusable. + */ + paca[cpu].cpu_start = 0; +} + +/* + * Update cpu_present_map and paca(s) for a new cpu node. The wrinkle + * here is that a cpu device node may represent up to two logical cpus + * in the SMT case. We must honor the assumption in other code that + * the logical ids for sibling SMT threads x and y are adjacent, such + * that x^1 == y and y^1 == x. + */ +static int pSeries_add_processor(struct device_node *np) +{ + unsigned int cpu; + cpumask_t candidate_map, tmp = CPU_MASK_NONE; + int err = -ENOSPC, len, nthreads, i; + const u32 *intserv; + + intserv = get_property(np, "ibm,ppc-interrupt-server#s", &len); + if (!intserv) + return 0; + + nthreads = len / sizeof(u32); + for (i = 0; i < nthreads; i++) + cpu_set(i, tmp); + + lock_cpu_hotplug(); + + BUG_ON(!cpus_subset(cpu_present_map, cpu_possible_map)); + + /* Get a bitmap of unoccupied slots. */ + cpus_xor(candidate_map, cpu_possible_map, cpu_present_map); + if (cpus_empty(candidate_map)) { + /* If we get here, it most likely means that NR_CPUS is + * less than the partition's max processors setting. + */ + printk(KERN_ERR "Cannot add cpu %s; this system configuration" + " supports %d logical cpus.\n", np->full_name, + cpus_weight(cpu_possible_map)); + goto out_unlock; + } + + while (!cpus_empty(tmp)) + if (cpus_subset(tmp, candidate_map)) + /* Found a range where we can insert the new cpu(s) */ + break; + else + cpus_shift_left(tmp, tmp, nthreads); + + if (cpus_empty(tmp)) { + printk(KERN_ERR "Unable to find space in cpu_present_map for" + " processor %s with %d thread(s)\n", np->name, + nthreads); + goto out_unlock; + } + + for_each_cpu_mask(cpu, tmp) { + BUG_ON(cpu_isset(cpu, cpu_present_map)); + cpu_set(cpu, cpu_present_map); + set_hard_smp_processor_id(cpu, *intserv++); + } + err = 0; +out_unlock: + unlock_cpu_hotplug(); + return err; +} + +/* + * Update the present map for a cpu node which is going away, and set + * the hard id in the paca(s) to -1 to be consistent with boot time + * convention for non-present cpus. + */ +static void pSeries_remove_processor(struct device_node *np) +{ + unsigned int cpu; + int len, nthreads, i; + const u32 *intserv; + + intserv = get_property(np, "ibm,ppc-interrupt-server#s", &len); + if (!intserv) + return; + + nthreads = len / sizeof(u32); + + lock_cpu_hotplug(); + for (i = 0; i < nthreads; i++) { + for_each_present_cpu(cpu) { + if (get_hard_smp_processor_id(cpu) != intserv[i]) + continue; + BUG_ON(cpu_online(cpu)); + cpu_clear(cpu, cpu_present_map); + set_hard_smp_processor_id(cpu, -1); + break; + } + if (cpu == NR_CPUS) + printk(KERN_WARNING "Could not find cpu to remove " + "with physical id 0x%x\n", intserv[i]); + } + unlock_cpu_hotplug(); +} + +static int pSeries_smp_notifier(struct notifier_block *nb, unsigned long action, void *node) +{ + int err = NOTIFY_OK; + + switch (action) { + case PSERIES_RECONFIG_ADD: + if (pSeries_add_processor(node)) + err = NOTIFY_BAD; + break; + case PSERIES_RECONFIG_REMOVE: + pSeries_remove_processor(node); + break; + default: + err = NOTIFY_DONE; + break; + } + return err; +} + +static struct notifier_block pSeries_smp_nb = { + .notifier_call = pSeries_smp_notifier, +}; + static int __init pseries_cpu_hotplug_init(void) { rtas_stop_self_args.token = rtas_token("stop-self"); ppc_md.cpu_die = pSeries_mach_cpu_die; + smp_ops->cpu_disable = pSeries_cpu_disable; + smp_ops->cpu_die = pSeries_cpu_die; + + /* Processors can be added/removed only on LPAR */ + if (firmware_has_feature(FW_FEATURE_LPAR)) + pSeries_reconfig_notifier_register(&pSeries_smp_nb); + return 0; } arch_initcall(pseries_cpu_hotplug_init); diff --git a/arch/powerpc/platforms/pseries/smp.c b/arch/powerpc/platforms/pseries/smp.c index c6624b8..4408518 100644 --- a/arch/powerpc/platforms/pseries/smp.c +++ b/arch/powerpc/platforms/pseries/smp.c @@ -64,197 +64,6 @@ static cpumask_t of_spin_map; extern void generic_secondary_smp_init(unsigned long); -#ifdef CONFIG_HOTPLUG_CPU - -/* Get state of physical CPU. - * Return codes: - * 0 - The processor is in the RTAS stopped state - * 1 - stop-self is in progress - * 2 - The processor is not in the RTAS stopped state - * -1 - Hardware Error - * -2 - Hardware Busy, Try again later. - */ -static int query_cpu_stopped(unsigned int pcpu) -{ - int cpu_status; - int status, qcss_tok; - - qcss_tok = rtas_token("query-cpu-stopped-state"); - if (qcss_tok == RTAS_UNKNOWN_SERVICE) - return -1; - status = rtas_call(qcss_tok, 1, 2, &cpu_status, pcpu); - if (status != 0) { - printk(KERN_ERR - "RTAS query-cpu-stopped-state failed: %i\n", status); - return status; - } - - return cpu_status; -} - -static int pSeries_cpu_disable(void) -{ - int cpu = smp_processor_id(); - - cpu_clear(cpu, cpu_online_map); - vdso_data->processorCount--; - - /*fix boot_cpuid here*/ - if (cpu == boot_cpuid) - boot_cpuid = any_online_cpu(cpu_online_map); - - /* FIXME: abstract this to not be platform specific later on */ - xics_migrate_irqs_away(); - return 0; -} - -static void pSeries_cpu_die(unsigned int cpu) -{ - int tries; - int cpu_status; - unsigned int pcpu = get_hard_smp_processor_id(cpu); - - for (tries = 0; tries < 25; tries++) { - cpu_status = query_cpu_stopped(pcpu); - if (cpu_status == 0 || cpu_status == -1) - break; - msleep(200); - } - if (cpu_status != 0) { - printk("Querying DEAD? cpu %i (%i) shows %i\n", - cpu, pcpu, cpu_status); - } - - /* Isolation and deallocation are definatly done by - * drslot_chrp_cpu. If they were not they would be - * done here. Change isolate state to Isolate and - * change allocation-state to Unusable. - */ - paca[cpu].cpu_start = 0; -} - -/* - * Update cpu_present_map and paca(s) for a new cpu node. The wrinkle - * here is that a cpu device node may represent up to two logical cpus - * in the SMT case. We must honor the assumption in other code that - * the logical ids for sibling SMT threads x and y are adjacent, such - * that x^1 == y and y^1 == x. - */ -static int pSeries_add_processor(struct device_node *np) -{ - unsigned int cpu; - cpumask_t candidate_map, tmp = CPU_MASK_NONE; - int err = -ENOSPC, len, nthreads, i; - const u32 *intserv; - - intserv = get_property(np, "ibm,ppc-interrupt-server#s", &len); - if (!intserv) - return 0; - - nthreads = len / sizeof(u32); - for (i = 0; i < nthreads; i++) - cpu_set(i, tmp); - - lock_cpu_hotplug(); - - BUG_ON(!cpus_subset(cpu_present_map, cpu_possible_map)); - - /* Get a bitmap of unoccupied slots. */ - cpus_xor(candidate_map, cpu_possible_map, cpu_present_map); - if (cpus_empty(candidate_map)) { - /* If we get here, it most likely means that NR_CPUS is - * less than the partition's max processors setting. - */ - printk(KERN_ERR "Cannot add cpu %s; this system configuration" - " supports %d logical cpus.\n", np->full_name, - cpus_weight(cpu_possible_map)); - goto out_unlock; - } - - while (!cpus_empty(tmp)) - if (cpus_subset(tmp, candidate_map)) - /* Found a range where we can insert the new cpu(s) */ - break; - else - cpus_shift_left(tmp, tmp, nthreads); - - if (cpus_empty(tmp)) { - printk(KERN_ERR "Unable to find space in cpu_present_map for" - " processor %s with %d thread(s)\n", np->name, - nthreads); - goto out_unlock; - } - - for_each_cpu_mask(cpu, tmp) { - BUG_ON(cpu_isset(cpu, cpu_present_map)); - cpu_set(cpu, cpu_present_map); - set_hard_smp_processor_id(cpu, *intserv++); - } - err = 0; -out_unlock: - unlock_cpu_hotplug(); - return err; -} - -/* - * Update the present map for a cpu node which is going away, and set - * the hard id in the paca(s) to -1 to be consistent with boot time - * convention for non-present cpus. - */ -static void pSeries_remove_processor(struct device_node *np) -{ - unsigned int cpu; - int len, nthreads, i; - const u32 *intserv; - - intserv = get_property(np, "ibm,ppc-interrupt-server#s", &len); - if (!intserv) - return; - - nthreads = len / sizeof(u32); - - lock_cpu_hotplug(); - for (i = 0; i < nthreads; i++) { - for_each_present_cpu(cpu) { - if (get_hard_smp_processor_id(cpu) != intserv[i]) - continue; - BUG_ON(cpu_online(cpu)); - cpu_clear(cpu, cpu_present_map); - set_hard_smp_processor_id(cpu, -1); - break; - } - if (cpu == NR_CPUS) - printk(KERN_WARNING "Could not find cpu to remove " - "with physical id 0x%x\n", intserv[i]); - } - unlock_cpu_hotplug(); -} - -static int pSeries_smp_notifier(struct notifier_block *nb, unsigned long action, void *node) -{ - int err = NOTIFY_OK; - - switch (action) { - case PSERIES_RECONFIG_ADD: - if (pSeries_add_processor(node)) - err = NOTIFY_BAD; - break; - case PSERIES_RECONFIG_REMOVE: - pSeries_remove_processor(node); - break; - default: - err = NOTIFY_DONE; - break; - } - return err; -} - -static struct notifier_block pSeries_smp_nb = { - .notifier_call = pSeries_smp_notifier, -}; - -#endif /* CONFIG_HOTPLUG_CPU */ - /** * smp_startup_cpu() - start the given cpu * @@ -422,15 +231,6 @@ static void __init smp_init_pseries(void) DBG(" -> smp_init_pSeries()\n"); -#ifdef CONFIG_HOTPLUG_CPU - smp_ops->cpu_disable = pSeries_cpu_disable; - smp_ops->cpu_die = pSeries_cpu_die; - - /* Processors can be added/removed only on LPAR */ - if (firmware_has_feature(FW_FEATURE_LPAR)) - pSeries_reconfig_notifier_register(&pSeries_smp_nb); -#endif - /* Mark threads which are still spinning in hold loops. */ if (cpu_has_feature(CPU_FTR_SMT)) { for_each_present_cpu(i) { -- cgit v0.10.2 From 674fa677c01ad0b90237f5cddc8d68502fea5156 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Tue, 5 Dec 2006 17:52:38 +1100 Subject: [POWERPC] Only enable cpu hotplug via RTAS if the required firmware support is found To support cpu hotplug on pseries we require two RTAS tokens. The cpu hotplug machinery should only be wired up if these tokens are found in the device tree. Signed-off-by: Michael Ellerman Signed-off-by: Paul Mackerras diff --git a/arch/powerpc/platforms/pseries/hotplug-cpu.c b/arch/powerpc/platforms/pseries/hotplug-cpu.c index 12864d7..e78e8ac 100644 --- a/arch/powerpc/platforms/pseries/hotplug-cpu.c +++ b/arch/powerpc/platforms/pseries/hotplug-cpu.c @@ -64,6 +64,8 @@ static void pSeries_mach_cpu_die(void) for(;;); } +static int qcss_tok; /* query-cpu-stopped-state token */ + /* Get state of physical CPU. * Return codes: * 0 - The processor is in the RTAS stopped state @@ -74,12 +76,8 @@ static void pSeries_mach_cpu_die(void) */ static int query_cpu_stopped(unsigned int pcpu) { - int cpu_status; - int status, qcss_tok; + int cpu_status, status; - qcss_tok = rtas_token("query-cpu-stopped-state"); - if (qcss_tok == RTAS_UNKNOWN_SERVICE) - return -1; status = rtas_call(qcss_tok, 1, 2, &cpu_status, pcpu); if (status != 0) { printk(KERN_ERR @@ -254,9 +252,16 @@ static struct notifier_block pSeries_smp_nb = { static int __init pseries_cpu_hotplug_init(void) { rtas_stop_self_args.token = rtas_token("stop-self"); + qcss_tok = rtas_token("query-cpu-stopped-state"); - ppc_md.cpu_die = pSeries_mach_cpu_die; + if (rtas_stop_self_args.token == RTAS_UNKNOWN_SERVICE || + qcss_tok == RTAS_UNKNOWN_SERVICE) { + printk(KERN_INFO "CPU Hotplug not supported by firmware " + "- disabling.\n"); + return 0; + } + ppc_md.cpu_die = pSeries_mach_cpu_die; smp_ops->cpu_disable = pSeries_cpu_disable; smp_ops->cpu_die = pSeries_cpu_die; -- cgit v0.10.2 From 06ba30b6bfb30c84b70fed681b8c920dbff5d5a4 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Tue, 5 Dec 2006 17:52:39 +1100 Subject: [POWERPC] Cleanup pass over platforms/pseries/hotplug-cpu.c Purely cosmetic. Change pSeries to pseries inline with other parts of the kernel, and fix an overly long line. Signed-off-by: Michael Ellerman Signed-off-by: Paul Mackerras diff --git a/arch/powerpc/platforms/pseries/hotplug-cpu.c b/arch/powerpc/platforms/pseries/hotplug-cpu.c index e78e8ac..f460b9c 100644 --- a/arch/powerpc/platforms/pseries/hotplug-cpu.c +++ b/arch/powerpc/platforms/pseries/hotplug-cpu.c @@ -53,7 +53,7 @@ static void rtas_stop_self(void) panic("Alas, I survived.\n"); } -static void pSeries_mach_cpu_die(void) +static void pseries_mach_cpu_die(void) { local_irq_disable(); idle_task_exit(); @@ -88,7 +88,7 @@ static int query_cpu_stopped(unsigned int pcpu) return cpu_status; } -static int pSeries_cpu_disable(void) +static int pseries_cpu_disable(void) { int cpu = smp_processor_id(); @@ -104,7 +104,7 @@ static int pSeries_cpu_disable(void) return 0; } -static void pSeries_cpu_die(unsigned int cpu) +static void pseries_cpu_die(unsigned int cpu) { int tries; int cpu_status; @@ -136,7 +136,7 @@ static void pSeries_cpu_die(unsigned int cpu) * the logical ids for sibling SMT threads x and y are adjacent, such * that x^1 == y and y^1 == x. */ -static int pSeries_add_processor(struct device_node *np) +static int pseries_add_processor(struct device_node *np) { unsigned int cpu; cpumask_t candidate_map, tmp = CPU_MASK_NONE; @@ -197,7 +197,7 @@ out_unlock: * the hard id in the paca(s) to -1 to be consistent with boot time * convention for non-present cpus. */ -static void pSeries_remove_processor(struct device_node *np) +static void pseries_remove_processor(struct device_node *np) { unsigned int cpu; int len, nthreads, i; @@ -226,17 +226,18 @@ static void pSeries_remove_processor(struct device_node *np) unlock_cpu_hotplug(); } -static int pSeries_smp_notifier(struct notifier_block *nb, unsigned long action, void *node) +static int pseries_smp_notifier(struct notifier_block *nb, + unsigned long action, void *node) { int err = NOTIFY_OK; switch (action) { case PSERIES_RECONFIG_ADD: - if (pSeries_add_processor(node)) + if (pseries_add_processor(node)) err = NOTIFY_BAD; break; case PSERIES_RECONFIG_REMOVE: - pSeries_remove_processor(node); + pseries_remove_processor(node); break; default: err = NOTIFY_DONE; @@ -245,8 +246,8 @@ static int pSeries_smp_notifier(struct notifier_block *nb, unsigned long action, return err; } -static struct notifier_block pSeries_smp_nb = { - .notifier_call = pSeries_smp_notifier, +static struct notifier_block pseries_smp_nb = { + .notifier_call = pseries_smp_notifier, }; static int __init pseries_cpu_hotplug_init(void) @@ -261,13 +262,13 @@ static int __init pseries_cpu_hotplug_init(void) return 0; } - ppc_md.cpu_die = pSeries_mach_cpu_die; - smp_ops->cpu_disable = pSeries_cpu_disable; - smp_ops->cpu_die = pSeries_cpu_die; + ppc_md.cpu_die = pseries_mach_cpu_die; + smp_ops->cpu_disable = pseries_cpu_disable; + smp_ops->cpu_die = pseries_cpu_die; /* Processors can be added/removed only on LPAR */ if (firmware_has_feature(FW_FEATURE_LPAR)) - pSeries_reconfig_notifier_register(&pSeries_smp_nb); + pSeries_reconfig_notifier_register(&pseries_smp_nb); return 0; } -- cgit v0.10.2 From be9575af7e8ec9040330f57b974e52d6921c08bb Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 5 Dec 2006 23:30:16 +0100 Subject: [POWERPC] cell: Fix spu_info.h header export It uses #ifdef __KERNEL__, so needs to be processed with unifdef. Signed-off-by: Arnd Bergann Acked-by: Geoff Levand Signed-off-by: Paul Mackerras diff --git a/include/asm-powerpc/Kbuild b/include/asm-powerpc/Kbuild index 1e63738..703970f 100644 --- a/include/asm-powerpc/Kbuild +++ b/include/asm-powerpc/Kbuild @@ -17,7 +17,6 @@ header-y += ipc.h header-y += poll.h header-y += shmparam.h header-y += sockios.h -header-y += spu_info.h header-y += ucontext.h header-y += ioctl.h header-y += linkage.h @@ -37,6 +36,7 @@ unifdef-y += posix_types.h unifdef-y += ptrace.h unifdef-y += seccomp.h unifdef-y += signal.h +unifdef-y += spu_info.h unifdef-y += termios.h unifdef-y += types.h unifdef-y += unistd.h -- cgit v0.10.2 From 885ed0fb484cc2d0a539558edf47a2a7c4fdd664 Mon Sep 17 00:00:00 2001 From: Geoff Levand Date: Tue, 5 Dec 2006 15:30:17 -0800 Subject: [POWERPC] powerpc: fix build error in rom.c Add missing include in rom.c. Fixes this build error when CONFIG_MTD=y: arch/powerpc/sysdev/rom.c:26: error: implicit declaration of function of_platform_device_create Signed-off-by: Geoff Levand Signed-off-by: Paul Mackerras diff --git a/arch/powerpc/sysdev/rom.c b/arch/powerpc/sysdev/rom.c index bf5b3f1..c855a3b 100644 --- a/arch/powerpc/sysdev/rom.c +++ b/arch/powerpc/sysdev/rom.c @@ -9,6 +9,7 @@ #include #include +#include static int __init powerpc_flash_init(void) { -- cgit v0.10.2 From 1d4454e7ce30239e67b154ae08f6d906b9737334 Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Wed, 6 Dec 2006 15:15:38 -0800 Subject: [POWERPC] Define pci_unmap_addr() et al. when CONFIG_NOT_COHERENT_CACHE=y The current PowerPC code makes pci_unmap_addr(), pci_unmap_addr_set(), and friends trivial for all 32-bit kernels. This is reasonable, since for those kernels it is true that pci_unmap_single() does not need the DMA address from the original DMA mapping -- in fact, it is a NOP. However, I recently tried the tg3 driver on a PowerPC 440SPe machine, which runs a 32-bit kernel and has non-cache-coherent PCI DMA. I found that the tg3 driver crashed in pci_dma_sync_single_for_cpu(), since for non-coherent systems, that function must invalidate the cache for the DMA address range requested, and therefore it does use the address passed in. tg3 uses a DMA address it stashes away with pci_unmap_addr_set() and retrieves with pci_unmap_addr(). Of course, since pci_unmap_addr() is defined to (0) right now, this doesn't work. It seems to me that the tg3 driver is using pci_unmap_addr() in a legitimate way -- I wouldn't want to have to teach all drivers that they should use pci_unmap_addr() if they only need the address for unmapping functions, but if they want the pci_dma_sync functions, then they have to store the DMA address without the helper macros. The right fix therefore seems to be in the definition of the macros in -- we should use the trivial versions only for 32-bit kernels for coherent systems, and the real versions for both 64-bit kernels and non-coherent systems. Signed-off-by: Roland Dreier Signed-off-by: Paul Mackerras diff --git a/include/asm-powerpc/pci.h b/include/asm-powerpc/pci.h index 16f1331..ac656ee 100644 --- a/include/asm-powerpc/pci.h +++ b/include/asm-powerpc/pci.h @@ -143,8 +143,13 @@ int pci_mmap_page_range(struct pci_dev *pdev, struct vm_area_struct *vma, /* Tell drivers/pci/proc.c that we have pci_mmap_page_range() */ #define HAVE_PCI_MMAP 1 -#ifdef CONFIG_PPC64 -/* pci_unmap_{single,page} is not a nop, thus... */ +#if defined(CONFIG_PPC64) || defined(CONFIG_NOT_COHERENT_CACHE) +/* + * For 64-bit kernels, pci_unmap_{single,page} is not a nop. + * For 32-bit non-coherent kernels, pci_dma_sync_single_for_cpu() and + * so on are not nops. + * and thus... + */ #define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME) \ dma_addr_t ADDR_NAME; #define DECLARE_PCI_UNMAP_LEN(LEN_NAME) \ @@ -158,6 +163,20 @@ int pci_mmap_page_range(struct pci_dev *pdev, struct vm_area_struct *vma, #define pci_unmap_len_set(PTR, LEN_NAME, VAL) \ (((PTR)->LEN_NAME) = (VAL)) +#else /* 32-bit && coherent */ + +/* pci_unmap_{page,single} is a nop so... */ +#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME) +#define DECLARE_PCI_UNMAP_LEN(LEN_NAME) +#define pci_unmap_addr(PTR, ADDR_NAME) (0) +#define pci_unmap_addr_set(PTR, ADDR_NAME, VAL) do { } while (0) +#define pci_unmap_len(PTR, LEN_NAME) (0) +#define pci_unmap_len_set(PTR, LEN_NAME, VAL) do { } while (0) + +#endif /* CONFIG_PPC64 || CONFIG_NOT_COHERENT_CACHE */ + +#ifdef CONFIG_PPC64 + /* The PCI address space does not equal the physical memory address * space (we have an IOMMU). The IDE and SCSI device layers use * this boolean for bounce buffer decisions. @@ -172,16 +191,8 @@ int pci_mmap_page_range(struct pci_dev *pdev, struct vm_area_struct *vma, */ #define PCI_DMA_BUS_IS_PHYS (1) -/* pci_unmap_{page,single} is a nop so... */ -#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME) -#define DECLARE_PCI_UNMAP_LEN(LEN_NAME) -#define pci_unmap_addr(PTR, ADDR_NAME) (0) -#define pci_unmap_addr_set(PTR, ADDR_NAME, VAL) do { } while (0) -#define pci_unmap_len(PTR, LEN_NAME) (0) -#define pci_unmap_len_set(PTR, LEN_NAME, VAL) do { } while (0) - #endif /* CONFIG_PPC64 */ - + extern void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, struct resource *res); diff --git a/include/asm-ppc/pci.h b/include/asm-ppc/pci.h index 11ffaaa..9d16202 100644 --- a/include/asm-ppc/pci.h +++ b/include/asm-ppc/pci.h @@ -61,6 +61,27 @@ extern unsigned long pci_bus_to_phys(unsigned int ba, int busnr); */ #define PCI_DMA_BUS_IS_PHYS (1) +#ifdef CONFIG_NOT_COHERENT_CACHE +/* + * pci_unmap_{page,single} are NOPs but pci_dma_sync_single_for_cpu() + * and so on are not, so... + */ + +#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME) \ + dma_addr_t ADDR_NAME; +#define DECLARE_PCI_UNMAP_LEN(LEN_NAME) \ + __u32 LEN_NAME; +#define pci_unmap_addr(PTR, ADDR_NAME) \ + ((PTR)->ADDR_NAME) +#define pci_unmap_addr_set(PTR, ADDR_NAME, VAL) \ + (((PTR)->ADDR_NAME) = (VAL)) +#define pci_unmap_len(PTR, LEN_NAME) \ + ((PTR)->LEN_NAME) +#define pci_unmap_len_set(PTR, LEN_NAME, VAL) \ + (((PTR)->LEN_NAME) = (VAL)) + +#else /* coherent */ + /* pci_unmap_{page,single} is a nop so... */ #define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME) #define DECLARE_PCI_UNMAP_LEN(LEN_NAME) @@ -69,6 +90,8 @@ extern unsigned long pci_bus_to_phys(unsigned int ba, int busnr); #define pci_unmap_len(PTR, LEN_NAME) (0) #define pci_unmap_len_set(PTR, LEN_NAME, VAL) do { } while (0) +#endif /* CONFIG_NOT_COHERENT_CACHE */ + #ifdef CONFIG_PCI static inline void pci_dma_burst_advice(struct pci_dev *pdev, enum pci_dma_burst_strategy *strat, -- cgit v0.10.2 From 3a1d1ac279fac16ab1b41b2868478f3085f9223c Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Tue, 5 Dec 2006 22:15:05 -0700 Subject: [POWERPC] Delete unused irq functions on powerpc The ack_irq macro is unused and conflicts with James' work to template the generic irq code. mask_irq and unmask_irq are also unused, so delete those macros too. Signed-off-by: Matthew Wilcox Signed-off-by: Paul Mackerras diff --git a/include/asm-powerpc/hw_irq.h b/include/asm-powerpc/hw_irq.h index d604863..9e4dd98 100644 --- a/include/asm-powerpc/hw_irq.h +++ b/include/asm-powerpc/hw_irq.h @@ -107,25 +107,6 @@ static inline void local_irq_save_ptr(unsigned long *flags) #endif /* CONFIG_PPC64 */ -#define mask_irq(irq) \ - ({ \ - irq_desc_t *desc = get_irq_desc(irq); \ - if (desc->chip && desc->chip->disable) \ - desc->chip->disable(irq); \ - }) -#define unmask_irq(irq) \ - ({ \ - irq_desc_t *desc = get_irq_desc(irq); \ - if (desc->chip && desc->chip->enable) \ - desc->chip->enable(irq); \ - }) -#define ack_irq(irq) \ - ({ \ - irq_desc_t *desc = get_irq_desc(irq); \ - if (desc->chip && desc->chip->ack) \ - desc->chip->ack(irq); \ - }) - /* * interrupt-retrigger: should we handle this via lost interrupts and IPIs * or should we not care like we do now ? --BenH. -- cgit v0.10.2 From d0e70341c05f6c31375530e0ae29b319153004a7 Mon Sep 17 00:00:00 2001 From: Linas Vepstas Date: Wed, 6 Dec 2006 12:32:20 -0600 Subject: [POWERPC] EEH recovery tweaks If one attempts to create a device driver recovery sequence that does not depend on a hard reset of the device, but simply just attempts to resume processing, then one discovers that the recovery sequence implemented on powerpc is not quite right. This patch fixes this up. Signed-off-by: Linas Vepstas Signed-off-by: Paul Mackerras diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c index 3c2d63e..da6e536 100644 --- a/arch/powerpc/platforms/pseries/eeh.c +++ b/arch/powerpc/platforms/pseries/eeh.c @@ -337,6 +337,7 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev) printk (KERN_ERR "EEH: Device driver ignored %d bad reads, panicing\n", pdn->eeh_check_count); dump_stack(); + msleep(5000); /* re-read the slot reset state */ if (read_slot_reset_state(pdn, rets) != 0) diff --git a/arch/powerpc/platforms/pseries/eeh_driver.c b/arch/powerpc/platforms/pseries/eeh_driver.c index c2bc990..cbd6b07 100644 --- a/arch/powerpc/platforms/pseries/eeh_driver.c +++ b/arch/powerpc/platforms/pseries/eeh_driver.c @@ -170,14 +170,19 @@ static void eeh_report_reset(struct pci_dev *dev, void *userdata) static void eeh_report_resume(struct pci_dev *dev, void *userdata) { struct pci_driver *driver = dev->driver; + struct device_node *dn = pci_device_to_OF_node(dev); dev->error_state = pci_channel_io_normal; if (!driver) return; - if (!driver->err_handler) - return; - if (!driver->err_handler->resume) + + if ((PCI_DN(dn)->eeh_mode) & EEH_MODE_IRQ_DISABLED) { + PCI_DN(dn)->eeh_mode &= ~EEH_MODE_IRQ_DISABLED; + enable_irq(dev->irq); + } + if (!driver->err_handler || + !driver->err_handler->resume) return; driver->err_handler->resume(dev); @@ -407,6 +412,8 @@ struct pci_dn * handle_eeh_events (struct eeh_event *event) if (rc) result = PCI_ERS_RESULT_NEED_RESET; + else + result = PCI_ERS_RESULT_RECOVERED; } /* If any device has a hard failure, then shut off everything. */ -- cgit v0.10.2 From 2c1d2f34a03ef0a89ff57da18b52fda9e6f09a10 Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Wed, 6 Dec 2006 15:16:24 -0600 Subject: [POWERPC] qe_ic: Do a sync when masking interrupts This patch causes a sync do be done after masking a QE interrupt, to ensure that the masking has completed before interrupts are enabled. This allows the masking of the cascade IRQ to be removed without causing spurious interrupts. The mask_and_ack function is also removed and set to the mask function, as the two are identical. Signed-off-by: Scott Wood Signed-off-by: Paul Mackerras diff --git a/arch/powerpc/sysdev/qe_lib/qe_ic.c b/arch/powerpc/sysdev/qe_lib/qe_ic.c index 6995f51..74e48d9 100644 --- a/arch/powerpc/sysdev/qe_lib/qe_ic.c +++ b/arch/powerpc/sysdev/qe_lib/qe_ic.c @@ -223,23 +223,15 @@ static void qe_ic_mask_irq(unsigned int virq) qe_ic_write(qe_ic->regs, qe_ic_info[src].mask_reg, temp & ~qe_ic_info[src].mask); - spin_unlock_irqrestore(&qe_ic_lock, flags); -} - -static void qe_ic_mask_irq_and_ack(unsigned int virq) -{ - struct qe_ic *qe_ic = qe_ic_from_irq(virq); - unsigned int src = virq_to_hw(virq); - unsigned long flags; - u32 temp; - - spin_lock_irqsave(&qe_ic_lock, flags); - - temp = qe_ic_read(qe_ic->regs, qe_ic_info[src].mask_reg); - qe_ic_write(qe_ic->regs, qe_ic_info[src].mask_reg, - temp & ~qe_ic_info[src].mask); - - /* There is nothing to do for ack here, ack is handled in ISR */ + /* Flush the above write before enabling interrupts; otherwise, + * spurious interrupts will sometimes happen. To be 100% sure + * that the write has reached the device before interrupts are + * enabled, the mask register would have to be read back; however, + * this is not required for correctness, only to avoid wasting + * time on a large number of spurious interrupts. In testing, + * a sync reduced the observed spurious interrupts to zero. + */ + mb(); spin_unlock_irqrestore(&qe_ic_lock, flags); } @@ -248,7 +240,7 @@ static struct irq_chip qe_ic_irq_chip = { .typename = " QEIC ", .unmask = qe_ic_unmask_irq, .mask = qe_ic_mask_irq, - .mask_ack = qe_ic_mask_irq_and_ack, + .mask_ack = qe_ic_mask_irq, }; static int qe_ic_host_match(struct irq_host *h, struct device_node *node) @@ -331,34 +323,22 @@ unsigned int qe_ic_get_high_irq(struct qe_ic *qe_ic) return irq_linear_revmap(qe_ic->irqhost, irq); } -/* FIXME: We mask all the QE Low interrupts while handling. We should - * let other interrupt come in, but BAD interrupts are generated */ void fastcall qe_ic_cascade_low(unsigned int irq, struct irq_desc *desc) { struct qe_ic *qe_ic = desc->handler_data; - struct irq_chip *chip = irq_desc[irq].chip; - unsigned int cascade_irq = qe_ic_get_low_irq(qe_ic); - chip->mask_ack(irq); if (cascade_irq != NO_IRQ) generic_handle_irq(cascade_irq); - chip->unmask(irq); } -/* FIXME: We mask all the QE High interrupts while handling. We should - * let other interrupt come in, but BAD interrupts are generated */ void fastcall qe_ic_cascade_high(unsigned int irq, struct irq_desc *desc) { struct qe_ic *qe_ic = desc->handler_data; - struct irq_chip *chip = irq_desc[irq].chip; - unsigned int cascade_irq = qe_ic_get_high_irq(qe_ic); - chip->mask_ack(irq); if (cascade_irq != NO_IRQ) generic_handle_irq(cascade_irq); - chip->unmask(irq); } void __init qe_ic_init(struct device_node *node, unsigned int flags) -- cgit v0.10.2 From 17877116c6b0fa78501526e7ec03cabe967a3a72 Mon Sep 17 00:00:00 2001 From: Nathan Lynch Date: Wed, 6 Dec 2006 18:50:43 -0600 Subject: [POWERPC] maple: Match "pcie" name for CPC945 Some firmwares have "pcie" for the "name" property of the CPC945 PCI Express host bridge. Check for "pcie" in addition to "pci" so we don't miss it. Signed-off-by: Nathan Lynch Signed-off-by: Paul Mackerras diff --git a/arch/powerpc/platforms/maple/pci.c b/arch/powerpc/platforms/maple/pci.c index 3a32ded..3f6a69f 100644 --- a/arch/powerpc/platforms/maple/pci.c +++ b/arch/powerpc/platforms/maple/pci.c @@ -562,7 +562,7 @@ void __init maple_pci_init(void) for (np = NULL; (np = of_get_next_child(root, np)) != NULL;) { if (np->name == NULL) continue; - if (strcmp(np->name, "pci") == 0) { + if (!strcmp(np->name, "pci") || !strcmp(np->name, "pcie")) { if (add_bridge(np) == 0) of_node_get(np); } -- cgit v0.10.2 From 9d9d868ecf93b35c3c5b0ea3d639dc85e423eb02 Mon Sep 17 00:00:00 2001 From: Nathan Lynch Date: Wed, 6 Dec 2006 18:50:44 -0600 Subject: [POWERPC] maple: Select PPC_RTAS Some systems supported by the maple platform have RTAS; make PPC_MAPLE select PPC_RTAS. Signed-off-by: Nathan Lynch Signed-off-by: Paul Mackerras diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 291c95a..f524b3b 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -470,6 +470,7 @@ config PPC_MAPLE select PPC_UDBG_16550 select PPC_970_NAP select PPC_NATIVE + select PPC_RTAS default n help This option enables support for the Maple 970FX Evaluation Board. -- cgit v0.10.2 From f2d6d2d8bb4e9bb4aef225c149e42cac3ac3d4d0 Mon Sep 17 00:00:00 2001 From: Nathan Lynch Date: Wed, 6 Dec 2006 18:50:45 -0600 Subject: [POWERPC] Add rtas_service_present() helper To test for the existence of an RTAS function, we typically do: foo_token = rtas_token("foo"); if (foo_token == RTAS_UNKNOWN_SERVICE) return; Add a rtas_service_present method, which provides a more conventional boolean interface for testing the existence of an RTAS method. Signed-off-by: Nathan Lynch Signed-off-by: Paul Mackerras diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c index 952f4c2..76b5d7e 100644 --- a/arch/powerpc/kernel/rtas.c +++ b/arch/powerpc/kernel/rtas.c @@ -303,6 +303,12 @@ int rtas_token(const char *service) } EXPORT_SYMBOL(rtas_token); +int rtas_service_present(const char *service) +{ + return rtas_token(service) != RTAS_UNKNOWN_SERVICE; +} +EXPORT_SYMBOL(rtas_service_present); + #ifdef CONFIG_RTAS_ERROR_LOGGING /* * Return the firmware-specified size of the error log buffer diff --git a/include/asm-powerpc/rtas.h b/include/asm-powerpc/rtas.h index 031ef57..8eaa7b2 100644 --- a/include/asm-powerpc/rtas.h +++ b/include/asm-powerpc/rtas.h @@ -159,6 +159,7 @@ extern struct rtas_t rtas; extern void enter_rtas(unsigned long); extern int rtas_token(const char *service); +extern int rtas_service_present(const char *service); extern int rtas_call(int token, int, int, int *, ...); extern void rtas_restart(char *cmd); extern void rtas_power_off(void); -- cgit v0.10.2 From 9e254c45fb0385b411ad93960f3838de80895210 Mon Sep 17 00:00:00 2001 From: Nathan Lynch Date: Wed, 6 Dec 2006 18:50:46 -0600 Subject: [POWERPC] maple: Use RTAS for reboot and halt On maple, use the RTAS "system-reboot" and "power-off" methods if they are available. Signed-off-by: Nathan Lynch Signed-off-by: Paul Mackerras diff --git a/arch/powerpc/platforms/maple/setup.c b/arch/powerpc/platforms/maple/setup.c index 094989d..f12d5c6 100644 --- a/arch/powerpc/platforms/maple/setup.c +++ b/arch/powerpc/platforms/maple/setup.c @@ -60,6 +60,7 @@ #include #include #include +#include #include #include "maple.h" @@ -166,6 +167,16 @@ struct smp_ops_t maple_smp_ops = { }; #endif /* CONFIG_SMP */ +static void __init maple_use_rtas_reboot_and_halt_if_present(void) +{ + if (rtas_service_present("system-reboot") && + rtas_service_present("power-off")) { + ppc_md.restart = rtas_restart; + ppc_md.power_off = rtas_power_off; + ppc_md.halt = rtas_halt; + } +} + void __init maple_setup_arch(void) { /* init to some ~sane value until calibrate_delay() runs */ @@ -181,6 +192,7 @@ void __init maple_setup_arch(void) #ifdef CONFIG_DUMMY_CONSOLE conswitchp = &dummy_con; #endif + maple_use_rtas_reboot_and_halt_if_present(); printk(KERN_DEBUG "Using native/NAP idle loop\n"); } -- cgit v0.10.2 From 30d368430e71bbeaa52ae978b10b6f5f6267d912 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 7 Dec 2006 16:49:18 +0100 Subject: [POWERPC] of_platform_make_bus_id(): make `magic' int of_platform_make_bus_id(): Kill a compiler warning which is a real bug on PPC64 by changing `magic' to `int'. Signed-off-by: Geert Uytterhoeven Signed-off-by: Paul Mackerras diff --git a/arch/powerpc/kernel/of_platform.c b/arch/powerpc/kernel/of_platform.c index b3189d0..3002ea3 100644 --- a/arch/powerpc/kernel/of_platform.c +++ b/arch/powerpc/kernel/of_platform.c @@ -169,7 +169,7 @@ static void of_platform_make_bus_id(struct of_device *dev) char *name = dev->dev.bus_id; const u32 *reg; u64 addr; - long magic; + int magic; /* * If it's a DCR based device, use 'd' for native DCRs -- cgit v0.10.2 From 18414ec0b56783f625edb95a9499bcb0ef12d6b8 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 7 Dec 2006 11:07:32 -0600 Subject: [POWERPC] Remove QE header files from lite5200.c The MPC 5200 does not have a QUICCEngine (QE), so lite5200.c should not include the QE header files. Signed-off-by: Timur Tabi Acked-by: Grant Likely Signed-off-by: Paul Mackerras diff --git a/arch/powerpc/platforms/52xx/lite5200.c b/arch/powerpc/platforms/52xx/lite5200.c index a375c15..eaff71e 100644 --- a/arch/powerpc/platforms/52xx/lite5200.c +++ b/arch/powerpc/platforms/52xx/lite5200.c @@ -40,8 +40,6 @@ #include #include #include -#include -#include #include #include -- cgit v0.10.2 From 22b6e590478ae8757f0411cf16a24c25d8dfea86 Mon Sep 17 00:00:00 2001 From: Christian Krafft Date: Thu, 7 Dec 2006 19:01:16 +0100 Subject: [POWERPC] cbe_thermal: Fix initialization of sysfs attribute_group This patch adds NULL to the initialization of the attribute_groups. The spu_attributes and ppe_attributes arrays are arrays of pointers that need to be terminated with a NULL entry. Signed-off-by: Christian Krafft Signed-off-by: Paul Mackerras diff --git a/arch/powerpc/platforms/cell/cbe_thermal.c b/arch/powerpc/platforms/cell/cbe_thermal.c index 616a0a3..70e0d96 100644 --- a/arch/powerpc/platforms/cell/cbe_thermal.c +++ b/arch/powerpc/platforms/cell/cbe_thermal.c @@ -115,6 +115,7 @@ static struct sysdev_attribute attr_spu_temperature = { static struct attribute *spu_attributes[] = { &attr_spu_temperature.attr, + NULL, }; static struct attribute_group spu_attribute_group = { @@ -135,6 +136,7 @@ static struct sysdev_attribute attr_ppe_temperature1 = { static struct attribute *ppe_attributes[] = { &attr_ppe_temperature0.attr, &attr_ppe_temperature1.attr, + NULL, }; static struct attribute_group ppe_attribute_group = { -- cgit v0.10.2 From f09b5ce0184da6a83bac7fafda4e624629272b37 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Fri, 8 Dec 2006 16:57:49 +1100 Subject: [POWERPC] iSeries: head_64.o needs to depend on lparmap.s This dependency was inadvertantly removed in a previous patch (e73aedba562d1e7777287043afb8e46131ed402e). Signed-off-by: Stephen Rothwell Signed-off-by: Paul Mackerras diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile index 4fe53d0..d2ded19 100644 --- a/arch/powerpc/kernel/Makefile +++ b/arch/powerpc/kernel/Makefile @@ -77,6 +77,7 @@ endif ifeq ($(CONFIG_PPC_ISERIES),y) extra-y += lparmap.s +$(obj)/head_64.o: $(obj)/lparmap.s AFLAGS_head_64.o += -I$(obj) endif -- cgit v0.10.2 From 396a1a5832ae28ce2c4150f98827873cbef554f5 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 8 Dec 2006 17:14:33 +1100 Subject: [POWERPC] Fix mmap of PCI resource with hack for X The powerpc version of pci_resource_to_user() and associated hooks used by /proc/bus/pci and /sys/bus/pci mmap have been broken for some time on machines that don't have a 1:1 mapping of devices (basically on non-PowerMacs) and have PCI devices above 32 bits. This attempts to fix it as well as possible. The rule is supposed to be that pci_resource_to_user() always converts the resources back into a BAR values since that's what the /proc interface was supposed to deal with. However, for X to work on platforms where PCI MMIO is not mapped 1:1, it became a habit of platforms like powerpc to pass "fixed up" values there since X expects to be able to use values from /proc/bus/pci/devices as offsets to mmap of /dev/mem... So we keep that contraption here, causing also /sys/*/resource to expose fully absolute MMIO addresses instead of BAR values, which is ugly, but should still work as long as those are only used to calculate alignment within a page. X is still broken when built 32 bits on machines where PCI MMIO can be above 32-bit space unfortunately. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras diff --git a/arch/powerpc/kernel/pci_32.c b/arch/powerpc/kernel/pci_32.c index 2f54cd8..ab5887b 100644 --- a/arch/powerpc/kernel/pci_32.c +++ b/arch/powerpc/kernel/pci_32.c @@ -1544,7 +1544,7 @@ pci_resource_to_bus(struct pci_dev *pdev, struct resource *res) static struct resource *__pci_mmap_make_offset(struct pci_dev *dev, - unsigned long *offset, + resource_size_t *offset, enum pci_mmap_state mmap_state) { struct pci_controller *hose = pci_bus_to_hose(dev->bus->number); @@ -1556,7 +1556,9 @@ static struct resource *__pci_mmap_make_offset(struct pci_dev *dev, /* If memory, add on the PCI bridge address offset */ if (mmap_state == pci_mmap_mem) { +#if 0 /* See comment in pci_resource_to_user() for why this is disabled */ *offset += hose->pci_mem_offset; +#endif res_bit = IORESOURCE_MEM; } else { io_offset = hose->io_base_virt - (void __iomem *)_IO_BASE; @@ -1624,9 +1626,6 @@ static pgprot_t __pci_mmap_set_pgprot(struct pci_dev *dev, struct resource *rp, else prot |= _PAGE_GUARDED; - printk("PCI map for %s:%llx, prot: %lx\n", pci_name(dev), - (unsigned long long)rp->start, prot); - return __pgprot(prot); } @@ -1695,7 +1694,7 @@ int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, enum pci_mmap_state mmap_state, int write_combine) { - unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + resource_size_t offset = vma->vm_pgoff << PAGE_SHIFT; struct resource *rp; int ret; @@ -1808,22 +1807,42 @@ void pci_resource_to_user(const struct pci_dev *dev, int bar, resource_size_t *start, resource_size_t *end) { struct pci_controller *hose = pci_bus_to_hose(dev->bus->number); - unsigned long offset = 0; + resource_size_t offset = 0; if (hose == NULL) return; if (rsrc->flags & IORESOURCE_IO) - offset = (void __iomem *)_IO_BASE - hose->io_base_virt - + hose->io_base_phys; + offset = (unsigned long)hose->io_base_virt - _IO_BASE; + + /* We pass a fully fixed up address to userland for MMIO instead of + * a BAR value because X is lame and expects to be able to use that + * to pass to /dev/mem ! + * + * That means that we'll have potentially 64 bits values where some + * userland apps only expect 32 (like X itself since it thinks only + * Sparc has 64 bits MMIO) but if we don't do that, we break it on + * 32 bits CHRPs :-( + * + * Hopefully, the sysfs insterface is immune to that gunk. Once X + * has been fixed (and the fix spread enough), we can re-enable the + * 2 lines below and pass down a BAR value to userland. In that case + * we'll also have to re-enable the matching code in + * __pci_mmap_make_offset(). + * + * BenH. + */ +#if 0 + else if (rsrc->flags & IORESOURCE_MEM) + offset = hose->pci_mem_offset; +#endif - *start = rsrc->start + offset; - *end = rsrc->end + offset; + *start = rsrc->start - offset; + *end = rsrc->end - offset; } -void __init -pci_init_resource(struct resource *res, unsigned long start, unsigned long end, - int flags, char *name) +void __init pci_init_resource(struct resource *res, resource_size_t start, + resource_size_t end, int flags, char *name) { res->start = start; res->end = end; diff --git a/arch/powerpc/kernel/pci_64.c b/arch/powerpc/kernel/pci_64.c index 6fa9a0a..a6b7692 100644 --- a/arch/powerpc/kernel/pci_64.c +++ b/arch/powerpc/kernel/pci_64.c @@ -682,7 +682,7 @@ int pci_proc_domain(struct pci_bus *bus) * Returns negative error code on failure, zero on success. */ static struct resource *__pci_mmap_make_offset(struct pci_dev *dev, - unsigned long *offset, + resource_size_t *offset, enum pci_mmap_state mmap_state) { struct pci_controller *hose = pci_bus_to_host(dev->bus); @@ -694,7 +694,9 @@ static struct resource *__pci_mmap_make_offset(struct pci_dev *dev, /* If memory, add on the PCI bridge address offset */ if (mmap_state == pci_mmap_mem) { +#if 0 /* See comment in pci_resource_to_user() for why this is disabled */ *offset += hose->pci_mem_offset; +#endif res_bit = IORESOURCE_MEM; } else { io_offset = (unsigned long)hose->io_base_virt - pci_io_base; @@ -762,9 +764,6 @@ static pgprot_t __pci_mmap_set_pgprot(struct pci_dev *dev, struct resource *rp, else prot |= _PAGE_GUARDED; - printk(KERN_DEBUG "PCI map for %s:%lx, prot: %lx\n", pci_name(dev), rp->start, - prot); - return __pgprot(prot); } @@ -832,7 +831,7 @@ pgprot_t pci_phys_mem_access_prot(struct file *file, int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, enum pci_mmap_state mmap_state, int write_combine) { - unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + resource_size_t offset = vma->vm_pgoff << PAGE_SHIFT; struct resource *rp; int ret; @@ -1333,20 +1332,41 @@ EXPORT_SYMBOL(pci_read_irq_line); void pci_resource_to_user(const struct pci_dev *dev, int bar, const struct resource *rsrc, - u64 *start, u64 *end) + resource_size_t *start, resource_size_t *end) { struct pci_controller *hose = pci_bus_to_host(dev->bus); - unsigned long offset = 0; + resource_size_t offset = 0; if (hose == NULL) return; if (rsrc->flags & IORESOURCE_IO) - offset = pci_io_base - (unsigned long)hose->io_base_virt + - hose->io_base_phys; + offset = (unsigned long)hose->io_base_virt - pci_io_base; + + /* We pass a fully fixed up address to userland for MMIO instead of + * a BAR value because X is lame and expects to be able to use that + * to pass to /dev/mem ! + * + * That means that we'll have potentially 64 bits values where some + * userland apps only expect 32 (like X itself since it thinks only + * Sparc has 64 bits MMIO) but if we don't do that, we break it on + * 32 bits CHRPs :-( + * + * Hopefully, the sysfs insterface is immune to that gunk. Once X + * has been fixed (and the fix spread enough), we can re-enable the + * 2 lines below and pass down a BAR value to userland. In that case + * we'll also have to re-enable the matching code in + * __pci_mmap_make_offset(). + * + * BenH. + */ +#if 0 + else if (rsrc->flags & IORESOURCE_MEM) + offset = hose->pci_mem_offset; +#endif - *start = rsrc->start + offset; - *end = rsrc->end + offset; + *start = rsrc->start - offset; + *end = rsrc->end - offset; } struct pci_controller* pci_find_hose_for_OF_device(struct device_node* node) diff --git a/arch/ppc/kernel/pci.c b/arch/ppc/kernel/pci.c index 63808e0..5e723c4 100644 --- a/arch/ppc/kernel/pci.c +++ b/arch/ppc/kernel/pci.c @@ -879,7 +879,7 @@ pci_resource_to_bus(struct pci_dev *pdev, struct resource *res) static struct resource *__pci_mmap_make_offset(struct pci_dev *dev, - unsigned long *offset, + resource_size_t *offset, enum pci_mmap_state mmap_state) { struct pci_controller *hose = pci_bus_to_hose(dev->bus->number); @@ -891,7 +891,9 @@ static struct resource *__pci_mmap_make_offset(struct pci_dev *dev, /* If memory, add on the PCI bridge address offset */ if (mmap_state == pci_mmap_mem) { +#if 0 /* See comment in pci_resource_to_user() for why this is disabled */ *offset += hose->pci_mem_offset; +#endif res_bit = IORESOURCE_MEM; } else { io_offset = hose->io_base_virt - ___IO_BASE; @@ -1030,7 +1032,7 @@ int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, enum pci_mmap_state mmap_state, int write_combine) { - unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + resource_size_t offset = vma->vm_pgoff << PAGE_SHIFT; struct resource *rp; int ret; @@ -1132,21 +1134,42 @@ void pci_resource_to_user(const struct pci_dev *dev, int bar, resource_size_t *start, resource_size_t *end) { struct pci_controller *hose = pci_bus_to_hose(dev->bus->number); - unsigned long offset = 0; + resource_size_t offset = 0; if (hose == NULL) return; if (rsrc->flags & IORESOURCE_IO) - offset = ___IO_BASE - hose->io_base_virt + hose->io_base_phys; + offset = (unsigned long)hose->io_base_virt - _IO_BASE; + + /* We pass a fully fixed up address to userland for MMIO instead of + * a BAR value because X is lame and expects to be able to use that + * to pass to /dev/mem ! + * + * That means that we'll have potentially 64 bits values where some + * userland apps only expect 32 (like X itself since it thinks only + * Sparc has 64 bits MMIO) but if we don't do that, we break it on + * 32 bits CHRPs :-( + * + * Hopefully, the sysfs insterface is immune to that gunk. Once X + * has been fixed (and the fix spread enough), we can re-enable the + * 2 lines below and pass down a BAR value to userland. In that case + * we'll also have to re-enable the matching code in + * __pci_mmap_make_offset(). + * + * BenH. + */ +#if 0 + else if (rsrc->flags & IORESOURCE_MEM) + offset = hose->pci_mem_offset; +#endif - *start = rsrc->start + offset; - *end = rsrc->end + offset; + *start = rsrc->start - offset; + *end = rsrc->end - offset; } -void __init -pci_init_resource(struct resource *res, unsigned long start, unsigned long end, - int flags, char *name) +void __init pci_init_resource(struct resource *res, resource_size_t start, + resource_size_t end, int flags, char *name) { res->start = start; res->end = end; diff --git a/include/asm-powerpc/pci-bridge.h b/include/asm-powerpc/pci-bridge.h index 7bb7f90..cb02c9d 100644 --- a/include/asm-powerpc/pci-bridge.h +++ b/include/asm-powerpc/pci-bridge.h @@ -31,12 +31,12 @@ struct pci_controller { int last_busno; void __iomem *io_base_virt; - unsigned long io_base_phys; + resource_size_t io_base_phys; /* Some machines have a non 1:1 mapping of * the PCI memory space in the CPU bus space */ - unsigned long pci_mem_offset; + resource_size_t pci_mem_offset; unsigned long pci_io_size; struct pci_ops *ops; diff --git a/include/asm-ppc/pci-bridge.h b/include/asm-ppc/pci-bridge.h index 6c955d0..4d35b84 100644 --- a/include/asm-ppc/pci-bridge.h +++ b/include/asm-ppc/pci-bridge.h @@ -20,8 +20,8 @@ extern unsigned long pci_bus_mem_base_phys(unsigned int bus); extern struct pci_controller* pcibios_alloc_controller(void); /* Helper function for setting up resources */ -extern void pci_init_resource(struct resource *res, unsigned long start, - unsigned long end, int flags, char *name); +extern void pci_init_resource(struct resource *res, resource_size_t start, + resource_size_t end, int flags, char *name); /* Get the PCI host controller for a bus */ extern struct pci_controller* pci_bus_to_hose(int bus); @@ -50,12 +50,12 @@ struct pci_controller { int bus_offset; void __iomem *io_base_virt; - unsigned long io_base_phys; + resource_size_t io_base_phys; /* Some machines (PReP) have a non 1:1 mapping of * the PCI memory space in the CPU bus space */ - unsigned long pci_mem_offset; + resource_size_t pci_mem_offset; struct pci_ops *ops; volatile unsigned int __iomem *cfg_addr; -- cgit v0.10.2 From d91df1aaa9e4c06f8ea10d4935888c4f1976ef56 Mon Sep 17 00:00:00 2001 From: Alexey Starikovskiy Date: Thu, 7 Dec 2006 18:42:16 +0300 Subject: ACPI: ec: Allow for write semantics in any command. Check for transaction attributes, not command index to decide on event to expect. Signed-off-by: Len Brown diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index e6d4b08..348a19e 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -253,7 +253,7 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, acpi_ec_write_data(ec, *(wdata++)); } - if (command == ACPI_EC_COMMAND_WRITE) { + if (!rdata_len) { result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0); if (result) return result; -- cgit v0.10.2 From 5d57a6a55ec0bdcb952dbcd3f8ffcde8a3ee9413 Mon Sep 17 00:00:00 2001 From: Alexey Starikovskiy Date: Thu, 7 Dec 2006 18:42:16 +0300 Subject: ACPI: ec: Enable EC GPE at beginning of transaction Temporary measure until resume sequence is right. Signed-off-by: Len Brown diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 348a19e..3ffe172 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -290,6 +290,9 @@ static int acpi_ec_transaction(struct acpi_ec *ec, u8 command, } down(&ec->sem); + /* Make sure GPE is enabled before doing transaction */ + acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); + status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0); if (status) { printk(KERN_DEBUG PREFIX "read EC, IB not empty\n"); -- cgit v0.10.2 From 5c4064124a5720a2576eb4bd5b7200d70052e9b5 Mon Sep 17 00:00:00 2001 From: Alexey Starikovskiy Date: Thu, 7 Dec 2006 18:42:16 +0300 Subject: ACPI: ec: Increase timeout from 50 to 500 ms to handle old slow machines. http://bugzilla.kernel.org/show_bug.cgi?id=7466 Signed-off-by: Len Brown diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 3ffe172..46a132d 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -65,10 +65,10 @@ enum { ACPI_EC_EVENT_IBF_0, /* Input buffer empty */ }; -#define ACPI_EC_DELAY 50 /* Wait 50ms max. during EC ops */ +#define ACPI_EC_DELAY 500 /* Wait 500ms max. during EC ops */ #define ACPI_EC_UDELAY_GLK 1000 /* Wait 1ms max. to get global lock */ #define ACPI_EC_UDELAY 100 /* Poll @ 100us increments */ -#define ACPI_EC_UDELAY_COUNT 1000 /* Wait 10ms max. during EC ops */ +#define ACPI_EC_UDELAY_COUNT 1000 /* Wait 100ms max. during EC ops */ enum { EC_INTR = 1, /* Output buffer full */ -- cgit v0.10.2 From bec5a1e0604d1b829b87b4b7e85f71ccc43dda50 Mon Sep 17 00:00:00 2001 From: Alexey Starikovskiy Date: Thu, 7 Dec 2006 18:42:16 +0300 Subject: ACPI: ec: Read status register from check_status() function Signed-off-by: Len Brown diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 46a132d..94b983b 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -134,8 +134,9 @@ static inline void acpi_ec_write_data(struct acpi_ec *ec, u8 data) outb(data, ec->data_addr); } -static int acpi_ec_check_status(u8 status, u8 event) +static int acpi_ec_check_status(struct acpi_ec *ec, u8 event) { + u8 status = acpi_ec_read_status(ec); switch (event) { case ACPI_EC_EVENT_OBF_1: if (status & ACPI_EC_FLAG_OBF) @@ -158,7 +159,7 @@ static int acpi_ec_wait(struct acpi_ec *ec, u8 event) long time_left; ec->expect_event = event; - if (acpi_ec_check_status(acpi_ec_read_status(ec), event)) { + if (acpi_ec_check_status(ec, event)) { ec->expect_event = 0; return 0; } @@ -175,7 +176,7 @@ static int acpi_ec_wait(struct acpi_ec *ec, u8 event) return 0; } } - if (acpi_ec_check_status(acpi_ec_read_status(ec), event)) { + if (acpi_ec_check_status(ec, event)) { ec->expect_event = 0; return 0; } @@ -457,15 +458,15 @@ static u32 acpi_ec_gpe_handler(void *data) struct acpi_ec *ec = (struct acpi_ec *)data; acpi_clear_gpe(NULL, ec->gpe_bit, ACPI_ISR); - value = acpi_ec_read_status(ec); if (acpi_ec_mode == EC_INTR) { - if (acpi_ec_check_status(value, ec->expect_event)) { + if (acpi_ec_check_status(ec, ec->expect_event)) { ec->expect_event = 0; wake_up(&ec->wait); } } + value = acpi_ec_read_status(ec); if (value & ACPI_EC_FLAG_SCI) { status = acpi_os_execute(OSL_EC_BURST_HANDLER, acpi_ec_gpe_query, ec); return status == AE_OK ? -- cgit v0.10.2 From af3fd1404fd4f0f58ebbb52b22be4f1ca0794cda Mon Sep 17 00:00:00 2001 From: Alexey Starikovskiy Date: Thu, 7 Dec 2006 18:42:16 +0300 Subject: ACPI: ec: Remove expect_event and all races around it. Signed-off-by: Len Brown diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 94b983b..1514e71 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -46,6 +46,9 @@ ACPI_MODULE_NAME("acpi_ec") #define ACPI_EC_DEVICE_NAME "Embedded Controller" #define ACPI_EC_FILE_INFO "info" +#undef PREFIX +#define PREFIX "ACPI: EC: " + /* EC status register */ #define ACPI_EC_FLAG_OBF 0x01 /* Output buffer full */ #define ACPI_EC_FLAG_IBF 0x02 /* Input buffer full */ @@ -101,7 +104,6 @@ struct acpi_ec { unsigned long data_addr; unsigned long global_lock; struct semaphore sem; - unsigned int expect_event; atomic_t leaving_burst; /* 0 : No, 1 : Yes, 2: abort */ wait_queue_head_t wait; } *ec_ecdt; @@ -155,34 +157,25 @@ static int acpi_ec_check_status(struct acpi_ec *ec, u8 event) static int acpi_ec_wait(struct acpi_ec *ec, u8 event) { - int i = (acpi_ec_mode == EC_POLL) ? ACPI_EC_UDELAY_COUNT : 0; - long time_left; - - ec->expect_event = event; - if (acpi_ec_check_status(ec, event)) { - ec->expect_event = 0; - return 0; - } - - do { - if (acpi_ec_mode == EC_POLL) { - udelay(ACPI_EC_UDELAY); - } else { - time_left = wait_event_timeout(ec->wait, - !ec->expect_event, - msecs_to_jiffies(ACPI_EC_DELAY)); - if (time_left > 0) { - ec->expect_event = 0; + if (acpi_ec_mode == EC_POLL) { + int i; + for (i = 0; i < ACPI_EC_UDELAY_COUNT; ++i) { + if (acpi_ec_check_status(ec, event)) return 0; - } + udelay(ACPI_EC_UDELAY); } - if (acpi_ec_check_status(ec, event)) { - ec->expect_event = 0; + } else { + if (wait_event_timeout(ec->wait, + acpi_ec_check_status(ec, event), + msecs_to_jiffies(ACPI_EC_DELAY)) || + acpi_ec_check_status(ec, event)) { return 0; + } else { + printk(KERN_ERR PREFIX "acpi_ec_wait timeout," + " status = %d, expect_event = %d\n", + acpi_ec_read_status(ec), event); } - } while (--i > 0); - - ec->expect_event = 0; + } return -ETIME; } @@ -243,32 +236,41 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, const u8 *wdata, unsigned wdata_len, u8 *rdata, unsigned rdata_len) { - int result; + int result = 0; acpi_ec_write_cmd(ec, command); for (; wdata_len > 0; wdata_len --) { result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0); - if (result) - return result; + if (result) { + printk(KERN_ERR PREFIX "write_cmd timeout, command = %d\n", + command); + goto end; + } acpi_ec_write_data(ec, *(wdata++)); } if (!rdata_len) { result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0); - if (result) - return result; + if (result) { + printk(KERN_ERR PREFIX "finish-write timeout, command = %d\n", + command); + goto end; + } } for (; rdata_len > 0; rdata_len --) { result = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF_1); - if (result) - return result; + if (result) { + printk(KERN_ERR PREFIX "read timeout, command = %d\n", + command); + goto end; + } *(rdata++) = acpi_ec_read_data(ec); } - - return 0; + end: + return result; } static int acpi_ec_transaction(struct acpi_ec *ec, u8 command, @@ -419,11 +421,6 @@ static int acpi_ec_query(struct acpi_ec *ec, u8 *data) Event Management -------------------------------------------------------------------------- */ -struct acpi_ec_query_data { - acpi_handle handle; - u8 data; -}; - static void acpi_ec_gpe_query(void *ec_cxt) { struct acpi_ec *ec = (struct acpi_ec *)ec_cxt; @@ -443,7 +440,7 @@ static void acpi_ec_gpe_query(void *ec_cxt) snprintf(object_name, 8, "_Q%2.2X", value); - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Evaluating %s", object_name)); + printk(KERN_INFO PREFIX "evaluating %s\n", object_name); acpi_evaluate_object(ec->handle, object_name, NULL, NULL); @@ -460,17 +457,12 @@ static u32 acpi_ec_gpe_handler(void *data) acpi_clear_gpe(NULL, ec->gpe_bit, ACPI_ISR); if (acpi_ec_mode == EC_INTR) { - if (acpi_ec_check_status(ec, ec->expect_event)) { - ec->expect_event = 0; - wake_up(&ec->wait); - } + wake_up(&ec->wait); } value = acpi_ec_read_status(ec); if (value & ACPI_EC_FLAG_SCI) { status = acpi_os_execute(OSL_EC_BURST_HANDLER, acpi_ec_gpe_query, ec); - return status == AE_OK ? - ACPI_INTERRUPT_HANDLED : ACPI_INTERRUPT_NOT_HANDLED; } acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_ISR); return status == AE_OK ? -- cgit v0.10.2 From e41334c0a6ef71458f255db25f011d15099e7cca Mon Sep 17 00:00:00 2001 From: Alexey Starikovskiy Date: Thu, 7 Dec 2006 18:42:16 +0300 Subject: ACPI: ec: Remove calls to clear_gpe() and enable_gpe(), as these are handled at dispatch_gpe() level. Signed-off-by: Len Brown diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 1514e71..acfe883 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -428,24 +428,21 @@ static void acpi_ec_gpe_query(void *ec_cxt) static char object_name[8]; if (!ec) - goto end; + return; value = acpi_ec_read_status(ec); if (!(value & ACPI_EC_FLAG_SCI)) - goto end; + return; if (acpi_ec_query(ec, &value)) - goto end; + return; snprintf(object_name, 8, "_Q%2.2X", value); printk(KERN_INFO PREFIX "evaluating %s\n", object_name); acpi_evaluate_object(ec->handle, object_name, NULL, NULL); - - end: - acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); } static u32 acpi_ec_gpe_handler(void *data) @@ -454,7 +451,6 @@ static u32 acpi_ec_gpe_handler(void *data) u8 value; struct acpi_ec *ec = (struct acpi_ec *)data; - acpi_clear_gpe(NULL, ec->gpe_bit, ACPI_ISR); if (acpi_ec_mode == EC_INTR) { wake_up(&ec->wait); @@ -464,7 +460,7 @@ static u32 acpi_ec_gpe_handler(void *data) if (value & ACPI_EC_FLAG_SCI) { status = acpi_os_execute(OSL_EC_BURST_HANDLER, acpi_ec_gpe_query, ec); } - acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_ISR); + return status == AE_OK ? ACPI_INTERRUPT_HANDLED : ACPI_INTERRUPT_NOT_HANDLED; } -- cgit v0.10.2 From 5d0c288b7362ad7ee235b59352ac2a89480e4757 Mon Sep 17 00:00:00 2001 From: Alexey Starikovskiy Date: Thu, 7 Dec 2006 18:42:16 +0300 Subject: ACPI: ec: Query only single query at a time. Signed-off-by: Len Brown diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index acfe883..a85f795 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -104,6 +104,7 @@ struct acpi_ec { unsigned long data_addr; unsigned long global_lock; struct semaphore sem; + atomic_t query_pending; atomic_t leaving_burst; /* 0 : No, 1 : Yes, 2: abort */ wait_queue_head_t wait; } *ec_ecdt; @@ -257,6 +258,8 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, command); goto end; } + } else if (command == ACPI_EC_COMMAND_QUERY) { + atomic_set(&ec->query_pending, 0); } for (; rdata_len > 0; rdata_len --) { @@ -425,17 +428,9 @@ static void acpi_ec_gpe_query(void *ec_cxt) { struct acpi_ec *ec = (struct acpi_ec *)ec_cxt; u8 value = 0; - static char object_name[8]; + char object_name[8]; - if (!ec) - return; - - value = acpi_ec_read_status(ec); - - if (!(value & ACPI_EC_FLAG_SCI)) - return; - - if (acpi_ec_query(ec, &value)) + if (!ec || acpi_ec_query(ec, &value)) return; snprintf(object_name, 8, "_Q%2.2X", value); @@ -457,7 +452,8 @@ static u32 acpi_ec_gpe_handler(void *data) } value = acpi_ec_read_status(ec); - if (value & ACPI_EC_FLAG_SCI) { + if ((value & ACPI_EC_FLAG_SCI) && !atomic_read(&ec->query_pending)) { + atomic_set(&ec->query_pending, 1); status = acpi_os_execute(OSL_EC_BURST_HANDLER, acpi_ec_gpe_query, ec); } @@ -652,6 +648,7 @@ static int acpi_ec_add(struct acpi_device *device) ec->handle = device->handle; ec->uid = -1; init_MUTEX(&ec->sem); + atomic_set(&ec->query_pending, 0); if (acpi_ec_mode == EC_INTR) { atomic_set(&ec->leaving_burst, 1); init_waitqueue_head(&ec->wait); -- cgit v0.10.2 From c787a8551e7fee85366962881e7a4f2fda656dfc Mon Sep 17 00:00:00 2001 From: Alexey Starikovskiy Date: Thu, 7 Dec 2006 18:42:16 +0300 Subject: ACPI: ec: Change semaphore to mutex. Signed-off-by: Len Brown diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index a85f795..034a963 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -103,7 +103,7 @@ struct acpi_ec { unsigned long command_addr; unsigned long data_addr; unsigned long global_lock; - struct semaphore sem; + struct mutex lock; atomic_t query_pending; atomic_t leaving_burst; /* 0 : No, 1 : Yes, 2: abort */ wait_queue_head_t wait; @@ -294,7 +294,7 @@ static int acpi_ec_transaction(struct acpi_ec *ec, u8 command, if (ACPI_FAILURE(status)) return -ENODEV; } - down(&ec->sem); + mutex_lock(&ec->lock); /* Make sure GPE is enabled before doing transaction */ acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); @@ -310,7 +310,7 @@ static int acpi_ec_transaction(struct acpi_ec *ec, u8 command, rdata, rdata_len); end: - up(&ec->sem); + mutex_unlock(&ec->lock); if (ec->global_lock) acpi_release_global_lock(glk); @@ -647,7 +647,7 @@ static int acpi_ec_add(struct acpi_device *device) ec->handle = device->handle; ec->uid = -1; - init_MUTEX(&ec->sem); + mutex_init(&ec->lock); atomic_set(&ec->query_pending, 0); if (acpi_ec_mode == EC_INTR) { atomic_set(&ec->leaving_burst, 1); @@ -830,7 +830,7 @@ acpi_fake_ecdt_callback(acpi_handle handle, { acpi_status status; - init_MUTEX(&ec_ecdt->sem); + mutex_init(&ec_ecdt->lock); if (acpi_ec_mode == EC_INTR) { init_waitqueue_head(&ec_ecdt->wait); } @@ -915,7 +915,7 @@ static int __init acpi_ec_get_real_ecdt(void) return -ENOMEM; memset(ec_ecdt, 0, sizeof(struct acpi_ec)); - init_MUTEX(&ec_ecdt->sem); + mutex_init(&ec_ecdt->lock); if (acpi_ec_mode == EC_INTR) { init_waitqueue_head(&ec_ecdt->wait); } -- cgit v0.10.2 From a86e277259b08be0f00cfcb182922da3ffc50f04 Mon Sep 17 00:00:00 2001 From: Alexey Starikovskiy Date: Thu, 7 Dec 2006 18:42:16 +0300 Subject: ACPI: ec: Rename gpe_bit to gpe Signed-off-by: Len Brown diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 034a963..722acaf 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -99,7 +99,7 @@ static struct acpi_driver acpi_ec_driver = { struct acpi_ec { acpi_handle handle; unsigned long uid; - unsigned long gpe_bit; + unsigned long gpe; unsigned long command_addr; unsigned long data_addr; unsigned long global_lock; @@ -297,7 +297,7 @@ static int acpi_ec_transaction(struct acpi_ec *ec, u8 command, mutex_lock(&ec->lock); /* Make sure GPE is enabled before doing transaction */ - acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); + acpi_enable_gpe(NULL, ec->gpe, ACPI_NOT_ISR); status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0); if (status) { @@ -563,14 +563,14 @@ static int acpi_ec_read_info(struct seq_file *seq, void *offset) if (!ec) goto end; - seq_printf(seq, "gpe bit: 0x%02x\n", - (u32) ec->gpe_bit); + seq_printf(seq, "gpe: 0x%02x\n", + (u32) ec->gpe); seq_printf(seq, "ports: 0x%02x, 0x%02x\n", (u32) ec->command_addr, (u32) ec->data_addr); seq_printf(seq, "use global lock: %s\n", ec->global_lock ? "yes" : "no"); - acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); + acpi_enable_gpe(NULL, ec->gpe, ACPI_NOT_ISR); end: return 0; @@ -668,7 +668,7 @@ static int acpi_ec_add(struct acpi_device *device) ACPI_ADR_SPACE_EC, &acpi_ec_space_handler); - acpi_remove_gpe_handler(NULL, ec_ecdt->gpe_bit, + acpi_remove_gpe_handler(NULL, ec_ecdt->gpe, &acpi_ec_gpe_handler); kfree(ec_ecdt); @@ -678,7 +678,7 @@ static int acpi_ec_add(struct acpi_device *device) /* TODO: Add support for _GPE returning a package */ status = acpi_evaluate_integer(ec->handle, "_GPE", NULL, - &ec->gpe_bit); + &ec->gpe); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, "Obtaining GPE bit assignment")); result = -ENODEV; @@ -691,7 +691,7 @@ static int acpi_ec_add(struct acpi_device *device) ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s [%s] (gpe %d) interrupt mode.", acpi_device_name(device), acpi_device_bid(device), - (u32) ec->gpe_bit)); + (u32) ec->gpe)); if (!first_ec) first_ec = device; @@ -771,26 +771,26 @@ static int acpi_ec_start(struct acpi_device *device) } ACPI_DEBUG_PRINT((ACPI_DB_INFO, "gpe=0x%02lx, ports=0x%2lx,0x%2lx", - ec->gpe_bit, ec->command_addr, ec->data_addr)); + ec->gpe, ec->command_addr, ec->data_addr)); /* * Install GPE handler */ - status = acpi_install_gpe_handler(NULL, ec->gpe_bit, + status = acpi_install_gpe_handler(NULL, ec->gpe, ACPI_GPE_EDGE_TRIGGERED, &acpi_ec_gpe_handler, ec); if (ACPI_FAILURE(status)) { return -ENODEV; } - acpi_set_gpe_type(NULL, ec->gpe_bit, ACPI_GPE_TYPE_RUNTIME); - acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); + acpi_set_gpe_type(NULL, ec->gpe, ACPI_GPE_TYPE_RUNTIME); + acpi_enable_gpe(NULL, ec->gpe, ACPI_NOT_ISR); status = acpi_install_address_space_handler(ec->handle, ACPI_ADR_SPACE_EC, &acpi_ec_space_handler, &acpi_ec_space_setup, ec); if (ACPI_FAILURE(status)) { - acpi_remove_gpe_handler(NULL, ec->gpe_bit, + acpi_remove_gpe_handler(NULL, ec->gpe, &acpi_ec_gpe_handler); return -ENODEV; } @@ -816,7 +816,7 @@ static int acpi_ec_stop(struct acpi_device *device, int type) return -ENODEV; status = - acpi_remove_gpe_handler(NULL, ec->gpe_bit, + acpi_remove_gpe_handler(NULL, ec->gpe, &acpi_ec_gpe_handler); if (ACPI_FAILURE(status)) return -ENODEV; @@ -844,14 +844,14 @@ acpi_fake_ecdt_callback(acpi_handle handle, status = acpi_evaluate_integer(handle, "_GPE", NULL, - &ec_ecdt->gpe_bit); + &ec_ecdt->gpe); if (ACPI_FAILURE(status)) return status; ec_ecdt->global_lock = TRUE; ec_ecdt->handle = handle; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "GPE=0x%02lx, ports=0x%2lx, 0x%2lx", - ec_ecdt->gpe_bit, ec_ecdt->command_addr, ec_ecdt->data_addr)); + ec_ecdt->gpe, ec_ecdt->command_addr, ec_ecdt->data_addr)); return AE_CTRL_TERMINATE; } @@ -921,7 +921,7 @@ static int __init acpi_ec_get_real_ecdt(void) } ec_ecdt->command_addr = ecdt_ptr->ec_control.address; ec_ecdt->data_addr = ecdt_ptr->ec_data.address; - ec_ecdt->gpe_bit = ecdt_ptr->gpe_bit; + ec_ecdt->gpe = ecdt_ptr->gpe_bit; /* use the GL just to be safe */ ec_ecdt->global_lock = TRUE; ec_ecdt->uid = ecdt_ptr->uid; @@ -959,14 +959,14 @@ int __init acpi_ec_ecdt_probe(void) /* * Install GPE handler */ - status = acpi_install_gpe_handler(NULL, ec_ecdt->gpe_bit, + status = acpi_install_gpe_handler(NULL, ec_ecdt->gpe, ACPI_GPE_EDGE_TRIGGERED, &acpi_ec_gpe_handler, ec_ecdt); if (ACPI_FAILURE(status)) { goto error; } - acpi_set_gpe_type(NULL, ec_ecdt->gpe_bit, ACPI_GPE_TYPE_RUNTIME); - acpi_enable_gpe(NULL, ec_ecdt->gpe_bit, ACPI_NOT_ISR); + acpi_set_gpe_type(NULL, ec_ecdt->gpe, ACPI_GPE_TYPE_RUNTIME); + acpi_enable_gpe(NULL, ec_ecdt->gpe, ACPI_NOT_ISR); status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT, ACPI_ADR_SPACE_EC, @@ -974,7 +974,7 @@ int __init acpi_ec_ecdt_probe(void) &acpi_ec_space_setup, ec_ecdt); if (ACPI_FAILURE(status)) { - acpi_remove_gpe_handler(NULL, ec_ecdt->gpe_bit, + acpi_remove_gpe_handler(NULL, ec_ecdt->gpe, &acpi_ec_gpe_handler); goto error; } -- cgit v0.10.2 From 50c1e1138cb94f6aca0f8555777edbcefe0324e2 Mon Sep 17 00:00:00 2001 From: Alexey Starikovskiy Date: Thu, 7 Dec 2006 18:42:17 +0300 Subject: ACPI: ec: Drop udelay() from poll mode. Loop by reading status field instead. Signed-off-by: Len Brown diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 722acaf..8ef74e7 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -70,8 +70,6 @@ enum { #define ACPI_EC_DELAY 500 /* Wait 500ms max. during EC ops */ #define ACPI_EC_UDELAY_GLK 1000 /* Wait 1ms max. to get global lock */ -#define ACPI_EC_UDELAY 100 /* Poll @ 100us increments */ -#define ACPI_EC_UDELAY_COUNT 1000 /* Wait 100ms max. during EC ops */ enum { EC_INTR = 1, /* Output buffer full */ @@ -159,11 +157,10 @@ static int acpi_ec_check_status(struct acpi_ec *ec, u8 event) static int acpi_ec_wait(struct acpi_ec *ec, u8 event) { if (acpi_ec_mode == EC_POLL) { - int i; - for (i = 0; i < ACPI_EC_UDELAY_COUNT; ++i) { + unsigned long delay = jiffies + msecs_to_jiffies(ACPI_EC_DELAY); + while (time_before(jiffies, delay)) { if (acpi_ec_check_status(ec, event)) return 0; - udelay(ACPI_EC_UDELAY); } } else { if (wait_event_timeout(ec->wait, -- cgit v0.10.2 From 523953b41e52952347d7d50dcc4bfc27bc001dc8 Mon Sep 17 00:00:00 2001 From: Alexey Starikovskiy Date: Thu, 7 Dec 2006 18:42:17 +0300 Subject: ACPI: ec: Acquire Global Lock under EC mutex. Signed-off-by: Len Brown diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 8ef74e7..4d17777 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -286,12 +286,12 @@ static int acpi_ec_transaction(struct acpi_ec *ec, u8 command, if (rdata) memset(rdata, 0, rdata_len); + mutex_lock(&ec->lock); if (ec->global_lock) { status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk); if (ACPI_FAILURE(status)) return -ENODEV; } - mutex_lock(&ec->lock); /* Make sure GPE is enabled before doing transaction */ acpi_enable_gpe(NULL, ec->gpe, ACPI_NOT_ISR); @@ -307,10 +307,10 @@ static int acpi_ec_transaction(struct acpi_ec *ec, u8 command, rdata, rdata_len); end: - mutex_unlock(&ec->lock); if (ec->global_lock) acpi_release_global_lock(glk); + mutex_unlock(&ec->lock); return status; } -- cgit v0.10.2 From 78d0af3392cba6dfdd1dc1eab5a86ba8e4af8fff Mon Sep 17 00:00:00 2001 From: Alexey Starikovskiy Date: Thu, 7 Dec 2006 18:42:17 +0300 Subject: ACPI: ec: Style changes. Signed-off-by: Len Brown diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 4d17777..17a98aa 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -135,20 +135,16 @@ static inline void acpi_ec_write_data(struct acpi_ec *ec, u8 data) outb(data, ec->data_addr); } -static int acpi_ec_check_status(struct acpi_ec *ec, u8 event) +static inline int acpi_ec_check_status(struct acpi_ec *ec, u8 event) { u8 status = acpi_ec_read_status(ec); - switch (event) { - case ACPI_EC_EVENT_OBF_1: + + if (event == ACPI_EC_EVENT_OBF_1) { if (status & ACPI_EC_FLAG_OBF) return 1; - break; - case ACPI_EC_EVENT_IBF_0: + } else if (event == ACPI_EC_EVENT_IBF_0) { if (!(status & ACPI_EC_FLAG_IBF)) return 1; - break; - default: - break; } return 0; @@ -238,7 +234,7 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, acpi_ec_write_cmd(ec, command); - for (; wdata_len > 0; wdata_len --) { + for (; wdata_len > 0; --wdata_len) { result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0); if (result) { printk(KERN_ERR PREFIX "write_cmd timeout, command = %d\n", @@ -259,7 +255,7 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, atomic_set(&ec->query_pending, 0); } - for (; rdata_len > 0; rdata_len --) { + for (; rdata_len > 0; --rdata_len) { result = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF_1); if (result) { printk(KERN_ERR PREFIX "read timeout, command = %d\n", -- cgit v0.10.2 From 3261ff4db3a33ac7e1b9ed98e905663845cadbc6 Mon Sep 17 00:00:00 2001 From: Alexey Starikovskiy Date: Thu, 7 Dec 2006 18:42:17 +0300 Subject: ACPI: ec: Change #define to enums there possible. Signed-off-by: Len Brown diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 17a98aa..e05bb14 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -56,14 +56,15 @@ ACPI_MODULE_NAME("acpi_ec") #define ACPI_EC_FLAG_SCI 0x20 /* EC-SCI occurred */ /* EC commands */ -#define ACPI_EC_COMMAND_READ 0x80 -#define ACPI_EC_COMMAND_WRITE 0x81 -#define ACPI_EC_BURST_ENABLE 0x82 -#define ACPI_EC_BURST_DISABLE 0x83 -#define ACPI_EC_COMMAND_QUERY 0x84 - +enum ec_command { + ACPI_EC_COMMAND_READ = 0x80, + ACPI_EC_COMMAND_WRITE = 0x81, + ACPI_EC_BURST_ENABLE = 0x82, + ACPI_EC_BURST_DISABLE = 0x83, + ACPI_EC_COMMAND_QUERY = 0x84, +}; /* EC events */ -enum { +enum ec_event { ACPI_EC_EVENT_OBF_1 = 1, /* Output buffer full */ ACPI_EC_EVENT_IBF_0, /* Input buffer empty */ }; @@ -71,10 +72,10 @@ enum { #define ACPI_EC_DELAY 500 /* Wait 500ms max. during EC ops */ #define ACPI_EC_UDELAY_GLK 1000 /* Wait 1ms max. to get global lock */ -enum { +static enum ec_mode { EC_INTR = 1, /* Output buffer full */ EC_POLL, /* Input buffer empty */ -}; +} acpi_ec_mode = EC_INTR; static int acpi_ec_remove(struct acpi_device *device, int type); static int acpi_ec_start(struct acpi_device *device); @@ -109,7 +110,6 @@ struct acpi_ec { /* External interfaces use first EC only, so remember */ static struct acpi_device *first_ec; -static int acpi_ec_mode = EC_INTR; /* -------------------------------------------------------------------------- Transaction Management @@ -135,7 +135,7 @@ static inline void acpi_ec_write_data(struct acpi_ec *ec, u8 data) outb(data, ec->data_addr); } -static inline int acpi_ec_check_status(struct acpi_ec *ec, u8 event) +static inline int acpi_ec_check_status(struct acpi_ec *ec, enum ec_event event) { u8 status = acpi_ec_read_status(ec); @@ -150,7 +150,7 @@ static inline int acpi_ec_check_status(struct acpi_ec *ec, u8 event) return 0; } -static int acpi_ec_wait(struct acpi_ec *ec, u8 event) +static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event) { if (acpi_ec_mode == EC_POLL) { unsigned long delay = jiffies + msecs_to_jiffies(ACPI_EC_DELAY); -- cgit v0.10.2 From 6ccedb10e39c34a4cb68f6c8dae67ecdd3e0b138 Mon Sep 17 00:00:00 2001 From: Alexey Starikovskiy Date: Thu, 7 Dec 2006 18:42:17 +0300 Subject: ACPI: ec: Lindent once again Signed-off-by: Len Brown diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index e05bb14..d713f76 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -45,36 +45,33 @@ ACPI_MODULE_NAME("acpi_ec") #define ACPI_EC_DRIVER_NAME "ACPI Embedded Controller Driver" #define ACPI_EC_DEVICE_NAME "Embedded Controller" #define ACPI_EC_FILE_INFO "info" - #undef PREFIX #define PREFIX "ACPI: EC: " - /* EC status register */ #define ACPI_EC_FLAG_OBF 0x01 /* Output buffer full */ #define ACPI_EC_FLAG_IBF 0x02 /* Input buffer full */ #define ACPI_EC_FLAG_BURST 0x10 /* burst mode */ #define ACPI_EC_FLAG_SCI 0x20 /* EC-SCI occurred */ - /* EC commands */ enum ec_command { - ACPI_EC_COMMAND_READ = 0x80, - ACPI_EC_COMMAND_WRITE = 0x81, - ACPI_EC_BURST_ENABLE = 0x82, - ACPI_EC_BURST_DISABLE = 0x83, - ACPI_EC_COMMAND_QUERY = 0x84, + ACPI_EC_COMMAND_READ = 0x80, + ACPI_EC_COMMAND_WRITE = 0x81, + ACPI_EC_BURST_ENABLE = 0x82, + ACPI_EC_BURST_DISABLE = 0x83, + ACPI_EC_COMMAND_QUERY = 0x84, }; /* EC events */ enum ec_event { ACPI_EC_EVENT_OBF_1 = 1, /* Output buffer full */ - ACPI_EC_EVENT_IBF_0, /* Input buffer empty */ + ACPI_EC_EVENT_IBF_0, /* Input buffer empty */ }; #define ACPI_EC_DELAY 500 /* Wait 500ms max. during EC ops */ #define ACPI_EC_UDELAY_GLK 1000 /* Wait 1ms max. to get global lock */ static enum ec_mode { - EC_INTR = 1, /* Output buffer full */ - EC_POLL, /* Input buffer empty */ + EC_INTR = 1, /* Output buffer full */ + EC_POLL, /* Input buffer empty */ } acpi_ec_mode = EC_INTR; static int acpi_ec_remove(struct acpi_device *device, int type); @@ -167,7 +164,7 @@ static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event) } else { printk(KERN_ERR PREFIX "acpi_ec_wait timeout," " status = %d, expect_event = %d\n", - acpi_ec_read_status(ec), event); + acpi_ec_read_status(ec), event); } } @@ -184,7 +181,6 @@ int acpi_ec_enter_burst_mode(struct acpi_ec *ec) u8 tmp = 0; u8 status = 0; - status = acpi_ec_read_status(ec); if (status != -EINVAL && !(status & ACPI_EC_FLAG_BURST)) { status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0); @@ -200,7 +196,7 @@ int acpi_ec_enter_burst_mode(struct acpi_ec *ec) atomic_set(&ec->leaving_burst, 0); return 0; - end: + end: ACPI_EXCEPTION((AE_INFO, status, "EC wait, burst mode")); return -1; } @@ -209,26 +205,25 @@ int acpi_ec_leave_burst_mode(struct acpi_ec *ec) { u8 status = 0; - status = acpi_ec_read_status(ec); - if (status != -EINVAL && (status & ACPI_EC_FLAG_BURST)){ + if (status != -EINVAL && (status & ACPI_EC_FLAG_BURST)) { status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0); - if(status) + if (status) goto end; acpi_ec_write_cmd(ec, ACPI_EC_BURST_DISABLE); acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0); } atomic_set(&ec->leaving_burst, 1); return 0; - end: + end: ACPI_EXCEPTION((AE_INFO, status, "EC leave burst mode")); return -1; } -#endif /* ACPI_FUTURE_USAGE */ +#endif /* ACPI_FUTURE_USAGE */ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, - const u8 *wdata, unsigned wdata_len, - u8 *rdata, unsigned rdata_len) + const u8 * wdata, unsigned wdata_len, + u8 * rdata, unsigned rdata_len) { int result = 0; @@ -237,8 +232,8 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, for (; wdata_len > 0; --wdata_len) { result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0); if (result) { - printk(KERN_ERR PREFIX "write_cmd timeout, command = %d\n", - command); + printk(KERN_ERR PREFIX + "write_cmd timeout, command = %d\n", command); goto end; } acpi_ec_write_data(ec, *(wdata++)); @@ -247,8 +242,8 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, if (!rdata_len) { result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0); if (result) { - printk(KERN_ERR PREFIX "finish-write timeout, command = %d\n", - command); + printk(KERN_ERR PREFIX + "finish-write timeout, command = %d\n", command); goto end; } } else if (command == ACPI_EC_COMMAND_QUERY) { @@ -259,7 +254,7 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, result = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF_1); if (result) { printk(KERN_ERR PREFIX "read timeout, command = %d\n", - command); + command); goto end; } @@ -270,8 +265,8 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, } static int acpi_ec_transaction(struct acpi_ec *ec, u8 command, - const u8 *wdata, unsigned wdata_len, - u8 *rdata, unsigned rdata_len) + const u8 * wdata, unsigned wdata_len, + u8 * rdata, unsigned rdata_len) { int status; u32 glk; @@ -279,8 +274,8 @@ static int acpi_ec_transaction(struct acpi_ec *ec, u8 command, if (!ec || (wdata_len && !wdata) || (rdata_len && !rdata)) return -EINVAL; - if (rdata) - memset(rdata, 0, rdata_len); + if (rdata) + memset(rdata, 0, rdata_len); mutex_lock(&ec->lock); if (ec->global_lock) { @@ -294,15 +289,16 @@ static int acpi_ec_transaction(struct acpi_ec *ec, u8 command, status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0); if (status) { - printk(KERN_DEBUG PREFIX "read EC, IB not empty\n"); + printk(KERN_DEBUG PREFIX + "input buffer is not empty, aborting transaction\n"); goto end; } - status = acpi_ec_transaction_unlocked(ec, command, - wdata, wdata_len, - rdata, rdata_len); + status = acpi_ec_transaction_unlocked(ec, command, + wdata, wdata_len, + rdata, rdata_len); -end: + end: if (ec->global_lock) acpi_release_global_lock(glk); @@ -311,7 +307,7 @@ end: return status; } -static int acpi_ec_read(struct acpi_ec *ec, u8 address, u8 *data) +static int acpi_ec_read(struct acpi_ec *ec, u8 address, u8 * data) { int result; u8 d; @@ -324,15 +320,15 @@ static int acpi_ec_read(struct acpi_ec *ec, u8 address, u8 *data) static int acpi_ec_write(struct acpi_ec *ec, u8 address, u8 data) { - u8 wdata[2] = { address, data }; - return acpi_ec_transaction(ec, ACPI_EC_COMMAND_WRITE, + u8 wdata[2] = { address, data }; + return acpi_ec_transaction(ec, ACPI_EC_COMMAND_WRITE, wdata, 2, NULL, 0); } /* * Externally callable EC access functions. For now, assume 1 EC only */ -int ec_read(u8 addr, u8 *val) +int ec_read(u8 addr, u8 * val) { struct acpi_ec *ec; int err; @@ -372,8 +368,8 @@ int ec_write(u8 addr, u8 val) EXPORT_SYMBOL(ec_write); extern int ec_transaction(u8 command, - const u8 *wdata, unsigned wdata_len, - u8 *rdata, unsigned rdata_len) + const u8 * wdata, unsigned wdata_len, + u8 * rdata, unsigned rdata_len) { struct acpi_ec *ec; @@ -388,29 +384,29 @@ extern int ec_transaction(u8 command, EXPORT_SYMBOL(ec_transaction); -static int acpi_ec_query(struct acpi_ec *ec, u8 *data) +static int acpi_ec_query(struct acpi_ec *ec, u8 * data) { int result; - u8 d; + u8 d; - if (!ec || !data) - return -EINVAL; + if (!ec || !data) + return -EINVAL; - /* - * Query the EC to find out which _Qxx method we need to evaluate. - * Note that successful completion of the query causes the ACPI_EC_SCI - * bit to be cleared (and thus clearing the interrupt source). - */ + /* + * Query the EC to find out which _Qxx method we need to evaluate. + * Note that successful completion of the query causes the ACPI_EC_SCI + * bit to be cleared (and thus clearing the interrupt source). + */ - result = acpi_ec_transaction(ec, ACPI_EC_COMMAND_QUERY, NULL, 0, &d, 1); - if (result) - return result; + result = acpi_ec_transaction(ec, ACPI_EC_COMMAND_QUERY, NULL, 0, &d, 1); + if (result) + return result; - if (!d) - return -ENODATA; + if (!d) + return -ENODATA; - *data = d; - return 0; + *data = d; + return 0; } /* -------------------------------------------------------------------------- @@ -439,7 +435,6 @@ static u32 acpi_ec_gpe_handler(void *data) u8 value; struct acpi_ec *ec = (struct acpi_ec *)data; - if (acpi_ec_mode == EC_INTR) { wake_up(&ec->wait); } @@ -447,7 +442,9 @@ static u32 acpi_ec_gpe_handler(void *data) value = acpi_ec_read_status(ec); if ((value & ACPI_EC_FLAG_SCI) && !atomic_read(&ec->query_pending)) { atomic_set(&ec->query_pending, 1); - status = acpi_os_execute(OSL_EC_BURST_HANDLER, acpi_ec_gpe_query, ec); + status = + acpi_os_execute(OSL_EC_BURST_HANDLER, acpi_ec_gpe_query, + ec); } return status == AE_OK ? @@ -485,7 +482,6 @@ acpi_ec_space_handler(u32 function, acpi_integer f_v = 0; int i = 0; - if ((address > 0xFF) || !value || !handler_context) return AE_BAD_PARAMETER; @@ -499,7 +495,7 @@ acpi_ec_space_handler(u32 function, switch (function) { case ACPI_READ: temp = 0; - result = acpi_ec_read(ec, (u8) address, (u8 *) &temp); + result = acpi_ec_read(ec, (u8) address, (u8 *) & temp); break; case ACPI_WRITE: result = acpi_ec_write(ec, (u8) address, (u8) temp); @@ -552,15 +548,12 @@ static int acpi_ec_read_info(struct seq_file *seq, void *offset) { struct acpi_ec *ec = (struct acpi_ec *)seq->private; - if (!ec) goto end; - seq_printf(seq, "gpe: 0x%02x\n", - (u32) ec->gpe); + seq_printf(seq, "gpe: 0x%02x\n", (u32) ec->gpe); seq_printf(seq, "ports: 0x%02x, 0x%02x\n", - (u32) ec->command_addr, - (u32) ec->data_addr); + (u32) ec->command_addr, (u32) ec->data_addr); seq_printf(seq, "use global lock: %s\n", ec->global_lock ? "yes" : "no"); acpi_enable_gpe(NULL, ec->gpe, ACPI_NOT_ISR); @@ -586,7 +579,6 @@ static int acpi_ec_add_fs(struct acpi_device *device) { struct proc_dir_entry *entry = NULL; - if (!acpi_device_dir(device)) { acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), acpi_ec_dir); @@ -629,7 +621,6 @@ static int acpi_ec_add(struct acpi_device *device) acpi_status status = AE_OK; struct acpi_ec *ec = NULL; - if (!device) return -EINVAL; @@ -651,8 +642,7 @@ static int acpi_ec_add(struct acpi_device *device) acpi_driver_data(device) = ec; /* Use the global lock for all EC transactions? */ - acpi_evaluate_integer(ec->handle, "_GLK", NULL, - &ec->global_lock); + acpi_evaluate_integer(ec->handle, "_GLK", NULL, &ec->global_lock); /* XXX we don't test uids, because on some boxes ecdt uid = 0, see: http://bugzilla.kernel.org/show_bug.cgi?id=6111 */ @@ -669,11 +659,10 @@ static int acpi_ec_add(struct acpi_device *device) /* Get GPE bit assignment (EC events). */ /* TODO: Add support for _GPE returning a package */ - status = - acpi_evaluate_integer(ec->handle, "_GPE", NULL, - &ec->gpe); + status = acpi_evaluate_integer(ec->handle, "_GPE", NULL, &ec->gpe); if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, "Obtaining GPE bit assignment")); + ACPI_EXCEPTION((AE_INFO, status, + "Obtaining GPE bit assignment")); result = -ENODEV; goto end; } @@ -683,13 +672,13 @@ static int acpi_ec_add(struct acpi_device *device) goto end; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s [%s] (gpe %d) interrupt mode.", - acpi_device_name(device), acpi_device_bid(device), - (u32) ec->gpe)); + acpi_device_name(device), acpi_device_bid(device), + (u32) ec->gpe)); if (!first_ec) first_ec = device; - end: + end: if (result) kfree(ec); @@ -700,7 +689,6 @@ static int acpi_ec_remove(struct acpi_device *device, int type) { struct acpi_ec *ec = NULL; - if (!device) return -EINVAL; @@ -743,7 +731,6 @@ static int acpi_ec_start(struct acpi_device *device) acpi_status status = AE_OK; struct acpi_ec *ec = NULL; - if (!device) return -EINVAL; @@ -783,8 +770,7 @@ static int acpi_ec_start(struct acpi_device *device) &acpi_ec_space_handler, &acpi_ec_space_setup, ec); if (ACPI_FAILURE(status)) { - acpi_remove_gpe_handler(NULL, ec->gpe, - &acpi_ec_gpe_handler); + acpi_remove_gpe_handler(NULL, ec->gpe, &acpi_ec_gpe_handler); return -ENODEV; } @@ -796,7 +782,6 @@ static int acpi_ec_stop(struct acpi_device *device, int type) acpi_status status = AE_OK; struct acpi_ec *ec = NULL; - if (!device) return -EINVAL; @@ -808,9 +793,7 @@ static int acpi_ec_stop(struct acpi_device *device, int type) if (ACPI_FAILURE(status)) return -ENODEV; - status = - acpi_remove_gpe_handler(NULL, ec->gpe, - &acpi_ec_gpe_handler); + status = acpi_remove_gpe_handler(NULL, ec->gpe, &acpi_ec_gpe_handler); if (ACPI_FAILURE(status)) return -ENODEV; @@ -835,16 +818,15 @@ acpi_fake_ecdt_callback(acpi_handle handle, ec_ecdt->uid = -1; acpi_evaluate_integer(handle, "_UID", NULL, &ec_ecdt->uid); - status = - acpi_evaluate_integer(handle, "_GPE", NULL, - &ec_ecdt->gpe); + status = acpi_evaluate_integer(handle, "_GPE", NULL, &ec_ecdt->gpe); if (ACPI_FAILURE(status)) return status; ec_ecdt->global_lock = TRUE; ec_ecdt->handle = handle; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "GPE=0x%02lx, ports=0x%2lx, 0x%2lx", - ec_ecdt->gpe, ec_ecdt->command_addr, ec_ecdt->data_addr)); + ec_ecdt->gpe, ec_ecdt->command_addr, + ec_ecdt->data_addr)); return AE_CTRL_TERMINATE; } @@ -883,7 +865,7 @@ static int __init acpi_ec_fake_ecdt(void) goto error; } return 0; - error: + error: return ret; } @@ -919,14 +901,13 @@ static int __init acpi_ec_get_real_ecdt(void) ec_ecdt->global_lock = TRUE; ec_ecdt->uid = ecdt_ptr->uid; - status = - acpi_get_handle(NULL, ecdt_ptr->ec_id, &ec_ecdt->handle); + status = acpi_get_handle(NULL, ecdt_ptr->ec_id, &ec_ecdt->handle); if (ACPI_FAILURE(status)) { goto error; } return 0; - error: + error: ACPI_EXCEPTION((AE_INFO, status, "Could not use ECDT")); kfree(ec_ecdt); ec_ecdt = NULL; @@ -986,7 +967,6 @@ static int __init acpi_ec_init(void) { int result = 0; - if (acpi_disabled) return 0; @@ -1039,7 +1019,8 @@ static int __init acpi_ec_set_intr_mode(char *str) acpi_ec_mode = EC_POLL; } acpi_ec_driver.ops.add = acpi_ec_add; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "EC %s mode.\n", intr ? "interrupt" : "polling")); + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "EC %s mode.\n", + intr ? "interrupt" : "polling")); return 1; } -- cgit v0.10.2 From 39043a5b3d0b1b92b20209b6d401fb70c17177b4 Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Fri, 8 Dec 2006 02:23:07 -0600 Subject: [POWERPC] of_device_register: propagate device_create_file return code Removed compiler warning about ignoring the return code of device_create_file in of_device_register. Signed-off-by: Kumar Gala diff --git a/arch/powerpc/kernel/of_device.c b/arch/powerpc/kernel/of_device.c index 8a06724..e921514 100644 --- a/arch/powerpc/kernel/of_device.c +++ b/arch/powerpc/kernel/of_device.c @@ -109,9 +109,7 @@ int of_device_register(struct of_device *ofdev) if (rc) return rc; - device_create_file(&ofdev->dev, &dev_attr_devspec); - - return 0; + return device_create_file(&ofdev->dev, &dev_attr_devspec); } void of_device_unregister(struct of_device *ofdev) -- cgit v0.10.2 From aa42c69c67f82e88f0726258efe7306708e1cf14 Mon Sep 17 00:00:00 2001 From: Kim Phillips Date: Fri, 8 Dec 2006 02:43:30 -0600 Subject: [POWERPC] Add support for FP emulation for the e300c2 core The e300c2 has no FPU. Its MSR[FP] is grounded to zero. If an attempt is made to execute a floating point instruction (including floating-point load, store, or move instructions), the e300c2 takes a floating-point unavailable interrupt. This patch adds support for FP emulation on the e300c2 by declaring a new CPU_FTR_FP_TAKES_FPUNAVAIL, where FP unavail interrupts are intercepted and redirected to the ProgramCheck exception path for correct emulation handling. (If we run out of CPU_FTR bits we could look to reclaim this bit by adding support to test the cpu_user_features for PPC_FEATURE_HAS_FPU instead) It adds a nop to the exception path for 32-bit processors with a FPU. Signed-off-by: Kim Phillips Signed-off-by: Kumar Gala diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 291c95a..0b2d05d 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -706,7 +706,7 @@ config FORCE_MAX_ZONEORDER config MATH_EMULATION bool "Math emulation" - depends on 4xx || 8xx || E200 || E500 + depends on 4xx || 8xx || E200 || PPC_83xx || E500 ---help--- Some PowerPC chips designed for embedded applications do not have a floating-point unit and therefore do not implement the diff --git a/arch/powerpc/kernel/cputable.c b/arch/powerpc/kernel/cputable.c index 9d1614c..11bfbaf 100644 --- a/arch/powerpc/kernel/cputable.c +++ b/arch/powerpc/kernel/cputable.c @@ -833,7 +833,7 @@ static struct cpu_spec cpu_specs[] = { .pvr_mask = 0x7fff0000, .pvr_value = 0x00840000, .cpu_name = "e300c2", - .cpu_features = CPU_FTRS_E300, + .cpu_features = CPU_FTRS_E300C2, .cpu_user_features = PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU, .icache_bsize = 32, .dcache_bsize = 32, diff --git a/arch/powerpc/kernel/head_32.S b/arch/powerpc/kernel/head_32.S index d88e182..9417cf5 100644 --- a/arch/powerpc/kernel/head_32.S +++ b/arch/powerpc/kernel/head_32.S @@ -437,6 +437,13 @@ Alignment: /* Floating-point unavailable */ . = 0x800 FPUnavailable: +BEGIN_FTR_SECTION +/* + * Certain Freescale cores don't have a FPU and treat fp instructions + * as a FP Unavailable exception. Redirect to illegal/emulation handling. + */ + b ProgramCheck +END_FTR_SECTION_IFSET(CPU_FTR_FPU_UNAVAILABLE) EXCEPTION_PROLOG bne load_up_fpu /* if from user, just load it up */ addi r3,r1,STACK_FRAME_OVERHEAD diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index 0d4e203..fde820e 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -782,6 +782,8 @@ void __kprobes program_check_exception(struct pt_regs *regs) unsigned int reason = get_reason(regs); extern int do_mathemu(struct pt_regs *regs); + /* We can now get here via a FP Unavailable exception if the core + * has no FPU, in that case no reason flags will be set */ #ifdef CONFIG_MATH_EMULATION /* (reason & REASON_ILLEGAL) would be the obvious thing here, * but there seems to be a hardware bug on the 405GP (RevD) diff --git a/include/asm-powerpc/cputable.h b/include/asm-powerpc/cputable.h index 6fe5c9d..aca72f9 100644 --- a/include/asm-powerpc/cputable.h +++ b/include/asm-powerpc/cputable.h @@ -126,6 +126,7 @@ extern void do_feature_fixups(unsigned long value, void *fixup_start, #define CPU_FTR_NODSISRALIGN ASM_CONST(0x0000000000100000) #define CPU_FTR_PPC_LE ASM_CONST(0x0000000000200000) #define CPU_FTR_REAL_LE ASM_CONST(0x0000000000400000) +#define CPU_FTR_FPU_UNAVAILABLE ASM_CONST(0x0000000000800000) /* * Add the 64-bit processor unique features in the top half of the word; @@ -295,6 +296,9 @@ extern void do_feature_fixups(unsigned long value, void *fixup_start, #define CPU_FTRS_E300 (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_MAYBE_CAN_DOZE | \ CPU_FTR_USE_TB | CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_HAS_HIGH_BATS | \ CPU_FTR_COMMON) +#define CPU_FTRS_E300C2 (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_MAYBE_CAN_DOZE | \ + CPU_FTR_USE_TB | CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_HAS_HIGH_BATS | \ + CPU_FTR_COMMON | CPU_FTR_FPU_UNAVAILABLE) #define CPU_FTRS_CLASSIC32 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ CPU_FTR_USE_TB | CPU_FTR_HPTE_TABLE) #define CPU_FTRS_8XX (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB) @@ -364,7 +368,8 @@ enum { CPU_FTRS_7450_21 | CPU_FTRS_7450_23 | CPU_FTRS_7455_1 | CPU_FTRS_7455_20 | CPU_FTRS_7455 | CPU_FTRS_7447_10 | CPU_FTRS_7447 | CPU_FTRS_7447A | CPU_FTRS_82XX | - CPU_FTRS_G2_LE | CPU_FTRS_E300 | CPU_FTRS_CLASSIC32 | + CPU_FTRS_G2_LE | CPU_FTRS_E300 | CPU_FTRS_E300C2 | + CPU_FTRS_CLASSIC32 | #else CPU_FTRS_GENERIC_32 | #endif @@ -403,7 +408,8 @@ enum { CPU_FTRS_7450_21 & CPU_FTRS_7450_23 & CPU_FTRS_7455_1 & CPU_FTRS_7455_20 & CPU_FTRS_7455 & CPU_FTRS_7447_10 & CPU_FTRS_7447 & CPU_FTRS_7447A & CPU_FTRS_82XX & - CPU_FTRS_G2_LE & CPU_FTRS_E300 & CPU_FTRS_CLASSIC32 & + CPU_FTRS_G2_LE & CPU_FTRS_E300 & CPU_FTRS_E300C2 & + CPU_FTRS_CLASSIC32 & #else CPU_FTRS_GENERIC_32 & #endif -- cgit v0.10.2 From a147c5857c0b591b05d787e59b691c3a4f245f83 Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Fri, 8 Dec 2006 02:34:38 -0600 Subject: [POWERPC] Fix 440SPe CPU table entry The 440SPe CPU table entry was missing the CPU_FTR_NODSISRALIGN and really should have been CPU_FTRS_44X. Signed-off-by: Kumar Gala diff --git a/arch/powerpc/kernel/cputable.c b/arch/powerpc/kernel/cputable.c index 11bfbaf..b742013 100644 --- a/arch/powerpc/kernel/cputable.c +++ b/arch/powerpc/kernel/cputable.c @@ -1136,8 +1136,7 @@ static struct cpu_spec cpu_specs[] = { .pvr_mask = 0xff000fff, .pvr_value = 0x53000890, .cpu_name = "440SPe Rev. A", - .cpu_features = CPU_FTR_SPLIT_ID_CACHE | - CPU_FTR_USE_TB, + .cpu_features = CPU_FTRS_44X, .cpu_user_features = COMMON_USER_BOOKE, .icache_bsize = 32, .dcache_bsize = 32, -- cgit v0.10.2 From c3e2a79c0bd3e12b67ce5f11cab10951ae8b7f37 Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Mon, 4 Dec 2006 13:46:52 +0100 Subject: [AVR32] Portmux API update Rename portmux_set_func to at32_select_periph, add at32_select_gpio and add flags parameter to specify the initial state of the pins. Signed-off-by: Haavard Skinnemoen diff --git a/arch/avr32/mach-at32ap/at32ap7000.c b/arch/avr32/mach-at32ap/at32ap7000.c index 7ff6ad8..ada7930 100644 --- a/arch/avr32/mach-at32ap/at32ap7000.c +++ b/arch/avr32/mach-at32ap/at32ap7000.c @@ -11,6 +11,7 @@ #include +#include #include #include #include @@ -57,6 +58,9 @@ static struct platform_device _name##_id##_device = { \ .num_resources = ARRAY_SIZE(_name##_id##_resource), \ } +#define select_peripheral(pin, periph, flags) \ + at32_select_periph(GPIO_PIN_##pin, GPIO_##periph, flags) + #define DEV_CLK(_name, devname, bus, _index) \ static struct clk devname##_##_name = { \ .name = #_name, \ @@ -67,18 +71,6 @@ static struct clk devname##_##_name = { \ .index = _index, \ } -enum { - PIOA, - PIOB, - PIOC, - PIOD, -}; - -enum { - FUNC_A, - FUNC_B, -}; - unsigned long at32ap7000_osc_rates[3] = { [0] = 32768, /* FIXME: these are ATSTK1002-specific */ @@ -569,26 +561,26 @@ DEV_CLK(usart, atmel_usart3, pba, 6); static inline void configure_usart0_pins(void) { - portmux_set_func(PIOA, 8, FUNC_B); /* RXD */ - portmux_set_func(PIOA, 9, FUNC_B); /* TXD */ + select_peripheral(PA(8), PERIPH_B, 0); /* RXD */ + select_peripheral(PA(9), PERIPH_B, 0); /* TXD */ } static inline void configure_usart1_pins(void) { - portmux_set_func(PIOA, 17, FUNC_A); /* RXD */ - portmux_set_func(PIOA, 18, FUNC_A); /* TXD */ + select_peripheral(PA(17), PERIPH_A, 0); /* RXD */ + select_peripheral(PA(18), PERIPH_A, 0); /* TXD */ } static inline void configure_usart2_pins(void) { - portmux_set_func(PIOB, 26, FUNC_B); /* RXD */ - portmux_set_func(PIOB, 27, FUNC_B); /* TXD */ + select_peripheral(PB(26), PERIPH_B, 0); /* RXD */ + select_peripheral(PB(27), PERIPH_B, 0); /* TXD */ } static inline void configure_usart3_pins(void) { - portmux_set_func(PIOB, 18, FUNC_B); /* RXD */ - portmux_set_func(PIOB, 17, FUNC_B); /* TXD */ + select_peripheral(PB(18), PERIPH_B, 0); /* RXD */ + select_peripheral(PB(17), PERIPH_B, 0); /* TXD */ } static struct platform_device *at32_usarts[4]; @@ -663,27 +655,27 @@ at32_add_device_eth(unsigned int id, struct eth_platform_data *data) case 0: pdev = &macb0_device; - portmux_set_func(PIOC, 3, FUNC_A); /* TXD0 */ - portmux_set_func(PIOC, 4, FUNC_A); /* TXD1 */ - portmux_set_func(PIOC, 7, FUNC_A); /* TXEN */ - portmux_set_func(PIOC, 8, FUNC_A); /* TXCK */ - portmux_set_func(PIOC, 9, FUNC_A); /* RXD0 */ - portmux_set_func(PIOC, 10, FUNC_A); /* RXD1 */ - portmux_set_func(PIOC, 13, FUNC_A); /* RXER */ - portmux_set_func(PIOC, 15, FUNC_A); /* RXDV */ - portmux_set_func(PIOC, 16, FUNC_A); /* MDC */ - portmux_set_func(PIOC, 17, FUNC_A); /* MDIO */ + select_peripheral(PC(3), PERIPH_A, 0); /* TXD0 */ + select_peripheral(PC(4), PERIPH_A, 0); /* TXD1 */ + select_peripheral(PC(7), PERIPH_A, 0); /* TXEN */ + select_peripheral(PC(8), PERIPH_A, 0); /* TXCK */ + select_peripheral(PC(9), PERIPH_A, 0); /* RXD0 */ + select_peripheral(PC(10), PERIPH_A, 0); /* RXD1 */ + select_peripheral(PC(13), PERIPH_A, 0); /* RXER */ + select_peripheral(PC(15), PERIPH_A, 0); /* RXDV */ + select_peripheral(PC(16), PERIPH_A, 0); /* MDC */ + select_peripheral(PC(17), PERIPH_A, 0); /* MDIO */ if (!data->is_rmii) { - portmux_set_func(PIOC, 0, FUNC_A); /* COL */ - portmux_set_func(PIOC, 1, FUNC_A); /* CRS */ - portmux_set_func(PIOC, 2, FUNC_A); /* TXER */ - portmux_set_func(PIOC, 5, FUNC_A); /* TXD2 */ - portmux_set_func(PIOC, 6, FUNC_A); /* TXD3 */ - portmux_set_func(PIOC, 11, FUNC_A); /* RXD2 */ - portmux_set_func(PIOC, 12, FUNC_A); /* RXD3 */ - portmux_set_func(PIOC, 14, FUNC_A); /* RXCK */ - portmux_set_func(PIOC, 18, FUNC_A); /* SPD */ + select_peripheral(PC(0), PERIPH_A, 0); /* COL */ + select_peripheral(PC(1), PERIPH_A, 0); /* CRS */ + select_peripheral(PC(2), PERIPH_A, 0); /* TXER */ + select_peripheral(PC(5), PERIPH_A, 0); /* TXD2 */ + select_peripheral(PC(6), PERIPH_A, 0); /* TXD3 */ + select_peripheral(PC(11), PERIPH_A, 0); /* RXD2 */ + select_peripheral(PC(12), PERIPH_A, 0); /* RXD3 */ + select_peripheral(PC(14), PERIPH_A, 0); /* RXCK */ + select_peripheral(PC(18), PERIPH_A, 0); /* SPD */ } break; @@ -714,12 +706,12 @@ struct platform_device *__init at32_add_device_spi(unsigned int id) switch (id) { case 0: pdev = &spi0_device; - portmux_set_func(PIOA, 0, FUNC_A); /* MISO */ - portmux_set_func(PIOA, 1, FUNC_A); /* MOSI */ - portmux_set_func(PIOA, 2, FUNC_A); /* SCK */ - portmux_set_func(PIOA, 3, FUNC_A); /* NPCS0 */ - portmux_set_func(PIOA, 4, FUNC_A); /* NPCS1 */ - portmux_set_func(PIOA, 5, FUNC_A); /* NPCS2 */ + select_peripheral(PA(0), PERIPH_A, 0); /* MISO */ + select_peripheral(PA(1), PERIPH_A, 0); /* MOSI */ + select_peripheral(PA(2), PERIPH_A, 0); /* SCK */ + select_peripheral(PA(3), PERIPH_A, 0); /* NPCS0 */ + select_peripheral(PA(4), PERIPH_A, 0); /* NPCS1 */ + select_peripheral(PA(5), PERIPH_A, 0); /* NPCS2 */ break; default: @@ -762,37 +754,37 @@ at32_add_device_lcdc(unsigned int id, struct lcdc_platform_data *data) switch (id) { case 0: pdev = &lcdc0_device; - portmux_set_func(PIOC, 19, FUNC_A); /* CC */ - portmux_set_func(PIOC, 20, FUNC_A); /* HSYNC */ - portmux_set_func(PIOC, 21, FUNC_A); /* PCLK */ - portmux_set_func(PIOC, 22, FUNC_A); /* VSYNC */ - portmux_set_func(PIOC, 23, FUNC_A); /* DVAL */ - portmux_set_func(PIOC, 24, FUNC_A); /* MODE */ - portmux_set_func(PIOC, 25, FUNC_A); /* PWR */ - portmux_set_func(PIOC, 26, FUNC_A); /* DATA0 */ - portmux_set_func(PIOC, 27, FUNC_A); /* DATA1 */ - portmux_set_func(PIOC, 28, FUNC_A); /* DATA2 */ - portmux_set_func(PIOC, 29, FUNC_A); /* DATA3 */ - portmux_set_func(PIOC, 30, FUNC_A); /* DATA4 */ - portmux_set_func(PIOC, 31, FUNC_A); /* DATA5 */ - portmux_set_func(PIOD, 0, FUNC_A); /* DATA6 */ - portmux_set_func(PIOD, 1, FUNC_A); /* DATA7 */ - portmux_set_func(PIOD, 2, FUNC_A); /* DATA8 */ - portmux_set_func(PIOD, 3, FUNC_A); /* DATA9 */ - portmux_set_func(PIOD, 4, FUNC_A); /* DATA10 */ - portmux_set_func(PIOD, 5, FUNC_A); /* DATA11 */ - portmux_set_func(PIOD, 6, FUNC_A); /* DATA12 */ - portmux_set_func(PIOD, 7, FUNC_A); /* DATA13 */ - portmux_set_func(PIOD, 8, FUNC_A); /* DATA14 */ - portmux_set_func(PIOD, 9, FUNC_A); /* DATA15 */ - portmux_set_func(PIOD, 10, FUNC_A); /* DATA16 */ - portmux_set_func(PIOD, 11, FUNC_A); /* DATA17 */ - portmux_set_func(PIOD, 12, FUNC_A); /* DATA18 */ - portmux_set_func(PIOD, 13, FUNC_A); /* DATA19 */ - portmux_set_func(PIOD, 14, FUNC_A); /* DATA20 */ - portmux_set_func(PIOD, 15, FUNC_A); /* DATA21 */ - portmux_set_func(PIOD, 16, FUNC_A); /* DATA22 */ - portmux_set_func(PIOD, 17, FUNC_A); /* DATA23 */ + select_peripheral(PC(19), PERIPH_A, 0); /* CC */ + select_peripheral(PC(20), PERIPH_A, 0); /* HSYNC */ + select_peripheral(PC(21), PERIPH_A, 0); /* PCLK */ + select_peripheral(PC(22), PERIPH_A, 0); /* VSYNC */ + select_peripheral(PC(23), PERIPH_A, 0); /* DVAL */ + select_peripheral(PC(24), PERIPH_A, 0); /* MODE */ + select_peripheral(PC(25), PERIPH_A, 0); /* PWR */ + select_peripheral(PC(26), PERIPH_A, 0); /* DATA0 */ + select_peripheral(PC(27), PERIPH_A, 0); /* DATA1 */ + select_peripheral(PC(28), PERIPH_A, 0); /* DATA2 */ + select_peripheral(PC(29), PERIPH_A, 0); /* DATA3 */ + select_peripheral(PC(30), PERIPH_A, 0); /* DATA4 */ + select_peripheral(PC(31), PERIPH_A, 0); /* DATA5 */ + select_peripheral(PD(0), PERIPH_A, 0); /* DATA6 */ + select_peripheral(PD(1), PERIPH_A, 0); /* DATA7 */ + select_peripheral(PD(2), PERIPH_A, 0); /* DATA8 */ + select_peripheral(PD(3), PERIPH_A, 0); /* DATA9 */ + select_peripheral(PD(4), PERIPH_A, 0); /* DATA10 */ + select_peripheral(PD(5), PERIPH_A, 0); /* DATA11 */ + select_peripheral(PD(6), PERIPH_A, 0); /* DATA12 */ + select_peripheral(PD(7), PERIPH_A, 0); /* DATA13 */ + select_peripheral(PD(8), PERIPH_A, 0); /* DATA14 */ + select_peripheral(PD(9), PERIPH_A, 0); /* DATA15 */ + select_peripheral(PD(10), PERIPH_A, 0); /* DATA16 */ + select_peripheral(PD(11), PERIPH_A, 0); /* DATA17 */ + select_peripheral(PD(12), PERIPH_A, 0); /* DATA18 */ + select_peripheral(PD(13), PERIPH_A, 0); /* DATA19 */ + select_peripheral(PD(14), PERIPH_A, 0); /* DATA20 */ + select_peripheral(PD(15), PERIPH_A, 0); /* DATA21 */ + select_peripheral(PD(16), PERIPH_A, 0); /* DATA22 */ + select_peripheral(PD(17), PERIPH_A, 0); /* DATA23 */ clk_set_parent(&lcdc0_pixclk, &pll0); clk_set_rate(&lcdc0_pixclk, clk_get_rate(&pll0)); diff --git a/arch/avr32/mach-at32ap/pio.c b/arch/avr32/mach-at32ap/pio.c index d3aabfc..f1280ed 100644 --- a/arch/avr32/mach-at32ap/pio.c +++ b/arch/avr32/mach-at32ap/pio.c @@ -25,27 +25,98 @@ struct pio_device { void __iomem *regs; const struct platform_device *pdev; struct clk *clk; - u32 alloc_mask; + u32 pinmux_mask; char name[32]; }; static struct pio_device pio_dev[MAX_NR_PIO_DEVICES]; -void portmux_set_func(unsigned int portmux_id, unsigned int pin_id, - unsigned int function_id) +static struct pio_device *gpio_to_pio(unsigned int gpio) { struct pio_device *pio; - u32 mask = 1 << pin_id; + unsigned int index; - BUG_ON(portmux_id >= MAX_NR_PIO_DEVICES); + index = gpio >> 5; + if (index >= MAX_NR_PIO_DEVICES) + return NULL; + pio = &pio_dev[index]; + if (!pio->regs) + return NULL; - pio = &pio_dev[portmux_id]; + return pio; +} + +/* Pin multiplexing API */ + +void __init at32_select_periph(unsigned int pin, unsigned int periph, + unsigned long flags) +{ + struct pio_device *pio; + unsigned int pin_index = pin & 0x1f; + u32 mask = 1 << pin_index; + + pio = gpio_to_pio(pin); + if (unlikely(!pio)) { + printk("pio: invalid pin %u\n", pin); + goto fail; + } - if (function_id) + if (unlikely(test_and_set_bit(pin_index, &pio->pinmux_mask))) { + printk("%s: pin %u is busy\n", pio->name, pin_index); + goto fail; + } + + pio_writel(pio, PUER, mask); + if (periph) pio_writel(pio, BSR, mask); else pio_writel(pio, ASR, mask); + pio_writel(pio, PDR, mask); + if (!(flags & AT32_GPIOF_PULLUP)) + pio_writel(pio, PUDR, mask); + + return; + +fail: + dump_stack(); +} + +void __init at32_select_gpio(unsigned int pin, unsigned long flags) +{ + struct pio_device *pio; + unsigned int pin_index = pin & 0x1f; + u32 mask = 1 << pin_index; + + pio = gpio_to_pio(pin); + if (unlikely(!pio)) { + printk("pio: invalid pin %u\n", pin); + goto fail; + } + + if (unlikely(test_and_set_bit(pin_index, &pio->pinmux_mask))) { + printk("%s: pin %u is busy\n", pio->name, pin_index); + goto fail; + } + + pio_writel(pio, PUER, mask); + if (flags & AT32_GPIOF_HIGH) + pio_writel(pio, SODR, mask); + else + pio_writel(pio, CODR, mask); + if (flags & AT32_GPIOF_OUTPUT) + pio_writel(pio, OER, mask); + else + pio_writel(pio, ODR, mask); + + pio_writel(pio, PER, mask); + if (!(flags & AT32_GPIOF_PULLUP)) + pio_writel(pio, PUDR, mask); + + return; + +fail: + dump_stack(); } static int __init pio_probe(struct platform_device *pdev) diff --git a/include/asm-avr32/arch-at32ap/at32ap7000.h b/include/asm-avr32/arch-at32ap/at32ap7000.h new file mode 100644 index 0000000..ba85e04 --- /dev/null +++ b/include/asm-avr32/arch-at32ap/at32ap7000.h @@ -0,0 +1,33 @@ +/* + * Pin definitions for AT32AP7000. + * + * Copyright (C) 2006 Atmel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __ASM_ARCH_AT32AP7000_H__ +#define __ASM_ARCH_AT32AP7000_H__ + +#define GPIO_PERIPH_A 0 +#define GPIO_PERIPH_B 1 + +#define NR_GPIO_CONTROLLERS 4 + +/* + * Pin numbers identifying specific GPIO pins on the chip. They can + * also be converted to IRQ numbers by passing them through + * gpio_to_irq(). + */ +#define GPIO_PIOA_BASE (0) +#define GPIO_PIOB_BASE (GPIO_PIOA_BASE + 32) +#define GPIO_PIOC_BASE (GPIO_PIOB_BASE + 32) +#define GPIO_PIOD_BASE (GPIO_PIOC_BASE + 32) + +#define GPIO_PIN_PA(N) (GPIO_PIOA_BASE + (N)) +#define GPIO_PIN_PB(N) (GPIO_PIOB_BASE + (N)) +#define GPIO_PIN_PC(N) (GPIO_PIOC_BASE + (N)) +#define GPIO_PIN_PD(N) (GPIO_PIOD_BASE + (N)) + +#endif /* __ASM_ARCH_AT32AP7000_H__ */ diff --git a/include/asm-avr32/arch-at32ap/portmux.h b/include/asm-avr32/arch-at32ap/portmux.h index 4d50421..83c6905 100644 --- a/include/asm-avr32/arch-at32ap/portmux.h +++ b/include/asm-avr32/arch-at32ap/portmux.h @@ -7,10 +7,20 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ -#ifndef __ASM_AVR32_AT32_PORTMUX_H__ -#define __ASM_AVR32_AT32_PORTMUX_H__ +#ifndef __ASM_ARCH_PORTMUX_H__ +#define __ASM_ARCH_PORTMUX_H__ -void portmux_set_func(unsigned int portmux_id, unsigned int pin_id, - unsigned int function_id); +/* + * Set up pin multiplexing, called from board init only. + * + * The following flags determine the initial state of the pin. + */ +#define AT32_GPIOF_PULLUP 0x00000001 /* Enable pull-up */ +#define AT32_GPIOF_OUTPUT 0x00000002 /* Enable output driver */ +#define AT32_GPIOF_HIGH 0x00000004 /* Set output high */ + +void at32_select_periph(unsigned int pin, unsigned int periph, + unsigned long flags); +void at32_select_gpio(unsigned int pin, unsigned long flags); -#endif /* __ASM_AVR32_AT32_PORTMUX_H__ */ +#endif /* __ASM_ARCH_PORTMUX_H__ */ -- cgit v0.10.2 From cfcb3a89d04144c064023bdc7d8dc600a88cc5c4 Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Mon, 30 Oct 2006 09:23:12 +0100 Subject: [AVR32] Add macb1 platform_device Add platform_device definition and pio init code for the second ethernet controller in AT32AP7000. Signed-off-by: Haavard Skinnemoen diff --git a/arch/avr32/mach-at32ap/at32ap7000.c b/arch/avr32/mach-at32ap/at32ap7000.c index ada7930..48f4ef3 100644 --- a/arch/avr32/mach-at32ap/at32ap7000.c +++ b/arch/avr32/mach-at32ap/at32ap7000.c @@ -646,6 +646,15 @@ DEFINE_DEV_DATA(macb, 0); DEV_CLK(hclk, macb0, hsb, 8); DEV_CLK(pclk, macb0, pbb, 6); +static struct eth_platform_data macb1_data; +static struct resource macb1_resource[] = { + PBMEM(0xfff01c00), + IRQ(26), +}; +DEFINE_DEV_DATA(macb, 1); +DEV_CLK(hclk, macb1, hsb, 9); +DEV_CLK(pclk, macb1, pbb, 7); + struct platform_device *__init at32_add_device_eth(unsigned int id, struct eth_platform_data *data) { @@ -679,6 +688,33 @@ at32_add_device_eth(unsigned int id, struct eth_platform_data *data) } break; + case 1: + pdev = &macb1_device; + + select_peripheral(PD(13), PERIPH_B, 0); /* TXD0 */ + select_peripheral(PD(14), PERIPH_B, 0); /* TXD1 */ + select_peripheral(PD(11), PERIPH_B, 0); /* TXEN */ + select_peripheral(PD(12), PERIPH_B, 0); /* TXCK */ + select_peripheral(PD(10), PERIPH_B, 0); /* RXD0 */ + select_peripheral(PD(6), PERIPH_B, 0); /* RXD1 */ + select_peripheral(PD(5), PERIPH_B, 0); /* RXER */ + select_peripheral(PD(4), PERIPH_B, 0); /* RXDV */ + select_peripheral(PD(3), PERIPH_B, 0); /* MDC */ + select_peripheral(PD(2), PERIPH_B, 0); /* MDIO */ + + if (!data->is_rmii) { + select_peripheral(PC(19), PERIPH_B, 0); /* COL */ + select_peripheral(PC(23), PERIPH_B, 0); /* CRS */ + select_peripheral(PC(26), PERIPH_B, 0); /* TXER */ + select_peripheral(PC(27), PERIPH_B, 0); /* TXD2 */ + select_peripheral(PC(28), PERIPH_B, 0); /* TXD3 */ + select_peripheral(PC(29), PERIPH_B, 0); /* RXD2 */ + select_peripheral(PC(30), PERIPH_B, 0); /* RXD3 */ + select_peripheral(PC(24), PERIPH_B, 0); /* RXCK */ + select_peripheral(PD(15), PERIPH_B, 0); /* SPD */ + } + break; + default: return NULL; } @@ -830,6 +866,8 @@ struct clk *at32_clock_list[] = { &atmel_usart3_usart, &macb0_hclk, &macb0_pclk, + &macb1_hclk, + &macb1_pclk, &spi0_mck, &lcdc0_hclk, &lcdc0_pixclk, -- cgit v0.10.2 From a6f92f3dc8e53185bae50d44b5042b9cddf7f475 Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Mon, 4 Dec 2006 13:58:27 +0100 Subject: [AVR32] Move ethernet tag parsing to board-specific code By moving the ethernet tag parsing to the board-specific code we avoid the issue of figuring out which device we're supposed to attach the information to. The board specific code knows this because it's where the actual devices are instantiated. Signed-off-by: Haavard Skinnemoen diff --git a/arch/avr32/boards/atstk1000/atstk1002.c b/arch/avr32/boards/atstk1000/atstk1002.c index cced73c..f65865c 100644 --- a/arch/avr32/boards/atstk1000/atstk1002.c +++ b/arch/avr32/boards/atstk1000/atstk1002.c @@ -8,19 +8,32 @@ * published by the Free Software Foundation. */ #include +#include +#include +#include +#include #include #include -struct eth_platform_data __initdata eth0_data = { - .valid = 1, - .mii_phy_addr = 0x10, - .is_rmii = 0, - .hw_addr = { 0x6a, 0x87, 0x71, 0x14, 0xcd, 0xcb }, -}; - +static struct eth_platform_data __initdata eth_data[2]; extern struct lcdc_platform_data atstk1000_fb0_data; +static int __init parse_tag_ethernet(struct tag *tag) +{ + int i; + + i = tag->u.ethernet.mac_index; + if (i < ARRAY_SIZE(eth_data)) { + eth_data[i].mii_phy_addr = tag->u.ethernet.mii_phy_addr; + memcpy(ð_data[i].hw_addr, tag->u.ethernet.hw_address, + sizeof(eth_data[i].hw_addr)); + eth_data[i].valid = 1; + } + return 0; +} +__tagtable(ATAG_ETHERNET, parse_tag_ethernet); + void __init setup_board(void) { at32_map_usart(1, 0); /* /dev/ttyS0 */ @@ -38,7 +51,9 @@ static int __init atstk1002_init(void) at32_add_device_usart(1); at32_add_device_usart(2); - at32_add_device_eth(0, ð0_data); + if (eth_data[0].valid) + at32_add_device_eth(0, ð_data[0]); + at32_add_device_spi(0); at32_add_device_lcdc(0, &atstk1000_fb0_data); diff --git a/arch/avr32/kernel/setup.c b/arch/avr32/kernel/setup.c index ea2d1ff..a342116 100644 --- a/arch/avr32/kernel/setup.c +++ b/arch/avr32/kernel/setup.c @@ -229,30 +229,6 @@ static int __init parse_tag_rsvd_mem(struct tag *tag) } __tagtable(ATAG_RSVD_MEM, parse_tag_rsvd_mem); -static int __init parse_tag_ethernet(struct tag *tag) -{ -#if 0 - const struct platform_device *pdev; - - /* - * We really need a bus type that supports "classes"...this - * will do for now (until we must handle other kinds of - * ethernet controllers) - */ - pdev = platform_get_device("macb", tag->u.ethernet.mac_index); - if (pdev && pdev->dev.platform_data) { - struct eth_platform_data *data = pdev->dev.platform_data; - - data->valid = 1; - data->mii_phy_addr = tag->u.ethernet.mii_phy_addr; - memcpy(data->hw_addr, tag->u.ethernet.hw_address, - sizeof(data->hw_addr)); - } -#endif - return 0; -} -__tagtable(ATAG_ETHERNET, parse_tag_ethernet); - /* * Scan the tag table for this tag, and call its parse function. The * tag table is built by the linker from all the __tagtable -- cgit v0.10.2 From c164b90135d05ac52f70e6652910a4f585f7b999 Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Mon, 4 Dec 2006 14:08:39 +0100 Subject: [AVR32] Remove mii_phy_addr and eth_addr from eth_platform_data The macb driver will probe for the PHY chip and read the mac address from the MACB registers, so we don't need them in eth_platform_data anymore. Since u-boot doesn't currently initialize the MACB registers with the mac addresses, the tag parsing code is kept but instead of sticking the information into eth_platform_data, it uses it to initialize the MACB registers (in case the boot loader didn't do it.) This code should be unnecessary at some point in the future. Signed-off-by: Haavard Skinnemoen diff --git a/arch/avr32/boards/atstk1000/atstk1002.c b/arch/avr32/boards/atstk1000/atstk1002.c index f65865c..32b361f 100644 --- a/arch/avr32/boards/atstk1000/atstk1002.c +++ b/arch/avr32/boards/atstk1000/atstk1002.c @@ -7,33 +7,83 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#include +#include #include #include +#include #include #include +#include #include #include #include +struct eth_addr { + u8 addr[6]; +}; + +static struct eth_addr __initdata hw_addr[2]; + static struct eth_platform_data __initdata eth_data[2]; extern struct lcdc_platform_data atstk1000_fb0_data; +/* + * The next two functions should go away as the boot loader is + * supposed to initialize the macb address registers with a valid + * ethernet address. But we need to keep it around for a while until + * we can be reasonably sure the boot loader does this. + * + * The phy_id is ignored as the driver will probe for it. + */ static int __init parse_tag_ethernet(struct tag *tag) { int i; i = tag->u.ethernet.mac_index; - if (i < ARRAY_SIZE(eth_data)) { - eth_data[i].mii_phy_addr = tag->u.ethernet.mii_phy_addr; - memcpy(ð_data[i].hw_addr, tag->u.ethernet.hw_address, - sizeof(eth_data[i].hw_addr)); - eth_data[i].valid = 1; - } + if (i < ARRAY_SIZE(hw_addr)) + memcpy(hw_addr[i].addr, tag->u.ethernet.hw_address, + sizeof(hw_addr[i].addr)); + return 0; } __tagtable(ATAG_ETHERNET, parse_tag_ethernet); +static void __init set_hw_addr(struct platform_device *pdev) +{ + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + const u8 *addr; + void __iomem *regs; + struct clk *pclk; + + if (!res) + return; + if (pdev->id >= ARRAY_SIZE(hw_addr)) + return; + + addr = hw_addr[pdev->id].addr; + if (!is_valid_ether_addr(addr)) + return; + + /* + * Since this is board-specific code, we'll cheat and use the + * physical address directly as we happen to know that it's + * the same as the virtual address. + */ + regs = (void __iomem __force *)res->start; + pclk = clk_get(&pdev->dev, "pclk"); + if (!pclk) + return; + + clk_enable(pclk); + __raw_writel((addr[3] << 24) | (addr[2] << 16) + | (addr[1] << 8) | addr[0], regs + 0x98); + __raw_writel((addr[5] << 8) | addr[4], regs + 0x9c); + clk_disable(pclk); + clk_put(pclk); +} + void __init setup_board(void) { at32_map_usart(1, 0); /* /dev/ttyS0 */ @@ -51,8 +101,7 @@ static int __init atstk1002_init(void) at32_add_device_usart(1); at32_add_device_usart(2); - if (eth_data[0].valid) - at32_add_device_eth(0, ð_data[0]); + set_hw_addr(at32_add_device_eth(0, ð_data[0])); at32_add_device_spi(0); at32_add_device_lcdc(0, &atstk1000_fb0_data); diff --git a/include/asm-avr32/arch-at32ap/board.h b/include/asm-avr32/arch-at32ap/board.h index a39b3e9..b120ee0 100644 --- a/include/asm-avr32/arch-at32ap/board.h +++ b/include/asm-avr32/arch-at32ap/board.h @@ -21,10 +21,7 @@ void at32_map_usart(unsigned int hw_id, unsigned int line); struct platform_device *at32_add_device_usart(unsigned int id); struct eth_platform_data { - u8 valid; - u8 mii_phy_addr; u8 is_rmii; - u8 hw_addr[6]; }; struct platform_device * at32_add_device_eth(unsigned int id, struct eth_platform_data *data); -- cgit v0.10.2 From da58e92f1f2939587ac85ae3abfec8b1c743400b Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Mon, 4 Dec 2006 11:52:04 +0100 Subject: [AVR32] Remove unused file Remove arch/avr32/mach-at32ap/sm.c, which is not referenced by any Makefile. Signed-off-by: Haavard Skinnemoen diff --git a/arch/avr32/mach-at32ap/sm.c b/arch/avr32/mach-at32ap/sm.c deleted file mode 100644 index 03306eb..0000000 --- a/arch/avr32/mach-at32ap/sm.c +++ /dev/null @@ -1,289 +0,0 @@ -/* - * System Manager driver for AT32AP CPUs - * - * Copyright (C) 2006 Atmel Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include "sm.h" - -#define SM_EIM_IRQ_RESOURCE 1 -#define SM_PM_IRQ_RESOURCE 2 -#define SM_RTC_IRQ_RESOURCE 3 - -#define to_eim(irqc) container_of(irqc, struct at32_sm, irqc) - -struct at32_sm system_manager; - -int __init at32_sm_init(void) -{ - struct resource *regs; - struct at32_sm *sm = &system_manager; - int ret = -ENXIO; - - regs = platform_get_resource(&at32_sm_device, IORESOURCE_MEM, 0); - if (!regs) - goto fail; - - spin_lock_init(&sm->lock); - sm->pdev = &at32_sm_device; - - ret = -ENOMEM; - sm->regs = ioremap(regs->start, regs->end - regs->start + 1); - if (!sm->regs) - goto fail; - - return 0; - -fail: - printk(KERN_ERR "Failed to initialize System Manager: %d\n", ret); - return ret; -} - -/* - * External Interrupt Module (EIM). - * - * EIM gets level- or edge-triggered interrupts of either polarity - * from the outside and converts it to active-high level-triggered - * interrupts that the internal interrupt controller can handle. EIM - * also provides masking/unmasking of interrupts, as well as - * acknowledging of edge-triggered interrupts. - */ - -static irqreturn_t spurious_eim_interrupt(int irq, void *dev_id, - struct pt_regs *regs) -{ - printk(KERN_WARNING "Spurious EIM interrupt %d\n", irq); - disable_irq(irq); - return IRQ_NONE; -} - -static struct irqaction eim_spurious_action = { - .handler = spurious_eim_interrupt, -}; - -static irqreturn_t eim_handle_irq(int irq, void *dev_id, struct pt_regs *regs) -{ - struct irq_controller * irqc = dev_id; - struct at32_sm *sm = to_eim(irqc); - unsigned long pending; - - /* - * No need to disable interrupts globally. The interrupt - * level relevant to this group must be masked all the time, - * so we know that this particular EIM instance will not be - * re-entered. - */ - spin_lock(&sm->lock); - - pending = intc_get_pending(sm->irqc.irq_group); - if (unlikely(!pending)) { - printk(KERN_ERR "EIM (group %u): No interrupts pending!\n", - sm->irqc.irq_group); - goto unlock; - } - - do { - struct irqaction *action; - unsigned int i; - - i = fls(pending) - 1; - pending &= ~(1 << i); - action = sm->action[i]; - - /* Acknowledge the interrupt */ - sm_writel(sm, EIM_ICR, 1 << i); - - spin_unlock(&sm->lock); - - if (action->flags & SA_INTERRUPT) - local_irq_disable(); - action->handler(sm->irqc.first_irq + i, action->dev_id, regs); - local_irq_enable(); - spin_lock(&sm->lock); - if (action->flags & SA_SAMPLE_RANDOM) - add_interrupt_randomness(sm->irqc.first_irq + i); - } while (pending); - -unlock: - spin_unlock(&sm->lock); - return IRQ_HANDLED; -} - -static void eim_mask(struct irq_controller *irqc, unsigned int irq) -{ - struct at32_sm *sm = to_eim(irqc); - unsigned int i; - - i = irq - sm->irqc.first_irq; - sm_writel(sm, EIM_IDR, 1 << i); -} - -static void eim_unmask(struct irq_controller *irqc, unsigned int irq) -{ - struct at32_sm *sm = to_eim(irqc); - unsigned int i; - - i = irq - sm->irqc.first_irq; - sm_writel(sm, EIM_IER, 1 << i); -} - -static int eim_setup(struct irq_controller *irqc, unsigned int irq, - struct irqaction *action) -{ - struct at32_sm *sm = to_eim(irqc); - sm->action[irq - sm->irqc.first_irq] = action; - /* Acknowledge earlier interrupts */ - sm_writel(sm, EIM_ICR, (1<<(irq - sm->irqc.first_irq))); - eim_unmask(irqc, irq); - return 0; -} - -static void eim_free(struct irq_controller *irqc, unsigned int irq, - void *dev) -{ - struct at32_sm *sm = to_eim(irqc); - eim_mask(irqc, irq); - sm->action[irq - sm->irqc.first_irq] = &eim_spurious_action; -} - -static int eim_set_type(struct irq_controller *irqc, unsigned int irq, - unsigned int type) -{ - struct at32_sm *sm = to_eim(irqc); - unsigned long flags; - u32 value, pattern; - - spin_lock_irqsave(&sm->lock, flags); - - pattern = 1 << (irq - sm->irqc.first_irq); - - value = sm_readl(sm, EIM_MODE); - if (type & IRQ_TYPE_LEVEL) - value |= pattern; - else - value &= ~pattern; - sm_writel(sm, EIM_MODE, value); - value = sm_readl(sm, EIM_EDGE); - if (type & IRQ_EDGE_RISING) - value |= pattern; - else - value &= ~pattern; - sm_writel(sm, EIM_EDGE, value); - value = sm_readl(sm, EIM_LEVEL); - if (type & IRQ_LEVEL_HIGH) - value |= pattern; - else - value &= ~pattern; - sm_writel(sm, EIM_LEVEL, value); - - spin_unlock_irqrestore(&sm->lock, flags); - - return 0; -} - -static unsigned int eim_get_type(struct irq_controller *irqc, - unsigned int irq) -{ - struct at32_sm *sm = to_eim(irqc); - unsigned long flags; - unsigned int type = 0; - u32 mode, edge, level, pattern; - - pattern = 1 << (irq - sm->irqc.first_irq); - - spin_lock_irqsave(&sm->lock, flags); - mode = sm_readl(sm, EIM_MODE); - edge = sm_readl(sm, EIM_EDGE); - level = sm_readl(sm, EIM_LEVEL); - spin_unlock_irqrestore(&sm->lock, flags); - - if (mode & pattern) - type |= IRQ_TYPE_LEVEL; - if (edge & pattern) - type |= IRQ_EDGE_RISING; - if (level & pattern) - type |= IRQ_LEVEL_HIGH; - - return type; -} - -static struct irq_controller_class eim_irq_class = { - .typename = "EIM", - .handle = eim_handle_irq, - .setup = eim_setup, - .free = eim_free, - .mask = eim_mask, - .unmask = eim_unmask, - .set_type = eim_set_type, - .get_type = eim_get_type, -}; - -static int __init eim_init(void) -{ - struct at32_sm *sm = &system_manager; - unsigned int i; - u32 pattern; - int ret; - - /* - * The EIM is really the same module as SM, so register - * mapping, etc. has been taken care of already. - */ - - /* - * Find out how many interrupt lines that are actually - * implemented in hardware. - */ - sm_writel(sm, EIM_IDR, ~0UL); - sm_writel(sm, EIM_MODE, ~0UL); - pattern = sm_readl(sm, EIM_MODE); - sm->irqc.nr_irqs = fls(pattern); - - ret = -ENOMEM; - sm->action = kmalloc(sizeof(*sm->action) * sm->irqc.nr_irqs, - GFP_KERNEL); - if (!sm->action) - goto out; - - for (i = 0; i < sm->irqc.nr_irqs; i++) - sm->action[i] = &eim_spurious_action; - - spin_lock_init(&sm->lock); - sm->irqc.irq_group = sm->pdev->resource[SM_EIM_IRQ_RESOURCE].start; - sm->irqc.class = &eim_irq_class; - - ret = intc_register_controller(&sm->irqc); - if (ret < 0) - goto out_free_actions; - - printk("EIM: External Interrupt Module at 0x%p, IRQ group %u\n", - sm->regs, sm->irqc.irq_group); - printk("EIM: Handling %u external IRQs, starting with IRQ%u\n", - sm->irqc.nr_irqs, sm->irqc.first_irq); - - return 0; - -out_free_actions: - kfree(sm->action); -out: - return ret; -} -arch_initcall(eim_init); -- cgit v0.10.2 From 01cb087e747538d6a831c3ab370a1e1fd4538d5c Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Mon, 4 Dec 2006 12:00:03 +0100 Subject: [AVR32] Set flow handler for external interrupts Make sure that the flow handler for external interrupts is updated whenever they type is changed. Also make sure that the defaults correspond with how the interrupt controller is configured. Signed-off-by: Haavard Skinnemoen diff --git a/arch/avr32/mach-at32ap/extint.c b/arch/avr32/mach-at32ap/extint.c index 4dff1f9..b59272e 100644 --- a/arch/avr32/mach-at32ap/extint.c +++ b/arch/avr32/mach-at32ap/extint.c @@ -49,12 +49,25 @@ static void eim_unmask_irq(unsigned int irq) static int eim_set_irq_type(unsigned int irq, unsigned int flow_type) { struct at32_sm *sm = get_irq_chip_data(irq); + struct irq_desc *desc; unsigned int i = irq - sm->eim_first_irq; u32 mode, edge, level; unsigned long flags; int ret = 0; - flow_type &= IRQ_TYPE_SENSE_MASK; + if (flow_type == IRQ_TYPE_NONE) + flow_type = IRQ_TYPE_LEVEL_LOW; + + desc = &irq_desc[irq]; + desc->status &= ~(IRQ_TYPE_SENSE_MASK | IRQ_LEVEL); + desc->status |= flow_type & IRQ_TYPE_SENSE_MASK; + + if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) { + desc->status |= IRQ_LEVEL; + set_irq_handler(irq, handle_level_irq); + } else { + set_irq_handler(irq, handle_edge_irq); + } spin_lock_irqsave(&sm->lock, flags); @@ -148,10 +161,15 @@ static int __init eim_init(void) pattern = sm_readl(sm, EIM_MODE); nr_irqs = fls(pattern); + /* Trigger on falling edge unless overridden by driver */ + sm_writel(sm, EIM_MODE, 0UL); + sm_writel(sm, EIM_EDGE, 0UL); + sm->eim_chip = &eim_chip; for (i = 0; i < nr_irqs; i++) { - set_irq_chip(sm->eim_first_irq + i, &eim_chip); + set_irq_chip_and_handler(sm->eim_first_irq + i, &eim_chip, + handle_edge_irq); set_irq_chip_data(sm->eim_first_irq + i, sm); } -- cgit v0.10.2 From c2eb5090ee531a50533ba9e739071c21f98a5a77 Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Mon, 4 Dec 2006 12:01:36 +0100 Subject: [AVR32] Put the chip in "stop" mode when halting the system Make machine_halt() execute a sleep instruction to put the chip in "stop" mode when the system is halted. This switches off all clocks except the 32 kHz oscillator, which is needed for the RTC to keep ticking. Signed-off-by: Haavard Skinnemoen diff --git a/arch/avr32/kernel/process.c b/arch/avr32/kernel/process.c index 317dc50..0b43259 100644 --- a/arch/avr32/kernel/process.c +++ b/arch/avr32/kernel/process.c @@ -38,6 +38,13 @@ void cpu_idle(void) void machine_halt(void) { + /* + * Enter Stop mode. The 32 kHz oscillator will keep running so + * the RTC will keep the time properly and the system will + * boot quickly. + */ + asm volatile("sleep 3\n\t" + "sub pc, -2"); } void machine_power_off(void) -- cgit v0.10.2 From acc9252a36dcefbcdae12f88566300a80d925e11 Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Mon, 4 Dec 2006 14:17:39 +0100 Subject: [AVR32] Don't include Include instead of from a few places. Signed-off-by: Haavard Skinnemoen diff --git a/arch/avr32/kernel/avr32_ksyms.c b/arch/avr32/kernel/avr32_ksyms.c index 372e3f8..7c4c761 100644 --- a/arch/avr32/kernel/avr32_ksyms.c +++ b/arch/avr32/kernel/avr32_ksyms.c @@ -7,12 +7,12 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#include #include #include #include #include -#include /* * GCC functions diff --git a/arch/avr32/lib/delay.c b/arch/avr32/lib/delay.c index 462c830..76a8474 100644 --- a/arch/avr32/lib/delay.c +++ b/arch/avr32/lib/delay.c @@ -14,7 +14,6 @@ #include #include -#include #include #include -- cgit v0.10.2 From 695621183ee10eb22352bb919f50e160fa37aaa1 Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Fri, 8 Dec 2006 11:04:19 +0100 Subject: [AVR32] Implement intc_get_pending() intc_get_pending() returns a bitmask with pending interrupts in a interrupt controller group (irq). This is used by the upcoming oprofile implementation for avr32 and may also be useful for chained interrupt controller drivers. Signed-off-by: Haavard Skinnemoen diff --git a/arch/avr32/mach-at32ap/intc.c b/arch/avr32/mach-at32ap/intc.c index eb87a18..dd5c009 100644 --- a/arch/avr32/mach-at32ap/intc.c +++ b/arch/avr32/mach-at32ap/intc.c @@ -136,3 +136,7 @@ fail: panic("Interrupt controller initialization failed!\n"); } +unsigned long intc_get_pending(int group) +{ + return intc_readl(&intc0, INTREQ0 + 4 * group); +} -- cgit v0.10.2 From 50954ab327a9f860caef1a7c8353346b945cb316 Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Fri, 8 Dec 2006 12:53:26 +0100 Subject: [AVR32] Pass dev parameter to dma_cache_sync() Fix build breakage resulting from the extra dev parameter added to dma_cache_sync(). Signed-off-by: Haavard Skinnemoen diff --git a/include/asm-avr32/dma-mapping.h b/include/asm-avr32/dma-mapping.h index 0580b5d..5c01e27 100644 --- a/include/asm-avr32/dma-mapping.h +++ b/include/asm-avr32/dma-mapping.h @@ -109,7 +109,7 @@ static inline dma_addr_t dma_map_single(struct device *dev, void *cpu_addr, size_t size, enum dma_data_direction direction) { - dma_cache_sync(cpu_addr, size, direction); + dma_cache_sync(dev, cpu_addr, size, direction); return virt_to_bus(cpu_addr); } @@ -211,7 +211,7 @@ dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, sg[i].dma_address = page_to_bus(sg[i].page) + sg[i].offset; virt = page_address(sg[i].page) + sg[i].offset; - dma_cache_sync(virt, sg[i].length, direction); + dma_cache_sync(dev, virt, sg[i].length, direction); } return nents; @@ -256,14 +256,14 @@ static inline void dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, size_t size, enum dma_data_direction direction) { - dma_cache_sync(bus_to_virt(dma_handle), size, direction); + dma_cache_sync(dev, bus_to_virt(dma_handle), size, direction); } static inline void dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, size_t size, enum dma_data_direction direction) { - dma_cache_sync(bus_to_virt(dma_handle), size, direction); + dma_cache_sync(dev, bus_to_virt(dma_handle), size, direction); } /** @@ -286,7 +286,7 @@ dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int i; for (i = 0; i < nents; i++) { - dma_cache_sync(page_address(sg[i].page) + sg[i].offset, + dma_cache_sync(dev, page_address(sg[i].page) + sg[i].offset, sg[i].length, direction); } } @@ -298,7 +298,7 @@ dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int i; for (i = 0; i < nents; i++) { - dma_cache_sync(page_address(sg[i].page) + sg[i].offset, + dma_cache_sync(dev, page_address(sg[i].page) + sg[i].offset, sg[i].length, direction); } } -- cgit v0.10.2 From 3fc0eb47aa96b1d9230ff85b722c45c3b9e83d14 Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Fri, 8 Dec 2006 12:55:03 +0100 Subject: [AVR32] Add missing #include to delay.c __const_udelay() needs HZ, which is defined in . Signed-off-by: Haavard Skinnemoen diff --git a/arch/avr32/lib/delay.c b/arch/avr32/lib/delay.c index 76a8474..b3bc0b5 100644 --- a/arch/avr32/lib/delay.c +++ b/arch/avr32/lib/delay.c @@ -12,6 +12,7 @@ #include #include +#include #include #include -- cgit v0.10.2 From 4a0c50c07a6100ca58d465bac951533347e18d71 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Wed, 6 Dec 2006 21:52:32 +0200 Subject: [MTD] nandsim: bugfix in page addressing Number of address bytes for 64-128 MiB NANDs is 4, not 5. Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index 3d39451..c3bca95 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -160,7 +160,7 @@ MODULE_PARM_DESC(dbg, "Output debug information if not zero"); /* After a command is input, the simulator goes to one of the following states */ #define STATE_CMD_READ0 0x00000001 /* read data from the beginning of page */ #define STATE_CMD_READ1 0x00000002 /* read data from the second half of page */ -#define STATE_CMD_READSTART 0x00000003 /* read data second command (large page devices) */ +#define STATE_CMD_READSTART 0x00000003 /* read data second command (large page devices) */ #define STATE_CMD_PAGEPROG 0x00000004 /* start page programm */ #define STATE_CMD_READOOB 0x00000005 /* read OOB area */ #define STATE_CMD_ERASE1 0x00000006 /* sector erase first command */ @@ -440,7 +440,7 @@ static int init_nandsim(struct mtd_info *mtd) } } else { if (ns->geom.totsz <= (128 << 20)) { - ns->geom.pgaddrbytes = 5; + ns->geom.pgaddrbytes = 4; ns->geom.secaddrbytes = 2; } else { ns->geom.pgaddrbytes = 5; -- cgit v0.10.2 From dd11b8cdf0c455f4cfbc5daa70aabce9dcc6c07b Mon Sep 17 00:00:00 2001 From: Andrew Victor Date: Fri, 8 Dec 2006 13:49:42 +0200 Subject: [MTD] NAND: Support for 16-bit bus-width on AT91. Add support for 16-bit NAND bus-width for the AT91 NAND driver. The 16-bit NAND is found on the Atmel AT91SAM9260-EK and AT91SAM9261-EK boards. Orignal Patch from Patrice Vilchez Signed-off-by: Andrew Victor Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/at91_nand.c b/drivers/mtd/nand/at91_nand.c index a58ed37..14b80cc 100644 --- a/drivers/mtd/nand/at91_nand.c +++ b/drivers/mtd/nand/at91_nand.c @@ -128,6 +128,9 @@ static int __init at91_nand_probe(struct platform_device *pdev) nand_chip->ecc.mode = NAND_ECC_SOFT; /* enable ECC */ nand_chip->chip_delay = 20; /* 20us command delay time */ + if (host->board->bus_width_16) /* 16-bit bus width */ + nand_chip->options |= NAND_BUSWIDTH_16; + platform_set_drvdata(pdev, host); at91_nand_enable(host); -- cgit v0.10.2 From f33665d931f33a0baf44fc5d3594b23f8118eb44 Mon Sep 17 00:00:00 2001 From: Rod Whitby Date: Wed, 6 Dec 2006 12:11:15 +1030 Subject: [MTD] Support combined RedBoot FIS directory and configuration area RedBoot supports storing the FIS directory and the RedBoot configuration area in the same block of flash memory. This is not the most common RedBoot configuration, but it is used on commercially available boards supported by the kernel. A recent patch to mtd/redboot.c (http://lkml.org/lkml/2006/3/20/410) which corrected the skipping of deleted table entries has exposed the latent problem of the kernel redboot parser running off the end of the FIS directory and interpreting the RedBoot configuration information as table entries. This patch terminates the table parsing when the first truly empty entry is found (table entry deletion only clears the first byte of the name, so two cleared bytes in a row indicates the end of the table), thereby supporting the combined redboot FIS directory and RedBoot configuration information flash layout scenario. Signed-off-by: Rod Whitby Signed-off-by: David Woodhouse diff --git a/drivers/mtd/redboot.c b/drivers/mtd/redboot.c index b525921..035cd9b 100644 --- a/drivers/mtd/redboot.c +++ b/drivers/mtd/redboot.c @@ -96,7 +96,19 @@ static int parse_redboot_partitions(struct mtd_info *master, */ if (swab32(buf[i].size) == master->erasesize) { int j; - for (j = 0; j < numslots && buf[j].name[0] != 0xff; ++j) { + for (j = 0; j < numslots; ++j) { + + /* A single 0xff denotes a deleted entry. + * Two of them in a row is the end of the table. + */ + if (buf[j].name[0] == 0xff) { + if (buf[j].name[1] == 0xff) { + break; + } else { + continue; + } + } + /* The unsigned long fields were written with the * wrong byte sex, name and pad have no byte sex. */ @@ -126,8 +138,13 @@ static int parse_redboot_partitions(struct mtd_info *master, for (i = 0; i < numslots; i++) { struct fis_list *new_fl, **prev; - if (buf[i].name[0] == 0xff) - continue; + if (buf[i].name[0] == 0xff) { + if (buf[i].name[1] == 0xff) { + break; + } else { + continue; + } + } if (!redboot_checksum(&buf[i])) break; -- cgit v0.10.2 From a2c2fe4b242cb9c62951ae154594cffbb94ab2ad Mon Sep 17 00:00:00 2001 From: Vitaly Wool Date: Wed, 6 Dec 2006 13:17:49 +0300 Subject: [MTD] of_device-based physmap driver inlined below is the patch that adds physmap driver for of_device. It's an MTD part of the two-part support for flash/ROM devices based on Open Firmware descriptions. The arch part (currently only PowerPC which is no surprise) was introduced to powerpc folks earlier and recently the older version of the powerpc part has been included into the powerpc.git tree (see http://www.kernel.org/git/?p=linux/kernel/git/paulus/powerpc.git;a=commitdiff;h=28f9ec349ae47c91768b7bc5607db4442c818e11). drivers/mtd/maps/Kconfig | 9 + drivers/mtd/maps/Makefile | 1 drivers/mtd/maps/physmap_of.c | 255 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 265 insertions(+) Signed-off-by: Vitaly Wool Signed-off-by: Sergey Shtylyov Signed-off-by: David Woodhouse diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index fa74668..4a51a3d 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -60,6 +60,15 @@ config MTD_PHYSMAP_BANKWIDTH Ignore this option if you use run-time physmap configuration (i.e., run-time calling physmap_configure()). +config MTD_PHYSMAP_OF + tristate "Flash device in physical memory map based on OF descirption" + depends on PPC_OF && (MTD_CFI || MTD_JEDECPROBE || MTD_ROM) + help + This provides a 'mapping' driver which allows the NOR Flash and + ROM driver code to communicate with chips which are mapped + physically into the CPU's memory. The mapping description here is + taken from OF device tree. + config MTD_SUN_UFLASH tristate "Sun Microsystems userflash support" depends on SPARC && MTD_CFI diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 3450521..df019be 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_MTD_MBX860) += mbx860.o obj-$(CONFIG_MTD_CEIVA) += ceiva.o obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o obj-$(CONFIG_MTD_PHYSMAP) += physmap.o +obj-$(CONFIG_MTD_PHYSMAP_OF) += physmap_of.o obj-$(CONFIG_MTD_PNC2000) += pnc2000.o obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o obj-$(CONFIG_MTD_RPXLITE) += rpxlite.o diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c new file mode 100644 index 0000000..7efe744 --- /dev/null +++ b/drivers/mtd/maps/physmap_of.c @@ -0,0 +1,255 @@ +/* + * Normal mappings of chips in physical memory for OF devices + * + * Copyright (C) 2006 MontaVista Software Inc. + * Author: Vitaly Wool + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct physmap_flash_info { + struct mtd_info *mtd; + struct map_info map; + struct resource *res; +#ifdef CONFIG_MTD_PARTITIONS + int nr_parts; + struct mtd_partition *parts; +#endif +}; + +static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", "map_rom", NULL }; +#ifdef CONFIG_MTD_PARTITIONS +static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", NULL }; +#endif + +#ifdef CONFIG_MTD_PARTITIONS +static int parse_flash_partitions(struct device_node *node, + struct mtd_partition **parts) +{ + int i, plen, retval = -ENOMEM; + const u32 *part; + const char *name; + + part = get_property(node, "partitions", &plen); + if (part == NULL) + goto err; + + retval = plen / (2 * sizeof(u32)); + *parts = kzalloc(retval * sizeof(struct mtd_partition), GFP_KERNEL); + if (*parts == NULL) { + printk(KERN_ERR "Can't allocate the flash partition data!\n"); + goto err; + } + + name = get_property(node, "partition-names", &plen); + + for (i = 0; i < retval; i++) { + (*parts)[i].offset = *part++; + (*parts)[i].size = *part & ~1; + if (*part++ & 1) /* bit 0 set signifies read only partition */ + (*parts)[i].mask_flags = MTD_WRITEABLE; + + if (name != NULL && plen > 0) { + int len = strlen(name) + 1; + + (*parts)[i].name = (char *)name; + plen -= len; + name += len; + } else + (*parts)[i].name = "unnamed"; + } +err: + return retval; +} +#endif + +static int of_physmap_remove(struct of_device *dev) +{ + struct physmap_flash_info *info; + + info = dev_get_drvdata(&dev->dev); + if (info == NULL) + return 0; + dev_set_drvdata(&dev->dev, NULL); + + if (info->mtd != NULL) { +#ifdef CONFIG_MTD_PARTITIONS + if (info->nr_parts) { + del_mtd_partitions(info->mtd); + kfree(info->parts); + } else { + del_mtd_device(info->mtd); + } +#else + del_mtd_device(info->mtd); +#endif + map_destroy(info->mtd); + } + + if (info->map.virt != NULL) + iounmap(info->map.virt); + + if (info->res != NULL) { + release_resource(info->res); + kfree(info->res); + } + + return 0; +} + +static int __devinit of_physmap_probe(struct of_device *dev, const struct of_device_id *match) +{ + struct device_node *dp = dev->node; + struct resource res; + struct physmap_flash_info *info; + const char **probe_type; + const char *of_probe; + const u32 *width; + int err; + + + if (of_address_to_resource(dp, 0, &res)) { + dev_err(&dev->dev, "Can't get the flash mapping!\n"); + err = -EINVAL; + goto err_out; + } + + dev_dbg(&dev->dev, "physmap flash device: %.8llx at %.8llx\n", + (unsigned long long)res.end - res.start + 1, + (unsigned long long)res.start); + + info = kzalloc(sizeof(struct physmap_flash_info), GFP_KERNEL); + if (info == NULL) { + err = -ENOMEM; + goto err_out; + } + memset(info, 0, sizeof(*info)); + + dev_set_drvdata(&dev->dev, info); + + info->res = request_mem_region(res.start, res.end - res.start + 1, + dev->dev.bus_id); + if (info->res == NULL) { + dev_err(&dev->dev, "Could not reserve memory region\n"); + err = -ENOMEM; + goto err_out; + } + + width = get_property(dp, "bank-width", NULL); + if (width == NULL) { + dev_err(&dev->dev, "Can't get the flash bank width!\n"); + err = -EINVAL; + goto err_out; + } + + info->map.name = dev->dev.bus_id; + info->map.phys = res.start; + info->map.size = res.end - res.start + 1; + info->map.bankwidth = *width; + + info->map.virt = ioremap(info->map.phys, info->map.size); + if (info->map.virt == NULL) { + dev_err(&dev->dev, "Failed to ioremap flash region\n"); + err = EIO; + goto err_out; + } + + simple_map_init(&info->map); + + of_probe = get_property(dp, "probe-type", NULL); + if (of_probe == NULL) { + probe_type = rom_probe_types; + for (; info->mtd == NULL && *probe_type != NULL; probe_type++) + info->mtd = do_map_probe(*probe_type, &info->map); + } else if (!strcmp(of_probe, "CFI")) + info->mtd = do_map_probe("cfi_probe", &info->map); + else if (!strcmp(of_probe, "JEDEC")) + info->mtd = do_map_probe("jedec_probe", &info->map); + else { + if (strcmp(of_probe, "ROM")) + dev_dbg(&dev->dev, "map_probe: don't know probe type " + "'%s', mapping as rom\n"); + info->mtd = do_map_probe("mtd_rom", &info->map); + } + if (info->mtd == NULL) { + dev_err(&dev->dev, "map_probe failed\n"); + err = -ENXIO; + goto err_out; + } + info->mtd->owner = THIS_MODULE; + +#ifdef CONFIG_MTD_PARTITIONS + err = parse_mtd_partitions(info->mtd, part_probe_types, &info->parts, 0); + if (err > 0) { + add_mtd_partitions(info->mtd, info->parts, err); + } else if ((err = parse_flash_partitions(dp, &info->parts)) > 0) { + dev_info(&dev->dev, "Using OF partition information\n"); + add_mtd_partitions(info->mtd, info->parts, err); + info->nr_parts = err; + } else +#endif + + add_mtd_device(info->mtd); + return 0; + +err_out: + of_physmap_remove(dev); + return err; + + return 0; + + +} + +static struct of_device_id of_physmap_match[] = { + { + .type = "rom", + .compatible = "direct-mapped" + }, + { }, +}; + +MODULE_DEVICE_TABLE(of, of_physmap_match); + + +static struct of_platform_driver of_physmap_flash_driver = { + .name = "physmap-flash", + .match_table = of_physmap_match, + .probe = of_physmap_probe, + .remove = of_physmap_remove, +}; + +static int __init of_physmap_init(void) +{ + return of_register_platform_driver(&of_physmap_flash_driver); +} + +static void __exit of_physmap_exit(void) +{ + of_unregister_platform_driver(&of_physmap_flash_driver); +} + +module_init(of_physmap_init); +module_exit(of_physmap_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Vitaly Wool "); +MODULE_DESCRIPTION("Configurable MTD map driver for OF"); -- cgit v0.10.2 From 22155914b66b348b7113a0b3baf96a72bd3f643d Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Fri, 8 Dec 2006 15:53:49 +0100 Subject: [S390] uaccess_pt: add missing down_read() and convert to is_init(). Doesn't seem to be a good idea to duplicate code :) Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky diff --git a/arch/s390/lib/uaccess_pt.c b/arch/s390/lib/uaccess_pt.c index 8741bdc..633249c 100644 --- a/arch/s390/lib/uaccess_pt.c +++ b/arch/s390/lib/uaccess_pt.c @@ -8,8 +8,8 @@ */ #include -#include #include +#include #include static inline int __handle_fault(struct mm_struct *mm, unsigned long address, @@ -60,8 +60,9 @@ out: out_of_memory: up_read(&mm->mmap_sem); - if (current->pid == 1) { + if (is_init(current)) { yield(); + down_read(&mm->mmap_sem); goto survive; } printk("VM: killing process %s\n", current->comm); -- cgit v0.10.2 From e45ccc0562e3f391dcba8b2e8a02551e8e42d8db Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 8 Dec 2006 15:53:52 +0100 Subject: [S390] workqueue fixes. Cc: David Howells Cc: Heiko Carstens Signed-off-by: Andrew Morton Signed-off-by: Martin Schwidefsky diff --git a/drivers/s390/char/ctrlchar.c b/drivers/s390/char/ctrlchar.c index 49e9628..c6cbcb3 100644 --- a/drivers/s390/char/ctrlchar.c +++ b/drivers/s390/char/ctrlchar.c @@ -16,14 +16,15 @@ #ifdef CONFIG_MAGIC_SYSRQ static int ctrlchar_sysrq_key; +static struct tty_struct *sysrq_tty; static void -ctrlchar_handle_sysrq(void *tty) +ctrlchar_handle_sysrq(struct work_struct *work) { - handle_sysrq(ctrlchar_sysrq_key, (struct tty_struct *) tty); + handle_sysrq(ctrlchar_sysrq_key, sysrq_tty); } -static DECLARE_WORK(ctrlchar_work, ctrlchar_handle_sysrq, NULL); +static DECLARE_WORK(ctrlchar_work, ctrlchar_handle_sysrq); #endif @@ -53,7 +54,7 @@ ctrlchar_handle(const unsigned char *buf, int len, struct tty_struct *tty) /* racy */ if (len == 3 && buf[1] == '-') { ctrlchar_sysrq_key = buf[2]; - ctrlchar_work.data = tty; + sysrq_tty = tty; schedule_work(&ctrlchar_work); return CTRLCHAR_SYSRQ; } -- cgit v0.10.2 From c16375329c2ab4667df873394c4be7a61d163c62 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Fri, 8 Dec 2006 15:53:57 +0100 Subject: [S390] more workqueue fixes. Signed-off-by: Martin Schwidefsky diff --git a/drivers/s390/char/tape.h b/drivers/s390/char/tape.h index 1f4c899..c9f1c4c 100644 --- a/drivers/s390/char/tape.h +++ b/drivers/s390/char/tape.h @@ -179,6 +179,7 @@ struct tape_char_data { /* Block Frontend Data */ struct tape_blk_data { + struct tape_device * device; /* Block device request queue. */ request_queue_t * request_queue; spinlock_t request_queue_lock; @@ -240,7 +241,7 @@ struct tape_device { #endif /* Function to start or stop the next request later. */ - struct work_struct tape_dnr; + struct delayed_work tape_dnr; }; /* Externals from tape_core.c */ diff --git a/drivers/s390/char/tape_34xx.c b/drivers/s390/char/tape_34xx.c index 7b95dab..e765875 100644 --- a/drivers/s390/char/tape_34xx.c +++ b/drivers/s390/char/tape_34xx.c @@ -95,6 +95,12 @@ tape_34xx_medium_sense(struct tape_device *device) return rc; } +struct tape_34xx_work { + struct tape_device *device; + enum tape_op op; + struct work_struct work; +}; + /* * These functions are currently used only to schedule a medium_sense for * later execution. This is because we get an interrupt whenever a medium @@ -103,13 +109,10 @@ tape_34xx_medium_sense(struct tape_device *device) * interrupt handler. */ static void -tape_34xx_work_handler(void *data) +tape_34xx_work_handler(struct work_struct *work) { - struct { - struct tape_device *device; - enum tape_op op; - struct work_struct work; - } *p = data; + struct tape_34xx_work *p = + container_of(work, struct tape_34xx_work, work); switch(p->op) { case TO_MSEN: @@ -126,17 +129,13 @@ tape_34xx_work_handler(void *data) static int tape_34xx_schedule_work(struct tape_device *device, enum tape_op op) { - struct { - struct tape_device *device; - enum tape_op op; - struct work_struct work; - } *p; + struct tape_34xx_work *p; if ((p = kmalloc(sizeof(*p), GFP_ATOMIC)) == NULL) return -ENOMEM; memset(p, 0, sizeof(*p)); - INIT_WORK(&p->work, tape_34xx_work_handler, p); + INIT_WORK(&p->work, tape_34xx_work_handler); p->device = tape_get_device_reference(device); p->op = op; diff --git a/drivers/s390/char/tape_3590.c b/drivers/s390/char/tape_3590.c index 928cbef..9df912f 100644 --- a/drivers/s390/char/tape_3590.c +++ b/drivers/s390/char/tape_3590.c @@ -236,9 +236,10 @@ struct work_handler_data { }; static void -tape_3590_work_handler(void *data) +tape_3590_work_handler(struct work_struct *work) { - struct work_handler_data *p = data; + struct work_handler_data *p = + container_of(work, struct work_handler_data, work); switch (p->op) { case TO_MSEN: @@ -263,7 +264,7 @@ tape_3590_schedule_work(struct tape_device *device, enum tape_op op) if ((p = kzalloc(sizeof(*p), GFP_ATOMIC)) == NULL) return -ENOMEM; - INIT_WORK(&p->work, tape_3590_work_handler, p); + INIT_WORK(&p->work, tape_3590_work_handler); p->device = tape_get_device_reference(device); p->op = op; diff --git a/drivers/s390/char/tape_block.c b/drivers/s390/char/tape_block.c index 3225fcd..c8a89b3 100644 --- a/drivers/s390/char/tape_block.c +++ b/drivers/s390/char/tape_block.c @@ -15,6 +15,7 @@ #include #include #include +#include #include @@ -143,7 +144,8 @@ tapeblock_start_request(struct tape_device *device, struct request *req) * queue. */ static void -tapeblock_requeue(void *data) { +tapeblock_requeue(struct work_struct *work) { + struct tape_blk_data * blkdat; struct tape_device * device; request_queue_t * queue; int nr_queued; @@ -151,7 +153,8 @@ tapeblock_requeue(void *data) { struct list_head * l; int rc; - device = (struct tape_device *) data; + blkdat = container_of(work, struct tape_blk_data, requeue_task); + device = blkdat->device; if (!device) return; @@ -212,6 +215,7 @@ tapeblock_setup_device(struct tape_device * device) int rc; blkdat = &device->blk_data; + blkdat->device = device; spin_lock_init(&blkdat->request_queue_lock); atomic_set(&blkdat->requeue_scheduled, 0); @@ -255,8 +259,8 @@ tapeblock_setup_device(struct tape_device * device) add_disk(disk); - INIT_WORK(&blkdat->requeue_task, tapeblock_requeue, - tape_get_device_reference(device)); + tape_get_device_reference(device); + INIT_WORK(&blkdat->requeue_task, tapeblock_requeue); return 0; @@ -271,7 +275,7 @@ void tapeblock_cleanup_device(struct tape_device *device) { flush_scheduled_work(); - device->blk_data.requeue_task.data = tape_put_device(device); + tape_put_device(device); if (!device->blk_data.disk) { PRINT_ERR("(%s): No gendisk to clean up!\n", diff --git a/drivers/s390/char/tape_core.c b/drivers/s390/char/tape_core.c index 2826aed..c6c2e91 100644 --- a/drivers/s390/char/tape_core.c +++ b/drivers/s390/char/tape_core.c @@ -28,7 +28,7 @@ #define PRINTK_HEADER "TAPE_CORE: " static void __tape_do_irq (struct ccw_device *, unsigned long, struct irb *); -static void tape_delayed_next_request(void * data); +static void tape_delayed_next_request(struct work_struct *); /* * One list to contain all tape devices of all disciplines, so @@ -272,7 +272,7 @@ __tape_cancel_io(struct tape_device *device, struct tape_request *request) return 0; case -EBUSY: request->status = TAPE_REQUEST_CANCEL; - schedule_work(&device->tape_dnr); + schedule_delayed_work(&device->tape_dnr, 0); return 0; case -ENODEV: DBF_EXCEPTION(2, "device gone, retry\n"); @@ -470,7 +470,7 @@ tape_alloc_device(void) *device->modeset_byte = 0; device->first_minor = -1; atomic_set(&device->ref_count, 1); - INIT_WORK(&device->tape_dnr, tape_delayed_next_request, device); + INIT_DELAYED_WORK(&device->tape_dnr, tape_delayed_next_request); return device; } @@ -724,7 +724,7 @@ __tape_start_io(struct tape_device *device, struct tape_request *request) } else if (rc == -EBUSY) { /* The common I/O subsystem is currently busy. Retry later. */ request->status = TAPE_REQUEST_QUEUED; - schedule_work(&device->tape_dnr); + schedule_delayed_work(&device->tape_dnr, 0); rc = 0; } else { /* Start failed. Remove request and indicate failure. */ @@ -790,11 +790,11 @@ __tape_start_next_request(struct tape_device *device) } static void -tape_delayed_next_request(void *data) +tape_delayed_next_request(struct work_struct *work) { - struct tape_device * device; + struct tape_device *device = + container_of(work, struct tape_device, tape_dnr.work); - device = (struct tape_device *) data; DBF_LH(6, "tape_delayed_next_request(%p)\n", device); spin_lock_irq(get_ccwdev_lock(device->cdev)); __tape_start_next_request(device); diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index 9ff064e..dfd5462 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h @@ -73,6 +73,8 @@ struct senseid { } __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; diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index d3d3716..0f60462 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -585,12 +585,13 @@ static struct ccw_device * get_disc_ccwdev_by_dev_id(struct ccw_dev_id *dev_id, } static void -ccw_device_add_changed(void *data) +ccw_device_add_changed(struct work_struct *work) { - + struct ccw_device_private *priv; struct ccw_device *cdev; - cdev = data; + priv = container_of(work, struct ccw_device_private, kick_work); + cdev = priv->cdev; if (device_add(&cdev->dev)) { put_device(&cdev->dev); return; @@ -605,13 +606,15 @@ ccw_device_add_changed(void *data) extern int css_get_ssd_info(struct subchannel *sch); void -ccw_device_do_unreg_rereg(void *data) +ccw_device_do_unreg_rereg(struct work_struct *work) { + struct ccw_device_private *priv; struct ccw_device *cdev; struct subchannel *sch; int need_rename; - cdev = data; + priv = container_of(work, struct ccw_device_private, kick_work); + cdev = priv->cdev; sch = to_subchannel(cdev->dev.parent); if (cdev->private->dev_id.devno != sch->schib.pmcw.dev) { /* @@ -659,7 +662,7 @@ ccw_device_do_unreg_rereg(void *data) snprintf (cdev->dev.bus_id, BUS_ID_SIZE, "0.%x.%04x", sch->schid.ssid, sch->schib.pmcw.dev); PREPARE_WORK(&cdev->private->kick_work, - ccw_device_add_changed, cdev); + ccw_device_add_changed); queue_work(ccw_device_work, &cdev->private->kick_work); } @@ -677,14 +680,16 @@ ccw_device_release(struct device *dev) * Register recognized device. */ static void -io_subchannel_register(void *data) +io_subchannel_register(struct work_struct *work) { + struct ccw_device_private *priv; struct ccw_device *cdev; struct subchannel *sch; int ret; unsigned long flags; - cdev = data; + priv = container_of(work, struct ccw_device_private, kick_work); + cdev = priv->cdev; sch = to_subchannel(cdev->dev.parent); /* @@ -734,11 +739,14 @@ out: } void -ccw_device_call_sch_unregister(void *data) +ccw_device_call_sch_unregister(struct work_struct *work) { - struct ccw_device *cdev = data; + struct ccw_device_private *priv; + struct ccw_device *cdev; struct subchannel *sch; + priv = container_of(work, struct ccw_device_private, kick_work); + cdev = priv->cdev; sch = to_subchannel(cdev->dev.parent); css_sch_device_unregister(sch); /* Reset intparm to zeroes. */ @@ -768,7 +776,7 @@ io_subchannel_recog_done(struct ccw_device *cdev) break; sch = to_subchannel(cdev->dev.parent); PREPARE_WORK(&cdev->private->kick_work, - ccw_device_call_sch_unregister, cdev); + ccw_device_call_sch_unregister); queue_work(slow_path_wq, &cdev->private->kick_work); if (atomic_dec_and_test(&ccw_device_init_count)) wake_up(&ccw_device_init_wq); @@ -783,7 +791,7 @@ io_subchannel_recog_done(struct ccw_device *cdev) if (!get_device(&cdev->dev)) break; PREPARE_WORK(&cdev->private->kick_work, - io_subchannel_register, cdev); + io_subchannel_register); queue_work(slow_path_wq, &cdev->private->kick_work); break; } @@ -865,6 +873,7 @@ io_subchannel_probe (struct subchannel *sch) kfree(cdev); return -ENOMEM; } + cdev->private->cdev = cdev; atomic_set(&cdev->private->onoff, 0); cdev->dev.parent = &sch->dev; cdev->dev.release = ccw_device_release; @@ -890,12 +899,13 @@ io_subchannel_probe (struct subchannel *sch) return rc; } -static void -ccw_device_unregister(void *data) +static void ccw_device_unregister(struct work_struct *work) { + struct ccw_device_private *priv; struct ccw_device *cdev; - cdev = (struct ccw_device *)data; + priv = container_of(work, struct ccw_device_private, kick_work); + cdev = priv->cdev; if (test_and_clear_bit(1, &cdev->private->registered)) device_unregister(&cdev->dev); put_device(&cdev->dev); @@ -921,7 +931,7 @@ io_subchannel_remove (struct subchannel *sch) */ if (get_device(&cdev->dev)) { PREPARE_WORK(&cdev->private->kick_work, - ccw_device_unregister, cdev); + ccw_device_unregister); queue_work(ccw_device_work, &cdev->private->kick_work); } return 0; @@ -1048,6 +1058,7 @@ ccw_device_probe_console(void) memset(&console_cdev, 0, sizeof(struct ccw_device)); memset(&console_private, 0, sizeof(struct ccw_device_private)); console_cdev.private = &console_private; + console_private.cdev = &console_cdev; ret = ccw_device_console_enable(&console_cdev, sch); if (ret) { cio_release_console(); diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h index 9233b5c..d5fe95e 100644 --- a/drivers/s390/cio/device.h +++ b/drivers/s390/cio/device.h @@ -78,8 +78,8 @@ void io_subchannel_recog_done(struct ccw_device *cdev); int ccw_device_cancel_halt_clear(struct ccw_device *); -void ccw_device_do_unreg_rereg(void *); -void ccw_device_call_sch_unregister(void *); +void ccw_device_do_unreg_rereg(struct work_struct *); +void ccw_device_call_sch_unregister(struct work_struct *); int ccw_device_recognition(struct ccw_device *); int ccw_device_online(struct ccw_device *); diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 09c7672..0f0301c 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -194,7 +194,7 @@ ccw_device_handle_oper(struct ccw_device *cdev) cdev->id.dev_model != cdev->private->senseid.dev_model || cdev->private->dev_id.devno != sch->schib.pmcw.dev) { PREPARE_WORK(&cdev->private->kick_work, - ccw_device_do_unreg_rereg, cdev); + ccw_device_do_unreg_rereg); queue_work(ccw_device_work, &cdev->private->kick_work); return 0; } @@ -329,19 +329,21 @@ ccw_device_sense_id_done(struct ccw_device *cdev, int err) } static void -ccw_device_oper_notify(void *data) +ccw_device_oper_notify(struct work_struct *work) { + struct ccw_device_private *priv; struct ccw_device *cdev; struct subchannel *sch; int ret; - cdev = data; + priv = container_of(work, struct ccw_device_private, kick_work); + cdev = priv->cdev; sch = to_subchannel(cdev->dev.parent); ret = (sch->driver && sch->driver->notify) ? sch->driver->notify(&sch->dev, CIO_OPER) : 0; if (!ret) /* Driver doesn't want device back. */ - ccw_device_do_unreg_rereg(cdev); + ccw_device_do_unreg_rereg(work); else { /* Reenable channel measurements, if needed. */ cmf_reenable(cdev); @@ -377,8 +379,7 @@ ccw_device_done(struct ccw_device *cdev, int state) if (cdev->private->flags.donotify) { cdev->private->flags.donotify = 0; - PREPARE_WORK(&cdev->private->kick_work, ccw_device_oper_notify, - cdev); + PREPARE_WORK(&cdev->private->kick_work, ccw_device_oper_notify); queue_work(ccw_device_notify_work, &cdev->private->kick_work); } wake_up(&cdev->private->wait_q); @@ -528,13 +529,15 @@ ccw_device_recog_timeout(struct ccw_device *cdev, enum dev_event dev_event) static void -ccw_device_nopath_notify(void *data) +ccw_device_nopath_notify(struct work_struct *work) { + struct ccw_device_private *priv; struct ccw_device *cdev; struct subchannel *sch; int ret; - cdev = data; + priv = container_of(work, struct ccw_device_private, kick_work); + cdev = priv->cdev; sch = to_subchannel(cdev->dev.parent); /* Extra sanity. */ if (sch->lpm) @@ -547,8 +550,7 @@ ccw_device_nopath_notify(void *data) cio_disable_subchannel(sch); if (get_device(&cdev->dev)) { PREPARE_WORK(&cdev->private->kick_work, - ccw_device_call_sch_unregister, - cdev); + ccw_device_call_sch_unregister); queue_work(ccw_device_work, &cdev->private->kick_work); } else @@ -607,7 +609,7 @@ ccw_device_verify_done(struct ccw_device *cdev, int err) /* Reset oper notify indication after verify error. */ cdev->private->flags.donotify = 0; PREPARE_WORK(&cdev->private->kick_work, - ccw_device_nopath_notify, cdev); + ccw_device_nopath_notify); queue_work(ccw_device_notify_work, &cdev->private->kick_work); ccw_device_done(cdev, DEV_STATE_NOT_OPER); break; @@ -738,7 +740,7 @@ ccw_device_offline_notoper(struct ccw_device *cdev, enum dev_event dev_event) sch = to_subchannel(cdev->dev.parent); if (get_device(&cdev->dev)) { PREPARE_WORK(&cdev->private->kick_work, - ccw_device_call_sch_unregister, cdev); + ccw_device_call_sch_unregister); queue_work(ccw_device_work, &cdev->private->kick_work); } wake_up(&cdev->private->wait_q); @@ -769,7 +771,7 @@ ccw_device_online_notoper(struct ccw_device *cdev, enum dev_event dev_event) } if (get_device(&cdev->dev)) { PREPARE_WORK(&cdev->private->kick_work, - ccw_device_call_sch_unregister, cdev); + ccw_device_call_sch_unregister); queue_work(ccw_device_work, &cdev->private->kick_work); } wake_up(&cdev->private->wait_q); @@ -874,7 +876,7 @@ ccw_device_online_timeout(struct ccw_device *cdev, enum dev_event dev_event) sch = to_subchannel(cdev->dev.parent); if (!sch->lpm) { PREPARE_WORK(&cdev->private->kick_work, - ccw_device_nopath_notify, cdev); + ccw_device_nopath_notify); queue_work(ccw_device_notify_work, &cdev->private->kick_work); } else @@ -969,7 +971,7 @@ ccw_device_killing_irq(struct ccw_device *cdev, enum dev_event dev_event) ERR_PTR(-EIO)); if (!sch->lpm) { PREPARE_WORK(&cdev->private->kick_work, - ccw_device_nopath_notify, cdev); + ccw_device_nopath_notify); queue_work(ccw_device_notify_work, &cdev->private->kick_work); } else if (cdev->private->flags.doverify) /* Start delayed path verification. */ @@ -992,7 +994,7 @@ ccw_device_killing_timeout(struct ccw_device *cdev, enum dev_event dev_event) sch = to_subchannel(cdev->dev.parent); if (!sch->lpm) { PREPARE_WORK(&cdev->private->kick_work, - ccw_device_nopath_notify, cdev); + ccw_device_nopath_notify); queue_work(ccw_device_notify_work, &cdev->private->kick_work); } else @@ -1021,7 +1023,7 @@ void device_kill_io(struct subchannel *sch) if (ret == -ENODEV) { if (!sch->lpm) { PREPARE_WORK(&cdev->private->kick_work, - ccw_device_nopath_notify, cdev); + ccw_device_nopath_notify); queue_work(ccw_device_notify_work, &cdev->private->kick_work); } else @@ -1033,7 +1035,7 @@ void device_kill_io(struct subchannel *sch) ERR_PTR(-EIO)); if (!sch->lpm) { PREPARE_WORK(&cdev->private->kick_work, - ccw_device_nopath_notify, cdev); + ccw_device_nopath_notify); queue_work(ccw_device_notify_work, &cdev->private->kick_work); } else /* Start delayed path verification. */ diff --git a/drivers/s390/cio/qdio.c b/drivers/s390/cio/qdio.c index 8d5fa1b..d066dbf 100644 --- a/drivers/s390/cio/qdio.c +++ b/drivers/s390/cio/qdio.c @@ -2045,11 +2045,13 @@ omit_handler_call: } static void -qdio_call_shutdown(void *data) +qdio_call_shutdown(struct work_struct *work) { + struct ccw_device_private *priv; struct ccw_device *cdev; - cdev = (struct ccw_device *)data; + priv = container_of(work, struct ccw_device_private, kick_work); + cdev = priv->cdev; qdio_shutdown(cdev, QDIO_FLAG_CLEANUP_USING_CLEAR); put_device(&cdev->dev); } @@ -2091,7 +2093,7 @@ qdio_timeout_handler(struct ccw_device *cdev) if (get_device(&cdev->dev)) { /* Can't call shutdown from interrupt context. */ PREPARE_WORK(&cdev->private->kick_work, - qdio_call_shutdown, (void *)cdev); + qdio_call_shutdown); queue_work(ccw_device_work, &cdev->private->kick_work); } break; -- cgit v0.10.2 From 85eca8503997cf3a869b159954f703530c865299 Mon Sep 17 00:00:00 2001 From: Ralph Wuerthner Date: Fri, 8 Dec 2006 15:54:07 +0100 Subject: [S390] add reset call handler to the ap bus. Signed-off-by: Ralph Wuerthner Signed-off-by: Martin Schwidefsky diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index e4dc947..ad60afe 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "ap_bus.h" @@ -1128,6 +1129,19 @@ static void ap_poll_thread_stop(void) mutex_unlock(&ap_poll_thread_mutex); } +static void ap_reset(void) +{ + int i, j; + + for (i = 0; i < AP_DOMAINS; i++) + for (j = 0; j < AP_DEVICES; j++) + ap_reset_queue(AP_MKQID(j, i)); +} + +static struct reset_call ap_reset_call = { + .fn = ap_reset, +}; + /** * The module initialization code. */ @@ -1144,6 +1158,7 @@ int __init ap_module_init(void) printk(KERN_WARNING "AP instructions not installed.\n"); return -ENODEV; } + register_reset_call(&ap_reset_call); /* Create /sys/bus/ap. */ rc = bus_register(&ap_bus_type); @@ -1197,6 +1212,7 @@ out_bus: bus_remove_file(&ap_bus_type, ap_bus_attrs[i]); bus_unregister(&ap_bus_type); out: + unregister_reset_call(&ap_reset_call); return rc; } @@ -1227,6 +1243,7 @@ void ap_module_exit(void) for (i = 0; ap_bus_attrs[i]; i++) bus_remove_file(&ap_bus_type, ap_bus_attrs[i]); bus_unregister(&ap_bus_type); + unregister_reset_call(&ap_reset_call); } #ifndef CONFIG_ZCRYPT_MONOLITHIC -- cgit v0.10.2 From 9575bf265711cabe7147a68003a56a9f19f034da Mon Sep 17 00:00:00 2001 From: Horst Hummel Date: Fri, 8 Dec 2006 15:54:15 +0100 Subject: [S390] New DASD feature for ERP related logging It is now possible to enable/disable ERP related logging without re-compile and re-ipl. A additional sysfs-attribute 'erplog' allows to switch the logging non-interruptive. Signed-off-by: Horst Hummel Signed-off-by: Martin Schwidefsky diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 2af2d9b..492b68b 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -1050,10 +1050,10 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, } } else { /* error */ memcpy(&cqr->irb, irb, sizeof (struct irb)); -#ifdef ERP_DEBUG - /* dump sense data */ - dasd_log_sense(cqr, irb); -#endif + 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; diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c index 669805d..4d01040 100644 --- a/drivers/s390/block/dasd_3990_erp.c +++ b/drivers/s390/block/dasd_3990_erp.c @@ -2641,14 +2641,12 @@ dasd_3990_erp_action(struct dasd_ccw_req * cqr) struct dasd_ccw_req *erp = NULL; struct dasd_device *device = cqr->device; __u32 cpa = cqr->irb.scsw.cpa; + struct dasd_ccw_req *temp_erp = NULL; -#ifdef ERP_DEBUG - /* print current erp_chain */ - DEV_MESSAGE(KERN_ERR, device, "%s", - "ERP chain at BEGINNING of ERP-ACTION"); - { - struct dasd_ccw_req *temp_erp = NULL; - + if (device->features & DASD_FEATURE_ERPLOG) { + /* print current erp_chain */ + DEV_MESSAGE(KERN_ERR, device, "%s", + "ERP chain at BEGINNING of ERP-ACTION"); for (temp_erp = cqr; temp_erp != NULL; temp_erp = temp_erp->refers) { @@ -2658,7 +2656,6 @@ dasd_3990_erp_action(struct dasd_ccw_req * cqr) temp_erp->refers); } } -#endif /* ERP_DEBUG */ /* double-check if current erp/cqr was successfull */ if ((cqr->irb.scsw.cstat == 0x00) && @@ -2695,11 +2692,10 @@ dasd_3990_erp_action(struct dasd_ccw_req * cqr) erp = dasd_3990_erp_handle_match_erp(cqr, erp); } -#ifdef ERP_DEBUG - /* print current erp_chain */ - DEV_MESSAGE(KERN_ERR, device, "%s", "ERP chain at END of ERP-ACTION"); - { - struct dasd_ccw_req *temp_erp = NULL; + if (device->features & DASD_FEATURE_ERPLOG) { + /* print current erp_chain */ + DEV_MESSAGE(KERN_ERR, device, "%s", + "ERP chain at END of ERP-ACTION"); for (temp_erp = erp; temp_erp != NULL; temp_erp = temp_erp->refers) { @@ -2709,7 +2705,6 @@ dasd_3990_erp_action(struct dasd_ccw_req * cqr) temp_erp->refers); } } -#endif /* ERP_DEBUG */ if (erp->status == DASD_CQR_FAILED) dasd_log_ccw(erp, 1, cpa); diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c index cf28ccc..5943266 100644 --- a/drivers/s390/block/dasd_devmap.c +++ b/drivers/s390/block/dasd_devmap.c @@ -202,6 +202,8 @@ dasd_feature_list(char *str, char **endp) features |= DASD_FEATURE_READONLY; else if (len == 4 && !strncmp(str, "diag", 4)) features |= DASD_FEATURE_USEDIAG; + else if (len == 6 && !strncmp(str, "erplog", 6)) + features |= DASD_FEATURE_ERPLOG; else { MESSAGE(KERN_WARNING, "unsupported feature: %*s, " @@ -709,6 +711,52 @@ dasd_ro_store(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR(readonly, 0644, dasd_ro_show, dasd_ro_store); +/* + * erplog controls the logging of ERP related data + * (e.g. failing channel programs). + */ +static ssize_t +dasd_erplog_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct dasd_devmap *devmap; + int erplog; + + devmap = dasd_find_busid(dev->bus_id); + if (!IS_ERR(devmap)) + erplog = (devmap->features & DASD_FEATURE_ERPLOG) != 0; + else + erplog = (DASD_FEATURE_DEFAULT & DASD_FEATURE_ERPLOG) != 0; + return snprintf(buf, PAGE_SIZE, erplog ? "1\n" : "0\n"); +} + +static ssize_t +dasd_erplog_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct dasd_devmap *devmap; + int val; + char *endp; + + devmap = dasd_devmap_from_cdev(to_ccwdev(dev)); + if (IS_ERR(devmap)) + return PTR_ERR(devmap); + + val = simple_strtoul(buf, &endp, 0); + if (((endp + 1) < (buf + count)) || (val > 1)) + return -EINVAL; + + spin_lock(&dasd_devmap_lock); + if (val) + devmap->features |= DASD_FEATURE_ERPLOG; + else + devmap->features &= ~DASD_FEATURE_ERPLOG; + if (devmap->device) + devmap->device->features = devmap->features; + spin_unlock(&dasd_devmap_lock); + return count; +} + +static DEVICE_ATTR(erplog, 0644, dasd_erplog_show, dasd_erplog_store); /* * use_diag controls whether the driver should use diag rather than ssch @@ -896,6 +944,7 @@ static struct attribute * dasd_attrs[] = { &dev_attr_uid.attr, &dev_attr_use_diag.attr, &dev_attr_eer_enabled.attr, + &dev_attr_erplog.attr, NULL, }; diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index dc5dd50..fb725e3 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -13,10 +13,6 @@ #ifdef __KERNEL__ -/* erp debugging in dasd.c and dasd_3990_erp.c */ -#define ERP_DEBUG - - /* we keep old device allocation scheme; IOW, minors are still in 0..255 */ #define DASD_PER_MAJOR (1U << (MINORBITS - DASD_PARTN_BITS)) #define DASD_PARTN_MASK ((1 << DASD_PARTN_BITS) - 1) diff --git a/include/asm-s390/dasd.h b/include/asm-s390/dasd.h index c042f95..604f68f 100644 --- a/include/asm-s390/dasd.h +++ b/include/asm-s390/dasd.h @@ -69,11 +69,13 @@ typedef struct dasd_information2_t { * 0x01: readonly (ro) * 0x02: use diag discipline (diag) * 0x04: set the device initially online (internal use only) + * 0x08: enable ERP related logging */ #define DASD_FEATURE_DEFAULT 0x00 #define DASD_FEATURE_READONLY 0x01 #define DASD_FEATURE_USEDIAG 0x02 #define DASD_FEATURE_INITIAL_ONLINE 0x04 +#define DASD_FEATURE_ERPLOG 0x08 #define DASD_PARTN_BITS 2 -- cgit v0.10.2 From 34249d0f9243fce773c2fa352934ba108320e234 Mon Sep 17 00:00:00 2001 From: Ursula Braun Date: Fri, 8 Dec 2006 15:54:18 +0100 Subject: [S390] runtime switch for qdio performance statistics Remove CONFIG_QETH_PERF_STATS and use a sysfs attribute instead. We want to have the ability to turn the statistics on/off at runtime. Signed-off-by: Ursula Braun Signed-off-by: Martin Schwidefsky diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index 583d9ff..a08e910 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -258,14 +258,6 @@ config QDIO If unsure, say Y. -config QDIO_PERF_STATS - bool "Performance statistics in /proc" - depends on QDIO - help - Say Y here to get performance statistics in /proc/qdio_perf - - If unsure, say N. - config QDIO_DEBUG bool "Extended debugging information" depends on QDIO diff --git a/arch/s390/defconfig b/arch/s390/defconfig index 7cd51e7..a6ec919 100644 --- a/arch/s390/defconfig +++ b/arch/s390/defconfig @@ -134,7 +134,6 @@ CONFIG_RESOURCES_64BIT=y # CONFIG_MACHCHK_WARNING=y CONFIG_QDIO=y -# CONFIG_QDIO_PERF_STATS is not set # CONFIG_QDIO_DEBUG is not set # diff --git a/drivers/s390/cio/qdio.c b/drivers/s390/cio/qdio.c index d066dbf..9d4ea44 100644 --- a/drivers/s390/cio/qdio.c +++ b/drivers/s390/cio/qdio.c @@ -46,6 +46,7 @@ #include #include +#include #include #include "cio.h" @@ -65,12 +66,12 @@ MODULE_LICENSE("GPL"); /******************** HERE WE GO ***********************************/ static const char version[] = "QDIO base support version 2"; +extern struct bus_type ccw_bus_type; -#ifdef QDIO_PERFORMANCE_STATS +static int qdio_performance_stats = 0; static int proc_perf_file_registration; static unsigned long i_p_c, i_p_nc, o_p_c, o_p_nc, ii_p_c, ii_p_nc; static struct qdio_perf_stats perf_stats; -#endif /* QDIO_PERFORMANCE_STATS */ static int hydra_thinints; static int is_passthrough = 0; @@ -275,9 +276,8 @@ qdio_siga_sync(struct qdio_q *q, unsigned int gpr2, QDIO_DBF_TEXT4(0,trace,"sigasync"); QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); -#ifdef QDIO_PERFORMANCE_STATS - perf_stats.siga_syncs++; -#endif /* QDIO_PERFORMANCE_STATS */ + if (qdio_performance_stats) + perf_stats.siga_syncs++; cc = do_siga_sync(q->schid, gpr2, gpr3); if (cc) @@ -322,9 +322,8 @@ qdio_siga_output(struct qdio_q *q) __u32 busy_bit; __u64 start_time=0; -#ifdef QDIO_PERFORMANCE_STATS - perf_stats.siga_outs++; -#endif /* QDIO_PERFORMANCE_STATS */ + if (qdio_performance_stats) + perf_stats.siga_outs++; QDIO_DBF_TEXT4(0,trace,"sigaout"); QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); @@ -358,9 +357,8 @@ qdio_siga_input(struct qdio_q *q) QDIO_DBF_TEXT4(0,trace,"sigain"); QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); -#ifdef QDIO_PERFORMANCE_STATS - perf_stats.siga_ins++; -#endif /* QDIO_PERFORMANCE_STATS */ + if (qdio_performance_stats) + perf_stats.siga_ins++; cc = do_siga_input(q->schid, q->mask); @@ -954,9 +952,8 @@ __qdio_outbound_processing(struct qdio_q *q) if (unlikely(qdio_reserve_q(q))) { qdio_release_q(q); -#ifdef QDIO_PERFORMANCE_STATS - o_p_c++; -#endif /* QDIO_PERFORMANCE_STATS */ + if (qdio_performance_stats) + o_p_c++; /* as we're sissies, we'll check next time */ if (likely(!atomic_read(&q->is_in_shutdown))) { qdio_mark_q(q); @@ -964,10 +961,10 @@ __qdio_outbound_processing(struct qdio_q *q) } return; } -#ifdef QDIO_PERFORMANCE_STATS - o_p_nc++; - perf_stats.tl_runs++; -#endif /* QDIO_PERFORMANCE_STATS */ + if (qdio_performance_stats) { + o_p_nc++; + perf_stats.tl_runs++; + } /* see comment in qdio_kick_outbound_q */ siga_attempts=atomic_read(&q->busy_siga_counter); @@ -1142,15 +1139,16 @@ qdio_has_inbound_q_moved(struct qdio_q *q) { int i; -#ifdef QDIO_PERFORMANCE_STATS static int old_pcis=0; static int old_thinints=0; - if ((old_pcis==perf_stats.pcis)&&(old_thinints==perf_stats.thinints)) - perf_stats.start_time_inbound=NOW; - else - old_pcis=perf_stats.pcis; -#endif /* QDIO_PERFORMANCE_STATS */ + if (qdio_performance_stats) { + if ((old_pcis==perf_stats.pcis)&& + (old_thinints==perf_stats.thinints)) + perf_stats.start_time_inbound=NOW; + else + old_pcis=perf_stats.pcis; + } i=qdio_get_inbound_buffer_frontier(q); if ( (i!=GET_SAVED_FRONTIER(q)) || @@ -1340,10 +1338,10 @@ qdio_kick_inbound_handler(struct qdio_q *q) q->siga_error=0; q->error_status_flags=0; -#ifdef QDIO_PERFORMANCE_STATS - perf_stats.inbound_time+=NOW-perf_stats.start_time_inbound; - perf_stats.inbound_cnt++; -#endif /* QDIO_PERFORMANCE_STATS */ + if (qdio_performance_stats) { + perf_stats.inbound_time+=NOW-perf_stats.start_time_inbound; + perf_stats.inbound_cnt++; + } } static inline void @@ -1363,9 +1361,8 @@ __tiqdio_inbound_processing(struct qdio_q *q, int spare_ind_was_set) */ if (unlikely(qdio_reserve_q(q))) { qdio_release_q(q); -#ifdef QDIO_PERFORMANCE_STATS - ii_p_c++; -#endif /* QDIO_PERFORMANCE_STATS */ + if (qdio_performance_stats) + ii_p_c++; /* * as we might just be about to stop polling, we make * sure that we check again at least once more @@ -1373,9 +1370,8 @@ __tiqdio_inbound_processing(struct qdio_q *q, int spare_ind_was_set) tiqdio_sched_tl(); return; } -#ifdef QDIO_PERFORMANCE_STATS - ii_p_nc++; -#endif /* QDIO_PERFORMANCE_STATS */ + if (qdio_performance_stats) + ii_p_nc++; if (unlikely(atomic_read(&q->is_in_shutdown))) { qdio_unmark_q(q); goto out; @@ -1416,11 +1412,11 @@ __tiqdio_inbound_processing(struct qdio_q *q, int spare_ind_was_set) irq_ptr = (struct qdio_irq*)q->irq_ptr; for (i=0;ino_output_qs;i++) { oq = irq_ptr->output_qs[i]; -#ifdef QDIO_PERFORMANCE_STATS - perf_stats.tl_runs--; -#endif /* QDIO_PERFORMANCE_STATS */ - if (!qdio_is_outbound_q_done(oq)) + if (!qdio_is_outbound_q_done(oq)) { + if (qdio_performance_stats) + perf_stats.tl_runs--; __qdio_outbound_processing(oq); + } } } @@ -1457,9 +1453,8 @@ __qdio_inbound_processing(struct qdio_q *q) if (unlikely(qdio_reserve_q(q))) { qdio_release_q(q); -#ifdef QDIO_PERFORMANCE_STATS - i_p_c++; -#endif /* QDIO_PERFORMANCE_STATS */ + if (qdio_performance_stats) + i_p_c++; /* as we're sissies, we'll check next time */ if (likely(!atomic_read(&q->is_in_shutdown))) { qdio_mark_q(q); @@ -1467,10 +1462,10 @@ __qdio_inbound_processing(struct qdio_q *q) } return; } -#ifdef QDIO_PERFORMANCE_STATS - i_p_nc++; - perf_stats.tl_runs++; -#endif /* QDIO_PERFORMANCE_STATS */ + if (qdio_performance_stats) { + i_p_nc++; + perf_stats.tl_runs++; + } again: if (qdio_has_inbound_q_moved(q)) { @@ -1516,9 +1511,8 @@ tiqdio_reset_processing_state(struct qdio_q *q, int q_laps) if (unlikely(qdio_reserve_q(q))) { qdio_release_q(q); -#ifdef QDIO_PERFORMANCE_STATS - ii_p_c++; -#endif /* QDIO_PERFORMANCE_STATS */ + if (qdio_performance_stats) + ii_p_c++; /* * as we might just be about to stop polling, we make * sure that we check again at least once more @@ -1609,9 +1603,8 @@ tiqdio_tl(unsigned long data) { QDIO_DBF_TEXT4(0,trace,"iqdio_tl"); -#ifdef QDIO_PERFORMANCE_STATS - perf_stats.tl_runs++; -#endif /* QDIO_PERFORMANCE_STATS */ + if (qdio_performance_stats) + perf_stats.tl_runs++; tiqdio_inbound_checks(); } @@ -1918,10 +1911,10 @@ tiqdio_thinint_handler(void) { QDIO_DBF_TEXT4(0,trace,"thin_int"); -#ifdef QDIO_PERFORMANCE_STATS - perf_stats.thinints++; - perf_stats.start_time_inbound=NOW; -#endif /* QDIO_PERFORMANCE_STATS */ + if (qdio_performance_stats) { + perf_stats.thinints++; + perf_stats.start_time_inbound=NOW; + } /* SVS only when needed: * issue SVS to benefit from iqdio interrupt avoidance @@ -1976,18 +1969,17 @@ qdio_handle_pci(struct qdio_irq *irq_ptr) int i; struct qdio_q *q; -#ifdef QDIO_PERFORMANCE_STATS - perf_stats.pcis++; - perf_stats.start_time_inbound=NOW; -#endif /* QDIO_PERFORMANCE_STATS */ + if (qdio_performance_stats) { + perf_stats.pcis++; + perf_stats.start_time_inbound=NOW; + } for (i=0;ino_input_qs;i++) { q=irq_ptr->input_qs[i]; if (q->is_input_q&QDIO_FLAG_NO_INPUT_INTERRUPT_CONTEXT) qdio_mark_q(q); else { -#ifdef QDIO_PERFORMANCE_STATS - perf_stats.tl_runs--; -#endif /* QDIO_PERFORMANCE_STATS */ + if (qdio_performance_stats) + perf_stats.tl_runs--; __qdio_inbound_processing(q); } } @@ -1995,11 +1987,10 @@ qdio_handle_pci(struct qdio_irq *irq_ptr) return; for (i=0;ino_output_qs;i++) { q=irq_ptr->output_qs[i]; -#ifdef QDIO_PERFORMANCE_STATS - perf_stats.tl_runs--; -#endif /* QDIO_PERFORMANCE_STATS */ if (qdio_is_outbound_q_done(q)) continue; + if (qdio_performance_stats) + perf_stats.tl_runs--; if (!irq_ptr->sync_done_on_outb_pcis) SYNC_MEMORY; __qdio_outbound_processing(q); @@ -3460,19 +3451,18 @@ do_qdio_handle_outbound(struct qdio_q *q, unsigned int callflags, struct qdio_irq *irq = (struct qdio_irq *) q->irq_ptr; /* This is the outbound handling of queues */ -#ifdef QDIO_PERFORMANCE_STATS - perf_stats.start_time_outbound=NOW; -#endif /* QDIO_PERFORMANCE_STATS */ + if (qdio_performance_stats) + perf_stats.start_time_outbound=NOW; qdio_do_qdio_fill_output(q,qidx,count,buffers); used_elements=atomic_add_return(count, &q->number_of_buffers_used) - count; if (callflags&QDIO_FLAG_DONT_SIGA) { -#ifdef QDIO_PERFORMANCE_STATS - perf_stats.outbound_time+=NOW-perf_stats.start_time_outbound; - perf_stats.outbound_cnt++; -#endif /* QDIO_PERFORMANCE_STATS */ + if (qdio_performance_stats) { + perf_stats.outbound_time+=NOW-perf_stats.start_time_outbound; + perf_stats.outbound_cnt++; + } return; } if (q->is_iqdio_q) { @@ -3502,9 +3492,8 @@ do_qdio_handle_outbound(struct qdio_q *q, unsigned int callflags, qdio_kick_outbound_q(q); } else { QDIO_DBF_TEXT3(0,trace, "fast-req"); -#ifdef QDIO_PERFORMANCE_STATS - perf_stats.fast_reqs++; -#endif /* QDIO_PERFORMANCE_STATS */ + if (qdio_performance_stats) + perf_stats.fast_reqs++; } } /* @@ -3515,10 +3504,10 @@ do_qdio_handle_outbound(struct qdio_q *q, unsigned int callflags, __qdio_outbound_processing(q); } -#ifdef QDIO_PERFORMANCE_STATS - perf_stats.outbound_time+=NOW-perf_stats.start_time_outbound; - perf_stats.outbound_cnt++; -#endif /* QDIO_PERFORMANCE_STATS */ + if (qdio_performance_stats) { + perf_stats.outbound_time+=NOW-perf_stats.start_time_outbound; + perf_stats.outbound_cnt++; + } } /* count must be 1 in iqdio */ @@ -3576,7 +3565,6 @@ do_QDIO(struct ccw_device *cdev,unsigned int callflags, return 0; } -#ifdef QDIO_PERFORMANCE_STATS static int qdio_perf_procfile_read(char *buffer, char **buffer_location, off_t offset, int buffer_length, int *eof, void *data) @@ -3592,29 +3580,29 @@ qdio_perf_procfile_read(char *buffer, char **buffer_location, off_t offset, _OUTP_IT("i_p_nc/c=%lu/%lu\n",i_p_nc,i_p_c); _OUTP_IT("ii_p_nc/c=%lu/%lu\n",ii_p_nc,ii_p_c); _OUTP_IT("o_p_nc/c=%lu/%lu\n",o_p_nc,o_p_c); - _OUTP_IT("Number of tasklet runs (total) : %u\n", + _OUTP_IT("Number of tasklet runs (total) : %lu\n", perf_stats.tl_runs); _OUTP_IT("\n"); - _OUTP_IT("Number of SIGA sync's issued : %u\n", + _OUTP_IT("Number of SIGA sync's issued : %lu\n", perf_stats.siga_syncs); - _OUTP_IT("Number of SIGA in's issued : %u\n", + _OUTP_IT("Number of SIGA in's issued : %lu\n", perf_stats.siga_ins); - _OUTP_IT("Number of SIGA out's issued : %u\n", + _OUTP_IT("Number of SIGA out's issued : %lu\n", perf_stats.siga_outs); - _OUTP_IT("Number of PCIs caught : %u\n", + _OUTP_IT("Number of PCIs caught : %lu\n", perf_stats.pcis); - _OUTP_IT("Number of adapter interrupts caught : %u\n", + _OUTP_IT("Number of adapter interrupts caught : %lu\n", perf_stats.thinints); - _OUTP_IT("Number of fast requeues (outg. SBALs w/o SIGA) : %u\n", + _OUTP_IT("Number of fast requeues (outg. SBALs w/o SIGA) : %lu\n", perf_stats.fast_reqs); _OUTP_IT("\n"); - _OUTP_IT("Total time of all inbound actions (us) incl. UL : %u\n", + _OUTP_IT("Total time of all inbound actions (us) incl. UL : %lu\n", perf_stats.inbound_time); - _OUTP_IT("Number of inbound transfers : %u\n", + _OUTP_IT("Number of inbound transfers : %lu\n", perf_stats.inbound_cnt); - _OUTP_IT("Total time of all outbound do_QDIOs (us) : %u\n", + _OUTP_IT("Total time of all outbound do_QDIOs (us) : %lu\n", perf_stats.outbound_time); - _OUTP_IT("Number of do_QDIOs outbound : %u\n", + _OUTP_IT("Number of do_QDIOs outbound : %lu\n", perf_stats.outbound_cnt); _OUTP_IT("\n"); @@ -3622,12 +3610,10 @@ qdio_perf_procfile_read(char *buffer, char **buffer_location, off_t offset, } static struct proc_dir_entry *qdio_perf_proc_file; -#endif /* QDIO_PERFORMANCE_STATS */ static void qdio_add_procfs_entry(void) { -#ifdef QDIO_PERFORMANCE_STATS proc_perf_file_registration=0; qdio_perf_proc_file=create_proc_entry(QDIO_PERF, S_IFREG|0444,&proc_root); @@ -3639,20 +3625,58 @@ qdio_add_procfs_entry(void) QDIO_PRINT_WARN("was not able to register perf. " \ "proc-file (%i).\n", proc_perf_file_registration); -#endif /* QDIO_PERFORMANCE_STATS */ } static void qdio_remove_procfs_entry(void) { -#ifdef QDIO_PERFORMANCE_STATS perf_stats.tl_runs=0; if (!proc_perf_file_registration) /* means if it went ok earlier */ remove_proc_entry(QDIO_PERF,&proc_root); -#endif /* QDIO_PERFORMANCE_STATS */ } +/** + * attributes in sysfs + *****************************************************************************/ + +static ssize_t +qdio_performance_stats_show(struct bus_type *bus, char *buf) +{ + return sprintf(buf, "%i\n", qdio_performance_stats ? 1 : 0); +} + +static ssize_t +qdio_performance_stats_store(struct bus_type *bus, const char *buf, size_t count) +{ + char *tmp; + int i; + + i = simple_strtoul(buf, &tmp, 16); + if ((i == 0) || (i == 1)) { + if (i == qdio_performance_stats) + return count; + qdio_performance_stats = i; + if (i==0) { + /* reset perf. stat. info */ + i_p_nc = 0; + i_p_c = 0; + ii_p_nc = 0; + ii_p_c = 0; + o_p_nc = 0; + o_p_c = 0; + memset(&perf_stats, 0, sizeof(struct qdio_perf_stats)); + } + } else { + QDIO_PRINT_WARN("QDIO performance_stats: write 0 or 1 to this file!\n"); + return -EINVAL; + } + return count; +} + +static BUS_ATTR(qdio_performance_stats, 0644, qdio_performance_stats_show, + qdio_performance_stats_store); + static void tiqdio_register_thinints(void) { @@ -3697,6 +3721,7 @@ qdio_release_qdio_memory(void) kfree(indicators); } + static void qdio_unregister_dbf_views(void) { @@ -3798,9 +3823,7 @@ static int __init init_QDIO(void) { int res; -#ifdef QDIO_PERFORMANCE_STATS void *ptr; -#endif /* QDIO_PERFORMANCE_STATS */ printk("qdio: loading %s\n",version); @@ -3813,13 +3836,12 @@ init_QDIO(void) return res; QDIO_DBF_TEXT0(0,setup,"initQDIO"); + res = bus_create_file(&ccw_bus_type, &bus_attr_qdio_performance_stats); -#ifdef QDIO_PERFORMANCE_STATS - memset((void*)&perf_stats,0,sizeof(perf_stats)); + memset((void*)&perf_stats,0,sizeof(perf_stats)); QDIO_DBF_TEXT0(0,setup,"perfstat"); ptr=&perf_stats; QDIO_DBF_HEX0(0,setup,&ptr,sizeof(void*)); -#endif /* QDIO_PERFORMANCE_STATS */ qdio_add_procfs_entry(); @@ -3843,7 +3865,7 @@ cleanup_QDIO(void) qdio_release_qdio_memory(); qdio_unregister_dbf_views(); mempool_destroy(qdio_mempool_scssc); - + bus_remove_file(&ccw_bus_type, &bus_attr_qdio_performance_stats); printk("qdio: %s: module removed\n",version); } diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h index 42927c1..ec9af72 100644 --- a/drivers/s390/cio/qdio.h +++ b/drivers/s390/cio/qdio.h @@ -12,10 +12,6 @@ #endif /* CONFIG_QDIO_DEBUG */ #define QDIO_USE_PROCESSING_STATE -#ifdef CONFIG_QDIO_PERF_STATS -#define QDIO_PERFORMANCE_STATS -#endif /* CONFIG_QDIO_PERF_STATS */ - #define QDIO_MINIMAL_BH_RELIEF_TIME 16 #define QDIO_TIMER_POLL_VALUE 1 #define IQDIO_TIMER_POLL_VALUE 1 @@ -409,25 +405,23 @@ do_clear_global_summary(void) #define CHSC_FLAG_SIGA_SYNC_DONE_ON_THININTS 0x08 #define CHSC_FLAG_SIGA_SYNC_DONE_ON_OUTB_PCIS 0x04 -#ifdef QDIO_PERFORMANCE_STATS struct qdio_perf_stats { - unsigned int tl_runs; + unsigned long tl_runs; - unsigned int siga_outs; - unsigned int siga_ins; - unsigned int siga_syncs; - unsigned int pcis; - unsigned int thinints; - unsigned int fast_reqs; + unsigned long siga_outs; + unsigned long siga_ins; + unsigned long siga_syncs; + unsigned long pcis; + unsigned long thinints; + unsigned long fast_reqs; __u64 start_time_outbound; - unsigned int outbound_cnt; - unsigned int outbound_time; + unsigned long outbound_cnt; + unsigned long outbound_time; __u64 start_time_inbound; - unsigned int inbound_cnt; - unsigned int inbound_time; + unsigned long inbound_cnt; + unsigned long inbound_time; }; -#endif /* QDIO_PERFORMANCE_STATS */ /* unlikely as the later the better */ #define SYNC_MEMORY if (unlikely(q->siga_sync)) qdio_siga_sync_q(q) -- cgit v0.10.2 From 7674da77cb2d27ae6559c55151da171ceb02beb4 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Fri, 8 Dec 2006 15:54:21 +0100 Subject: [S390] Some preparations for the dynamic subchannel mapping patch. - Move adding subchannel attributes to css_register_subchannel(). - Don't call device_trigger_reprobe() for non-operational devices. - Introduce io_subchannel_create_ccwdev(). Signed-off-by: Cornelia Huck Cc: Greg KH Signed-off-by: Andrew Morton Signed-off-by: Martin Schwidefsky diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 26cf2f5..55895c8 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -135,14 +135,19 @@ css_register_subchannel(struct subchannel *sch) sch->dev.parent = &css[0]->device; sch->dev.bus = &css_bus_type; sch->dev.release = &css_subchannel_release; - + /* make it known to the system */ ret = css_sch_device_register(sch); - if (ret) + if (ret) { printk (KERN_WARNING "%s: could not register %s\n", __func__, sch->dev.bus_id); - else - css_get_ssd_info(sch); + return ret; + } + css_get_ssd_info(sch); + ret = subchannel_add_files(&sch->dev); + if (ret) + printk(KERN_WARNING "%s: could not add attributes to %s\n", + __func__, sch->dev.bus_id); return ret; } diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index dfd5462..ac845c1 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h @@ -189,4 +189,6 @@ extern int need_rescan; extern struct workqueue_struct *slow_path_wq; extern struct work_struct slow_path_work; + +int subchannel_add_files (struct device *); #endif diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 0f60462..e644fd6 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -294,6 +294,18 @@ online_show (struct device *dev, struct device_attribute *attr, char *buf) return sprintf(buf, cdev->online ? "1\n" : "0\n"); } +static void ccw_device_unregister(struct work_struct *work) +{ + struct ccw_device_private *priv; + struct ccw_device *cdev; + + priv = container_of(work, struct ccw_device_private, kick_work); + cdev = priv->cdev; + if (test_and_clear_bit(1, &cdev->private->registered)) + device_unregister(&cdev->dev); + put_device(&cdev->dev); +} + static void ccw_device_remove_disconnected(struct ccw_device *cdev) { @@ -498,8 +510,7 @@ static struct attribute_group subch_attr_group = { .attrs = subch_attrs, }; -static inline int -subchannel_add_files (struct device *dev) +int subchannel_add_files (struct device *dev) { return sysfs_create_group(&dev->kobj, &subch_attr_group); } @@ -676,6 +687,55 @@ ccw_device_release(struct device *dev) kfree(cdev); } +static struct ccw_device * io_subchannel_allocate_dev(struct subchannel *sch) +{ + struct ccw_device *cdev; + + cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); + if (cdev) { + cdev->private = kzalloc(sizeof(struct ccw_device_private), + GFP_KERNEL | GFP_DMA); + if (cdev->private) + return cdev; + } + kfree(cdev); + return ERR_PTR(-ENOMEM); +} + +static int io_subchannel_initialize_dev(struct subchannel *sch, + struct ccw_device *cdev) +{ + cdev->private->cdev = cdev; + atomic_set(&cdev->private->onoff, 0); + cdev->dev.parent = &sch->dev; + cdev->dev.release = ccw_device_release; + INIT_LIST_HEAD(&cdev->private->kick_work.entry); + /* Do first half of device_register. */ + device_initialize(&cdev->dev); + if (!get_device(&sch->dev)) { + if (cdev->dev.release) + cdev->dev.release(&cdev->dev); + return -ENODEV; + } + return 0; +} + +static struct ccw_device * io_subchannel_create_ccwdev(struct subchannel *sch) +{ + struct ccw_device *cdev; + int ret; + + cdev = io_subchannel_allocate_dev(sch); + if (!IS_ERR(cdev)) { + ret = io_subchannel_initialize_dev(sch, cdev); + if (ret) { + kfree(cdev); + cdev = ERR_PTR(ret); + } + } + return cdev; +} + /* * Register recognized device. */ @@ -724,11 +784,6 @@ io_subchannel_register(struct work_struct *work) wake_up(&ccw_device_init_wq); return; } - - ret = subchannel_add_files(cdev->dev.parent); - if (ret) - printk(KERN_WARNING "%s: could not add attributes to %s\n", - __func__, sch->dev.bus_id); put_device(&cdev->dev); out: cdev->private->flags.recog_done = 1; @@ -851,7 +906,6 @@ io_subchannel_probe (struct subchannel *sch) cdev = sch->dev.driver_data; device_initialize(&cdev->dev); ccw_device_register(cdev); - subchannel_add_files(&sch->dev); /* * Check if the device is already online. If it is * the reference count needs to be corrected @@ -864,28 +918,9 @@ io_subchannel_probe (struct subchannel *sch) get_device(&cdev->dev); return 0; } - cdev = kzalloc (sizeof(*cdev), GFP_KERNEL); - if (!cdev) - return -ENOMEM; - cdev->private = kzalloc(sizeof(struct ccw_device_private), - GFP_KERNEL | GFP_DMA); - if (!cdev->private) { - kfree(cdev); - return -ENOMEM; - } - cdev->private->cdev = cdev; - atomic_set(&cdev->private->onoff, 0); - cdev->dev.parent = &sch->dev; - cdev->dev.release = ccw_device_release; - INIT_LIST_HEAD(&cdev->private->kick_work.entry); - /* Do first half of device_register. */ - device_initialize(&cdev->dev); - - if (!get_device(&sch->dev)) { - if (cdev->dev.release) - cdev->dev.release(&cdev->dev); - return -ENODEV; - } + cdev = io_subchannel_create_ccwdev(sch); + if (IS_ERR(cdev)) + return PTR_ERR(cdev); rc = io_subchannel_recog(cdev, sch); if (rc) { @@ -899,18 +934,6 @@ io_subchannel_probe (struct subchannel *sch) return rc; } -static void ccw_device_unregister(struct work_struct *work) -{ - struct ccw_device_private *priv; - struct ccw_device *cdev; - - priv = container_of(work, struct ccw_device_private, kick_work); - cdev = priv->cdev; - if (test_and_clear_bit(1, &cdev->private->registered)) - device_unregister(&cdev->dev); - put_device(&cdev->dev); -} - static int io_subchannel_remove (struct subchannel *sch) { diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 0f0301c..a487fb0 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -1106,7 +1106,8 @@ device_trigger_reprobe(struct subchannel *sch) /* Update some values. */ if (stsch(sch->schid, &sch->schib)) return; - + if (!sch->schib.pmcw.dnv) + return; /* * The pim, pam, pom values may not be accurate, but they are the best * we have before performing device selection :/ -- cgit v0.10.2 From 2ec2298412e1ab4674b3780005058d4f0b8bd858 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Fri, 8 Dec 2006 15:54:26 +0100 Subject: [S390] subchannel lock conversion. Convert the subchannel lock to a pointer to a lock. Needed for the dynamic subchannel mapping patch. Signed-off-by: Cornelia Huck Cc: Greg KH Signed-off-by: Andrew Morton Signed-off-by: Martin Schwidefsky diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index dbfb77b..cbab8d2 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -183,7 +183,7 @@ css_get_ssd_info(struct subchannel *sch) page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); if (!page) return -ENOMEM; - spin_lock_irq(&sch->lock); + spin_lock_irq(sch->lock); ret = chsc_get_sch_desc_irq(sch, page); if (ret) { static int cio_chsc_err_msg; @@ -197,7 +197,7 @@ css_get_ssd_info(struct subchannel *sch) cio_chsc_err_msg = 1; } } - spin_unlock_irq(&sch->lock); + spin_unlock_irq(sch->lock); free_page((unsigned long)page); if (!ret) { int j, chpid, mask; @@ -233,7 +233,7 @@ s390_subchannel_remove_chpid(struct device *dev, void *data) if (j >= 8) return 0; - spin_lock_irq(&sch->lock); + spin_lock_irq(sch->lock); stsch(sch->schid, &schib); if (!schib.pmcw.dnv) @@ -265,10 +265,10 @@ s390_subchannel_remove_chpid(struct device *dev, void *data) else if (sch->lpm == mask) goto out_unreg; out_unlock: - spin_unlock_irq(&sch->lock); + spin_unlock_irq(sch->lock); return 0; out_unreg: - spin_unlock_irq(&sch->lock); + spin_unlock_irq(sch->lock); sch->lpm = 0; if (css_enqueue_subchannel_slow(sch->schid)) { css_clear_subchannel_slow_list(); @@ -378,12 +378,12 @@ __s390_process_res_acc(struct subchannel_id schid, void *data) /* Check if a subchannel is newly available. */ return s390_process_res_acc_new_sch(schid); - spin_lock_irq(&sch->lock); + spin_lock_irq(sch->lock); chp_mask = s390_process_res_acc_sch(res_data, sch); if (chp_mask == 0) { - spin_unlock_irq(&sch->lock); + spin_unlock_irq(sch->lock); put_device(&sch->dev); return 0; } @@ -397,7 +397,7 @@ __s390_process_res_acc(struct subchannel_id schid, void *data) else if (sch->driver && sch->driver->verify) sch->driver->verify(&sch->dev); - spin_unlock_irq(&sch->lock); + spin_unlock_irq(sch->lock); put_device(&sch->dev); return 0; } @@ -635,21 +635,21 @@ __chp_add(struct subchannel_id schid, void *data) if (!sch) /* Check if the subchannel is now available. */ return __chp_add_new_sch(schid); - spin_lock_irq(&sch->lock); + 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] == chp->id)) { if (stsch(sch->schid, &sch->schib) != 0) { /* Endgame. */ - spin_unlock_irq(&sch->lock); + spin_unlock_irq(sch->lock); return -ENXIO; } break; } } if (i==8) { - spin_unlock_irq(&sch->lock); + spin_unlock_irq(sch->lock); return 0; } sch->lpm = ((sch->schib.pmcw.pim & @@ -660,7 +660,7 @@ __chp_add(struct subchannel_id schid, void *data) if (sch->driver && sch->driver->verify) sch->driver->verify(&sch->dev); - spin_unlock_irq(&sch->lock); + spin_unlock_irq(sch->lock); put_device(&sch->dev); return 0; } @@ -750,7 +750,7 @@ __s390_subchannel_vary_chpid(struct subchannel *sch, __u8 chpid, int on) if (!sch->ssd_info.valid) return; - spin_lock_irqsave(&sch->lock, flags); + spin_lock_irqsave(sch->lock, flags); old_lpm = sch->lpm; for (chp = 0; chp < 8; chp++) { if (sch->ssd_info.chpid[chp] != chpid) @@ -785,7 +785,7 @@ __s390_subchannel_vary_chpid(struct subchannel *sch, __u8 chpid, int on) sch->driver->verify(&sch->dev); break; } - spin_unlock_irqrestore(&sch->lock, flags); + spin_unlock_irqrestore(sch->lock, flags); } static int diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index 20aee27..e8d3314 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -143,11 +143,11 @@ cio_tpi(void) return 1; local_bh_disable(); irq_enter (); - spin_lock(&sch->lock); + spin_lock(sch->lock); memcpy (&sch->schib.scsw, &irb->scsw, sizeof (struct scsw)); if (sch->driver && sch->driver->irq) sch->driver->irq(&sch->dev); - spin_unlock(&sch->lock); + spin_unlock(sch->lock); irq_exit (); _local_bh_enable(); return 1; @@ -496,6 +496,15 @@ cio_disable_subchannel (struct subchannel *sch) return ret; } +static int cio_create_sch_lock(struct subchannel *sch) +{ + sch->lock = kmalloc(sizeof(spinlock_t), GFP_KERNEL); + if (!sch->lock) + return -ENOMEM; + spin_lock_init(sch->lock); + return 0; +} + /* * cio_validate_subchannel() * @@ -513,6 +522,7 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid) { char dbf_txt[15]; int ccode; + int err; sprintf (dbf_txt, "valsch%x", schid.sch_no); CIO_TRACE_EVENT (4, dbf_txt); @@ -520,9 +530,15 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid) /* Nuke all fields. */ memset(sch, 0, sizeof(struct subchannel)); - spin_lock_init(&sch->lock); + sch->schid = schid; + if (cio_is_console(schid)) { + sch->lock = cio_get_console_lock(); + } else { + err = cio_create_sch_lock(sch); + if (err) + goto out; + } mutex_init(&sch->reg_mutex); - /* Set a name for the subchannel */ snprintf (sch->dev.bus_id, BUS_ID_SIZE, "0.%x.%04x", schid.ssid, schid.sch_no); @@ -534,10 +550,10 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid) * is not valid. */ ccode = stsch_err (schid, &sch->schib); - if (ccode) - return (ccode == 3) ? -ENXIO : ccode; - - sch->schid = schid; + if (ccode) { + err = (ccode == 3) ? -ENXIO : ccode; + goto out; + } /* Copy subchannel type from path management control word. */ sch->st = sch->schib.pmcw.st; @@ -550,14 +566,16 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid) "non-I/O subchannel type %04X\n", sch->schid.ssid, sch->schid.sch_no, sch->st); /* We stop here for non-io subchannels. */ - return sch->st; + err = sch->st; + goto out; } /* Initialization for io subchannels. */ - if (!sch->schib.pmcw.dnv) + if (!sch->schib.pmcw.dnv) { /* io subchannel but device number is invalid. */ - return -ENODEV; - + err = -ENODEV; + goto out; + } /* Devno is valid. */ if (is_blacklisted (sch->schid.ssid, sch->schib.pmcw.dev)) { /* @@ -567,7 +585,8 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid) CIO_MSG_EVENT(0, "Blacklisted device detected " "at devno %04X, subchannel set %x\n", sch->schib.pmcw.dev, sch->schid.ssid); - return -ENODEV; + err = -ENODEV; + goto out; } sch->opm = 0xff; if (!cio_is_console(sch->schid)) @@ -595,6 +614,11 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid) if ((sch->lpm & (sch->lpm - 1)) != 0) sch->schib.pmcw.mp = 1; /* multipath mode */ return 0; +out: + if (!cio_is_console(schid)) + kfree(sch->lock); + sch->lock = NULL; + return err; } /* @@ -637,7 +661,7 @@ do_IRQ (struct pt_regs *regs) } sch = (struct subchannel *)(unsigned long)tpi_info->intparm; if (sch) - spin_lock(&sch->lock); + spin_lock(sch->lock); /* Store interrupt response block to lowcore. */ if (tsch (tpi_info->schid, irb) == 0 && sch) { /* Keep subchannel information word up to date. */ @@ -648,7 +672,7 @@ do_IRQ (struct pt_regs *regs) sch->driver->irq(&sch->dev); } if (sch) - spin_unlock(&sch->lock); + spin_unlock(sch->lock); /* * Are more interrupts pending? * If so, the tpi instruction will update the lowcore @@ -687,10 +711,10 @@ wait_cons_dev (void) __ctl_load (cr6, 6, 6); do { - spin_unlock(&console_subchannel.lock); + spin_unlock(console_subchannel.lock); if (!cio_tpi()) cpu_relax(); - spin_lock(&console_subchannel.lock); + spin_lock(console_subchannel.lock); } while (console_subchannel.schib.scsw.actl != 0); /* * restore previous isc value diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h index 4541c1a..7e7369b 100644 --- a/drivers/s390/cio/cio.h +++ b/drivers/s390/cio/cio.h @@ -87,7 +87,7 @@ struct orb { /* subchannel data structure used by I/O subroutines */ struct subchannel { struct subchannel_id schid; - spinlock_t lock; /* subchannel lock */ + spinlock_t *lock; /* subchannel lock */ struct mutex reg_mutex; enum { SUBCHANNEL_TYPE_IO = 0, @@ -137,9 +137,11 @@ extern struct subchannel *cio_probe_console(void); 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); #else #define cio_is_console(schid) 0 #define cio_get_console_subchannel() NULL +#define cio_get_console_lock() NULL; #endif extern int cio_show_msg; diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 55895c8..65939e2 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -91,9 +91,9 @@ css_free_subchannel(struct subchannel *sch) /* Reset intparm to zeroes. */ sch->schib.pmcw.intparm = 0; cio_modify(sch); + kfree(sch->lock); kfree(sch); } - } static void @@ -102,8 +102,10 @@ css_subchannel_release(struct device *dev) struct subchannel *sch; sch = to_subchannel(dev); - if (!cio_is_console(sch->schid)) + if (!cio_is_console(sch->schid)) { + kfree(sch->lock); kfree(sch); + } } extern int css_get_ssd_info(struct subchannel *sch); @@ -206,18 +208,18 @@ static int css_evaluate_known_subchannel(struct subchannel *sch, int slow) unsigned long flags; enum { NONE, UNREGISTER, UNREGISTER_PROBE, REPROBE } action; - spin_lock_irqsave(&sch->lock, flags); + spin_lock_irqsave(sch->lock, flags); disc = device_is_disconnected(sch); if (disc && slow) { /* Disconnected devices are evaluated directly only.*/ - spin_unlock_irqrestore(&sch->lock, flags); + spin_unlock_irqrestore(sch->lock, flags); return 0; } /* No interrupt after machine check - kill pending timers. */ device_kill_pending_timer(sch); if (!disc && !slow) { /* Non-disconnected devices are evaluated on the slow path. */ - spin_unlock_irqrestore(&sch->lock, flags); + spin_unlock_irqrestore(sch->lock, flags); return -EAGAIN; } event = css_get_subchannel_status(sch); @@ -242,9 +244,9 @@ static int css_evaluate_known_subchannel(struct subchannel *sch, int slow) /* Ask driver what to do with device. */ action = UNREGISTER; if (sch->driver && sch->driver->notify) { - spin_unlock_irqrestore(&sch->lock, flags); + spin_unlock_irqrestore(sch->lock, flags); ret = sch->driver->notify(&sch->dev, event); - spin_lock_irqsave(&sch->lock, flags); + spin_lock_irqsave(sch->lock, flags); if (ret) action = NONE; } @@ -269,9 +271,9 @@ static int css_evaluate_known_subchannel(struct subchannel *sch, int slow) case UNREGISTER: case UNREGISTER_PROBE: /* Unregister device (will use subchannel lock). */ - spin_unlock_irqrestore(&sch->lock, flags); + spin_unlock_irqrestore(sch->lock, flags); css_sch_device_unregister(sch); - spin_lock_irqsave(&sch->lock, flags); + spin_lock_irqsave(sch->lock, flags); /* Reset intparm to zeroes. */ sch->schib.pmcw.intparm = 0; @@ -283,7 +285,7 @@ static int css_evaluate_known_subchannel(struct subchannel *sch, int slow) default: break; } - spin_unlock_irqrestore(&sch->lock, flags); + spin_unlock_irqrestore(sch->lock, flags); /* Probe if necessary. */ if (action == UNREGISTER_PROBE) ret = css_probe_device(sch->schid); diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index e644fd6..9a31239f 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -774,9 +774,9 @@ io_subchannel_register(struct work_struct *work) printk (KERN_WARNING "%s: could not register %s\n", __func__, cdev->dev.bus_id); put_device(&cdev->dev); - spin_lock_irqsave(&sch->lock, flags); + spin_lock_irqsave(sch->lock, flags); sch->dev.driver_data = NULL; - spin_unlock_irqrestore(&sch->lock, flags); + spin_unlock_irqrestore(sch->lock, flags); kfree (cdev->private); kfree (cdev); put_device(&sch->dev); @@ -860,7 +860,7 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch) sch->dev.driver_data = cdev; sch->driver = &io_subchannel_driver; - cdev->ccwlock = &sch->lock; + cdev->ccwlock = sch->lock; /* Init private data. */ priv = cdev->private; @@ -880,9 +880,9 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch) atomic_inc(&ccw_device_init_count); /* Start async. device sensing. */ - spin_lock_irq(&sch->lock); + spin_lock_irq(sch->lock); rc = ccw_device_recognition(cdev); - spin_unlock_irq(&sch->lock); + spin_unlock_irq(sch->lock); if (rc) { if (atomic_dec_and_test(&ccw_device_init_count)) wake_up(&ccw_device_init_wq); @@ -924,9 +924,9 @@ io_subchannel_probe (struct subchannel *sch) rc = io_subchannel_recog(cdev, sch); if (rc) { - spin_lock_irqsave(&sch->lock, flags); + spin_lock_irqsave(sch->lock, flags); sch->dev.driver_data = NULL; - spin_unlock_irqrestore(&sch->lock, flags); + spin_unlock_irqrestore(sch->lock, flags); if (cdev->dev.release) cdev->dev.release(&cdev->dev); } @@ -1036,6 +1036,13 @@ static struct ccw_device console_cdev; static struct ccw_device_private console_private; static int console_cdev_in_use; +static DEFINE_SPINLOCK(ccw_console_lock); + +spinlock_t * cio_get_console_lock(void) +{ + return &ccw_console_lock; +} + static int ccw_device_console_enable (struct ccw_device *cdev, struct subchannel *sch) { diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c index b39c1fa..d269607 100644 --- a/drivers/s390/cio/device_ops.c +++ b/drivers/s390/cio/device_ops.c @@ -316,9 +316,9 @@ __ccw_device_retry_loop(struct ccw_device *cdev, struct ccw1 *ccw, long magic, _ ccw_device_set_timeout(cdev, 0); if (ret == -EBUSY) { /* Try again later. */ - spin_unlock_irq(&sch->lock); + spin_unlock_irq(sch->lock); msleep(10); - spin_lock_irq(&sch->lock); + spin_lock_irq(sch->lock); continue; } if (ret != 0) @@ -326,12 +326,12 @@ __ccw_device_retry_loop(struct ccw_device *cdev, struct ccw1 *ccw, long magic, _ break; /* Wait for end of request. */ cdev->private->intparm = magic; - spin_unlock_irq(&sch->lock); + spin_unlock_irq(sch->lock); wait_event(cdev->private->wait_q, (cdev->private->intparm == -EIO) || (cdev->private->intparm == -EAGAIN) || (cdev->private->intparm == 0)); - spin_lock_irq(&sch->lock); + spin_lock_irq(sch->lock); /* Check at least for channel end / device end */ if (cdev->private->intparm == -EIO) { /* Non-retryable error. */ @@ -342,9 +342,9 @@ __ccw_device_retry_loop(struct ccw_device *cdev, struct ccw1 *ccw, long magic, _ /* Success. */ break; /* Try again later. */ - spin_unlock_irq(&sch->lock); + spin_unlock_irq(sch->lock); msleep(10); - spin_lock_irq(&sch->lock); + spin_lock_irq(sch->lock); } while (1); return ret; @@ -389,7 +389,7 @@ read_dev_chars (struct ccw_device *cdev, void **buffer, int length) return ret; } - spin_lock_irq(&sch->lock); + spin_lock_irq(sch->lock); /* Save interrupt handler. */ handler = cdev->handler; /* Temporarily install own handler. */ @@ -406,7 +406,7 @@ read_dev_chars (struct ccw_device *cdev, void **buffer, int length) /* Restore interrupt handler. */ cdev->handler = handler; - spin_unlock_irq(&sch->lock); + spin_unlock_irq(sch->lock); clear_normalized_cda (rdc_ccw); kfree(rdc_ccw); @@ -463,7 +463,7 @@ read_conf_data_lpm (struct ccw_device *cdev, void **buffer, int *length, __u8 lp rcd_ccw->count = ciw->count; rcd_ccw->flags = CCW_FLAG_SLI; - spin_lock_irq(&sch->lock); + spin_lock_irq(sch->lock); /* Save interrupt handler. */ handler = cdev->handler; /* Temporarily install own handler. */ @@ -480,7 +480,7 @@ read_conf_data_lpm (struct ccw_device *cdev, void **buffer, int *length, __u8 lp /* Restore interrupt handler. */ cdev->handler = handler; - spin_unlock_irq(&sch->lock); + spin_unlock_irq(sch->lock); /* * on success we update the user input parms @@ -537,7 +537,7 @@ ccw_device_stlck(struct ccw_device *cdev) kfree(buf); return -ENOMEM; } - spin_lock_irqsave(&sch->lock, flags); + spin_lock_irqsave(sch->lock, flags); ret = cio_enable_subchannel(sch, 3); if (ret) goto out_unlock; @@ -559,9 +559,9 @@ ccw_device_stlck(struct ccw_device *cdev) goto out_unlock; } cdev->private->irb.scsw.actl |= SCSW_ACTL_START_PEND; - spin_unlock_irqrestore(&sch->lock, flags); + spin_unlock_irqrestore(sch->lock, flags); wait_event(cdev->private->wait_q, cdev->private->irb.scsw.actl == 0); - spin_lock_irqsave(&sch->lock, flags); + spin_lock_irqsave(sch->lock, flags); cio_disable_subchannel(sch); //FIXME: return code? if ((cdev->private->irb.scsw.dstat != (DEV_STAT_CHN_END|DEV_STAT_DEV_END)) || @@ -572,7 +572,7 @@ ccw_device_stlck(struct ccw_device *cdev) out_unlock: kfree(buf); kfree(buf2); - spin_unlock_irqrestore(&sch->lock, flags); + spin_unlock_irqrestore(sch->lock, flags); return ret; } -- cgit v0.10.2 From d7b5a4c94f49131811112526f7d404a50f0b5ca7 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Fri, 8 Dec 2006 15:54:28 +0100 Subject: [S390] Support for disconnected devices reappearing on another subchannel. - create a 'pseudo_subchannel' per channel subsystem (the 'orphanage') - use the orphanage as a shelter for ccw_devices that can't remain on the same subchannel Signed-off-by: Cornelia Huck Cc: Greg KH Signed-off-by: Andrew Morton Signed-off-by: Martin Schwidefsky diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index e8d3314..7835a71 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -415,6 +415,8 @@ cio_enable_subchannel (struct subchannel *sch, unsigned int isc) CIO_TRACE_EVENT (2, "ensch"); CIO_TRACE_EVENT (2, sch->dev.bus_id); + if (sch_is_pseudo_sch(sch)) + return -EINVAL; ccode = stsch (sch->schid, &sch->schib); if (ccode) return -ENODEV; @@ -462,6 +464,8 @@ cio_disable_subchannel (struct subchannel *sch) CIO_TRACE_EVENT (2, "dissch"); CIO_TRACE_EVENT (2, sch->dev.bus_id); + if (sch_is_pseudo_sch(sch)) + return 0; ccode = stsch (sch->schid, &sch->schib); if (ccode == 3) /* Not operational. */ return -ENODEV; @@ -496,7 +500,7 @@ cio_disable_subchannel (struct subchannel *sch) return ret; } -static int cio_create_sch_lock(struct subchannel *sch) +int cio_create_sch_lock(struct subchannel *sch) { sch->lock = kmalloc(sizeof(spinlock_t), GFP_KERNEL); if (!sch->lock) diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h index 7e7369b..35154a2 100644 --- a/drivers/s390/cio/cio.h +++ b/drivers/s390/cio/cio.h @@ -131,6 +131,8 @@ extern int cio_set_options (struct subchannel *, int); extern int cio_get_options (struct subchannel *); extern int cio_modify (struct subchannel *); +int cio_create_sch_lock(struct subchannel *); + /* Use with care. */ #ifdef CONFIG_CCW_CONSOLE extern struct subchannel *cio_probe_console(void); diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 65939e2..0bf7166 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -580,12 +580,24 @@ css_cm_enable_store(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(cm_enable, 0644, css_cm_enable_show, css_cm_enable_store); -static inline void __init -setup_css(int nr) +static inline int __init setup_css(int nr) { u32 tod_high; + int ret; memset(css[nr], 0, sizeof(struct channel_subsystem)); + css[nr]->pseudo_subchannel = + kzalloc(sizeof(*css[nr]->pseudo_subchannel), GFP_KERNEL); + if (!css[nr]->pseudo_subchannel) + return -ENOMEM; + css[nr]->pseudo_subchannel->dev.parent = &css[nr]->device; + css[nr]->pseudo_subchannel->dev.release = css_subchannel_release; + sprintf(css[nr]->pseudo_subchannel->dev.bus_id, "defunct"); + ret = cio_create_sch_lock(css[nr]->pseudo_subchannel); + if (ret) { + kfree(css[nr]->pseudo_subchannel); + return ret; + } mutex_init(&css[nr]->mutex); css[nr]->valid = 1; css[nr]->cssid = nr; @@ -593,6 +605,7 @@ setup_css(int nr) css[nr]->device.release = channel_subsystem_release; tod_high = (u32) (get_clock() >> 32); css_generate_pgid(css[nr], tod_high); + return 0; } /* @@ -629,10 +642,12 @@ init_channel_subsystem (void) ret = -ENOMEM; goto out_unregister; } - setup_css(i); - ret = device_register(&css[i]->device); + ret = setup_css(i); if (ret) goto out_free; + ret = device_register(&css[i]->device); + if (ret) + goto out_free_all; if (css_characteristics_avail && css_chsc_characteristics.secm) { ret = device_create_file(&css[i]->device, @@ -640,6 +655,9 @@ init_channel_subsystem (void) if (ret) goto out_device; } + ret = device_register(&css[i]->pseudo_subchannel->dev); + if (ret) + goto out_file; } css_init_done = 1; @@ -647,13 +665,19 @@ init_channel_subsystem (void) for_each_subchannel(__init_channel_subsystem, NULL); return 0; +out_file: + device_remove_file(&css[i]->device, &dev_attr_cm_enable); out_device: device_unregister(&css[i]->device); +out_free_all: + kfree(css[i]->pseudo_subchannel->lock); + kfree(css[i]->pseudo_subchannel); out_free: kfree(css[i]); out_unregister: while (i > 0) { i--; + device_unregister(&css[i]->pseudo_subchannel->dev); if (css_characteristics_avail && css_chsc_characteristics.secm) device_remove_file(&css[i]->device, &dev_attr_cm_enable); @@ -665,6 +689,11 @@ out: return ret; } +int sch_is_pseudo_sch(struct subchannel *sch) +{ + return sch == to_css(sch->dev.parent)->pseudo_subchannel; +} + /* * find a driver for a subchannel. They identify by the subchannel * type with the exception that the console subchannel driver has its own diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index ac845c1..71fe380 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h @@ -160,6 +160,8 @@ struct channel_subsystem { int cm_enabled; void *cub_addr1; void *cub_addr2; + /* for orphaned ccw devices */ + struct subchannel *pseudo_subchannel; }; #define to_css(dev) container_of(dev, struct channel_subsystem, device) @@ -187,6 +189,8 @@ void css_clear_subchannel_slow_list(void); int css_slow_subchannels_exist(void); extern int need_rescan; +int sch_is_pseudo_sch(struct subchannel *); + extern struct workqueue_struct *slow_path_wq; extern struct work_struct slow_path_work; diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 9a31239f..7fe1ccd 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -23,6 +23,7 @@ #include /* HZ */ #include "cio.h" +#include "cio_debug.h" #include "css.h" #include "device.h" #include "ioasm.h" @@ -294,6 +295,11 @@ online_show (struct device *dev, struct device_attribute *attr, char *buf) return sprintf(buf, cdev->online ? "1\n" : "0\n"); } +int ccw_device_is_orphan(struct ccw_device *cdev) +{ + return sch_is_pseudo_sch(to_subchannel(cdev->dev.parent)); +} + static void ccw_device_unregister(struct work_struct *work) { struct ccw_device_private *priv; @@ -310,10 +316,23 @@ static void ccw_device_remove_disconnected(struct ccw_device *cdev) { struct subchannel *sch; + unsigned long flags; /* * Forced offline in disconnected state means * 'throw away device'. */ + if (ccw_device_is_orphan(cdev)) { + /* Deregister ccw device. */ + spin_lock_irqsave(cdev->ccwlock, flags); + cdev->private->state = DEV_STATE_NOT_OPER; + spin_unlock_irqrestore(cdev->ccwlock, flags); + if (get_device(&cdev->dev)) { + PREPARE_WORK(&cdev->private->kick_work, + ccw_device_unregister); + queue_work(ccw_device_work, &cdev->private->kick_work); + } + return ; + } sch = to_subchannel(cdev->dev.parent); css_sch_device_unregister(sch); /* Reset intparm to zeroes. */ @@ -474,6 +493,8 @@ available_show (struct device *dev, struct device_attribute *attr, char *buf) struct ccw_device *cdev = to_ccwdev(dev); struct subchannel *sch; + if (ccw_device_is_orphan(cdev)) + return sprintf(buf, "no device\n"); switch (cdev->private->state) { case DEV_STATE_BOXED: return sprintf(buf, "boxed\n"); @@ -574,11 +595,10 @@ match_devno(struct device * dev, void * data) cdev = to_ccwdev(dev); if ((cdev->private->state == DEV_STATE_DISCONNECTED) && + !ccw_device_is_orphan(cdev) && ccw_dev_id_is_equal(&cdev->private->dev_id, &d->dev_id) && - (cdev != d->sibling)) { - cdev->private->state = DEV_STATE_NOT_OPER; + (cdev != d->sibling)) return 1; - } return 0; } @@ -595,6 +615,28 @@ static struct ccw_device * get_disc_ccwdev_by_dev_id(struct ccw_dev_id *dev_id, return dev ? to_ccwdev(dev) : NULL; } +static int match_orphan(struct device *dev, void *data) +{ + struct ccw_dev_id *dev_id; + struct ccw_device *cdev; + + dev_id = data; + cdev = to_ccwdev(dev); + return ccw_dev_id_is_equal(&cdev->private->dev_id, dev_id); +} + +static struct ccw_device * +get_orphaned_ccwdev_by_dev_id(struct channel_subsystem *css, + struct ccw_dev_id *dev_id) +{ + struct device *dev; + + dev = device_find_child(&css->pseudo_subchannel->dev, dev_id, + match_orphan); + + return dev ? to_ccwdev(dev) : NULL; +} + static void ccw_device_add_changed(struct work_struct *work) { @@ -614,64 +656,19 @@ ccw_device_add_changed(struct work_struct *work) } } -extern int css_get_ssd_info(struct subchannel *sch); - -void -ccw_device_do_unreg_rereg(struct work_struct *work) +void ccw_device_do_unreg_rereg(struct work_struct *work) { struct ccw_device_private *priv; struct ccw_device *cdev; struct subchannel *sch; - int need_rename; priv = container_of(work, struct ccw_device_private, kick_work); cdev = priv->cdev; sch = to_subchannel(cdev->dev.parent); - if (cdev->private->dev_id.devno != sch->schib.pmcw.dev) { - /* - * The device number has changed. This is usually only when - * a device has been detached under VM and then re-appeared - * on another subchannel because of a different attachment - * order than before. Ideally, we should should just switch - * subchannels, but unfortunately, this is not possible with - * the current implementation. - * Instead, we search for the old subchannel for this device - * number and deregister so there are no collisions with the - * newly registered ccw_device. - * FIXME: Find another solution so the block layer doesn't - * get possibly sick... - */ - struct ccw_device *other_cdev; - struct ccw_dev_id dev_id; - - need_rename = 1; - dev_id.devno = sch->schib.pmcw.dev; - dev_id.ssid = sch->schid.ssid; - other_cdev = get_disc_ccwdev_by_dev_id(&dev_id, cdev); - if (other_cdev) { - struct subchannel *other_sch; - - other_sch = to_subchannel(other_cdev->dev.parent); - if (get_device(&other_sch->dev)) { - stsch(other_sch->schid, &other_sch->schib); - if (other_sch->schib.pmcw.dnv) { - other_sch->schib.pmcw.intparm = 0; - cio_modify(other_sch); - } - css_sch_device_unregister(other_sch); - } - } - /* Update ssd info here. */ - css_get_ssd_info(sch); - cdev->private->dev_id.devno = sch->schib.pmcw.dev; - } else - need_rename = 0; + device_remove_files(&cdev->dev); if (test_and_clear_bit(1, &cdev->private->registered)) device_del(&cdev->dev); - if (need_rename) - snprintf (cdev->dev.bus_id, BUS_ID_SIZE, "0.%x.%04x", - sch->schid.ssid, sch->schib.pmcw.dev); PREPARE_WORK(&cdev->private->kick_work, ccw_device_add_changed); queue_work(ccw_device_work, &cdev->private->kick_work); @@ -736,6 +733,131 @@ static struct ccw_device * io_subchannel_create_ccwdev(struct subchannel *sch) return cdev; } +static int io_subchannel_recog(struct ccw_device *, struct subchannel *); + +static void sch_attach_device(struct subchannel *sch, + struct ccw_device *cdev) +{ + spin_lock_irq(sch->lock); + sch->dev.driver_data = cdev; + cdev->private->schid = sch->schid; + cdev->ccwlock = sch->lock; + device_trigger_reprobe(sch); + spin_unlock_irq(sch->lock); +} + +static void sch_attach_disconnected_device(struct subchannel *sch, + struct ccw_device *cdev) +{ + struct subchannel *other_sch; + int ret; + + other_sch = to_subchannel(get_device(cdev->dev.parent)); + ret = device_move(&cdev->dev, &sch->dev); + if (ret) { + CIO_MSG_EVENT(2, "Moving disconnected device 0.%x.%04x failed " + "(ret=%d)!\n", cdev->private->dev_id.ssid, + cdev->private->dev_id.devno, ret); + put_device(&other_sch->dev); + return; + } + other_sch->dev.driver_data = NULL; + /* No need to keep a subchannel without ccw device around. */ + css_sch_device_unregister(other_sch); + put_device(&other_sch->dev); + sch_attach_device(sch, cdev); +} + +static void sch_attach_orphaned_device(struct subchannel *sch, + struct ccw_device *cdev) +{ + int ret; + + /* Try to move the ccw device to its new subchannel. */ + ret = device_move(&cdev->dev, &sch->dev); + if (ret) { + CIO_MSG_EVENT(0, "Moving device 0.%x.%04x from orphanage " + "failed (ret=%d)!\n", + cdev->private->dev_id.ssid, + cdev->private->dev_id.devno, ret); + return; + } + sch_attach_device(sch, cdev); +} + +static void sch_create_and_recog_new_device(struct subchannel *sch) +{ + struct ccw_device *cdev; + + /* Need to allocate a new ccw device. */ + cdev = io_subchannel_create_ccwdev(sch); + if (IS_ERR(cdev)) { + /* OK, we did everything we could... */ + css_sch_device_unregister(sch); + return; + } + spin_lock_irq(sch->lock); + sch->dev.driver_data = 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; + spin_unlock_irq(sch->lock); + if (cdev->dev.release) + cdev->dev.release(&cdev->dev); + css_sch_device_unregister(sch); + } +} + + +void ccw_device_move_to_orphanage(struct work_struct *work) +{ + struct ccw_device_private *priv; + struct ccw_device *cdev; + struct ccw_device *replacing_cdev; + struct subchannel *sch; + int ret; + struct channel_subsystem *css; + struct ccw_dev_id dev_id; + + priv = container_of(work, struct ccw_device_private, kick_work); + cdev = priv->cdev; + sch = to_subchannel(cdev->dev.parent); + css = to_css(sch->dev.parent); + dev_id.devno = sch->schib.pmcw.dev; + dev_id.ssid = sch->schid.ssid; + + /* + * Move the orphaned ccw device to the orphanage so the replacing + * ccw device can take its place on the subchannel. + */ + ret = device_move(&cdev->dev, &css->pseudo_subchannel->dev); + if (ret) { + CIO_MSG_EVENT(0, "Moving device 0.%x.%04x to orphanage failed " + "(ret=%d)!\n", cdev->private->dev_id.ssid, + cdev->private->dev_id.devno, ret); + return; + } + cdev->ccwlock = css->pseudo_subchannel->lock; + /* + * Search for the replacing ccw device + * - among the disconnected devices + * - in the orphanage + */ + replacing_cdev = get_disc_ccwdev_by_dev_id(&dev_id, cdev); + if (replacing_cdev) { + sch_attach_disconnected_device(sch, replacing_cdev); + return; + } + replacing_cdev = get_orphaned_ccwdev_by_dev_id(css, &dev_id); + if (replacing_cdev) { + sch_attach_orphaned_device(sch, replacing_cdev); + return; + } + sch_create_and_recog_new_device(sch); +} + /* * Register recognized device. */ @@ -890,12 +1012,55 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch) return rc; } +static void ccw_device_move_to_sch(struct work_struct *work) +{ + struct ccw_device_private *priv; + int rc; + struct subchannel *sch; + struct ccw_device *cdev; + struct subchannel *former_parent; + + priv = container_of(work, struct ccw_device_private, kick_work); + sch = priv->sch; + cdev = priv->cdev; + former_parent = ccw_device_is_orphan(cdev) ? + NULL : to_subchannel(get_device(cdev->dev.parent)); + mutex_lock(&sch->reg_mutex); + /* Try to move the ccw device to its new subchannel. */ + rc = device_move(&cdev->dev, &sch->dev); + mutex_unlock(&sch->reg_mutex); + if (rc) { + CIO_MSG_EVENT(2, "Moving device 0.%x.%04x to subchannel " + "0.%x.%04x failed (ret=%d)!\n", + cdev->private->dev_id.ssid, + cdev->private->dev_id.devno, sch->schid.ssid, + sch->schid.sch_no, rc); + css_sch_device_unregister(sch); + goto out; + } + if (former_parent) { + spin_lock_irq(former_parent->lock); + former_parent->dev.driver_data = NULL; + spin_unlock_irq(former_parent->lock); + css_sch_device_unregister(former_parent); + /* Reset intparm to zeroes. */ + former_parent->schib.pmcw.intparm = 0; + cio_modify(former_parent); + } + sch_attach_device(sch, cdev); +out: + if (former_parent) + put_device(&former_parent->dev); + put_device(&cdev->dev); +} + static int io_subchannel_probe (struct subchannel *sch) { struct ccw_device *cdev; int rc; unsigned long flags; + struct ccw_dev_id dev_id; if (sch->dev.driver_data) { /* @@ -918,6 +1083,28 @@ io_subchannel_probe (struct subchannel *sch) get_device(&cdev->dev); return 0; } + /* + * First check if a fitting device may be found amongst the + * disconnected devices or in the orphanage. + */ + dev_id.devno = sch->schib.pmcw.dev; + dev_id.ssid = sch->schid.ssid; + cdev = get_disc_ccwdev_by_dev_id(&dev_id, NULL); + if (!cdev) + cdev = get_orphaned_ccwdev_by_dev_id(to_css(sch->dev.parent), + &dev_id); + if (cdev) { + /* + * Schedule moving the device until when we have a registered + * subchannel to move to and succeed the probe. We can + * unregister later again, when the probe is through. + */ + cdev->private->sch = sch; + PREPARE_WORK(&cdev->private->kick_work, + ccw_device_move_to_sch); + queue_work(slow_path_wq, &cdev->private->kick_work); + return 0; + } cdev = io_subchannel_create_ccwdev(sch); if (IS_ERR(cdev)) return PTR_ERR(cdev); diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h index d5fe95e..29db634 100644 --- a/drivers/s390/cio/device.h +++ b/drivers/s390/cio/device.h @@ -80,6 +80,8 @@ int ccw_device_cancel_halt_clear(struct ccw_device *); void ccw_device_do_unreg_rereg(struct work_struct *); void ccw_device_call_sch_unregister(struct work_struct *); +void ccw_device_move_to_orphanage(struct work_struct *); +int ccw_device_is_orphan(struct ccw_device *); int ccw_device_recognition(struct ccw_device *); int ccw_device_online(struct ccw_device *); diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index a487fb0..eed1457 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -186,13 +186,12 @@ ccw_device_handle_oper(struct ccw_device *cdev) /* * Check if cu type and device type still match. If * not, it is certainly another device and we have to - * de- and re-register. Also check here for non-matching devno. + * de- and re-register. */ if (cdev->id.cu_type != cdev->private->senseid.cu_type || cdev->id.cu_model != cdev->private->senseid.cu_model || cdev->id.dev_type != cdev->private->senseid.dev_type || - cdev->id.dev_model != cdev->private->senseid.dev_model || - cdev->private->dev_id.devno != sch->schib.pmcw.dev) { + cdev->id.dev_model != cdev->private->senseid.dev_model) { PREPARE_WORK(&cdev->private->kick_work, ccw_device_do_unreg_rereg); queue_work(ccw_device_work, &cdev->private->kick_work); @@ -676,6 +675,10 @@ ccw_device_offline(struct ccw_device *cdev) { struct subchannel *sch; + if (ccw_device_is_orphan(cdev)) { + ccw_device_done(cdev, DEV_STATE_OFFLINE); + return 0; + } sch = to_subchannel(cdev->dev.parent); if (stsch(sch->schid, &sch->schib) || !sch->schib.pmcw.dnv) return -ENODEV; @@ -1121,7 +1124,13 @@ device_trigger_reprobe(struct subchannel *sch) sch->schib.pmcw.mp = 1; sch->schib.pmcw.intparm = (__u32)(unsigned long)sch; /* We should also udate ssd info, but this has to wait. */ - ccw_device_start_id(cdev, 0); + /* Check if this is another device which appeared on the same sch. */ + if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) { + PREPARE_WORK(&cdev->private->kick_work, + ccw_device_move_to_orphanage); + queue_work(ccw_device_work, &cdev->private->kick_work); + } else + ccw_device_start_id(cdev, 0); } static void -- cgit v0.10.2 From 529192f3b90682e37c5fcab461d968d062f1e0e4 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Fri, 8 Dec 2006 15:55:57 +0100 Subject: [S390] Use dev->groups for adding/removing the subchannel attribute group. Signed-off-by: Cornelia Huck Cc: Greg KH Signed-off-by: Andrew Morton Signed-off-by: Martin Schwidefsky diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 0bf7166..4c81d89 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -137,6 +137,7 @@ css_register_subchannel(struct subchannel *sch) sch->dev.parent = &css[0]->device; sch->dev.bus = &css_bus_type; sch->dev.release = &css_subchannel_release; + sch->dev.groups = subch_attr_groups; /* make it known to the system */ ret = css_sch_device_register(sch); @@ -146,10 +147,6 @@ css_register_subchannel(struct subchannel *sch) return ret; } css_get_ssd_info(sch); - ret = subchannel_add_files(&sch->dev); - if (ret) - printk(KERN_WARNING "%s: could not add attributes to %s\n", - __func__, sch->dev.bus_id); return ret; } diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index 71fe380..3464c5b 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h @@ -195,4 +195,5 @@ extern struct workqueue_struct *slow_path_wq; extern struct work_struct slow_path_work; int subchannel_add_files (struct device *); +extern struct attribute_group *subch_attr_groups[]; #endif diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 7fe1ccd..8035790 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -235,9 +235,11 @@ chpids_show (struct device * dev, struct device_attribute *attr, char * buf) ssize_t ret = 0; int chp; - for (chp = 0; chp < 8; chp++) - ret += sprintf (buf+ret, "%02x ", ssd->chpid[chp]); - + if (ssd) + for (chp = 0; chp < 8; chp++) + ret += sprintf (buf+ret, "%02x ", ssd->chpid[chp]); + else + ret += sprintf (buf, "n/a"); ret += sprintf (buf+ret, "\n"); return min((ssize_t)PAGE_SIZE, ret); } @@ -531,10 +533,10 @@ static struct attribute_group subch_attr_group = { .attrs = subch_attrs, }; -int subchannel_add_files (struct device *dev) -{ - return sysfs_create_group(&dev->kobj, &subch_attr_group); -} +struct attribute_group *subch_attr_groups[] = { + &subch_attr_group, + NULL, +}; static struct attribute * ccwdev_attrs[] = { &dev_attr_devtype.attr, -- cgit v0.10.2 From 7f090145a14afc35844dce80174c9c24f9e66ec5 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Fri, 8 Dec 2006 15:56:02 +0100 Subject: [S390] Update documentation for dynamic subchannel mapping. Signed-off-by: Cornelia Huck Cc: Greg KH Signed-off-by: Andrew Morton Signed-off-by: Martin Schwidefsky diff --git a/Documentation/s390/driver-model.txt b/Documentation/s390/driver-model.txt index 77bf450..e938c44 100644 --- a/Documentation/s390/driver-model.txt +++ b/Documentation/s390/driver-model.txt @@ -18,11 +18,18 @@ devices/ - 0.0.0002/ - 0.1.0000/0.1.1234/ ... + - defunct/ In this example, device 0815 is accessed via subchannel 0 in subchannel set 0, device 4711 via subchannel 1 in subchannel set 0, and subchannel 2 is a non-I/O subchannel. Device 1234 is accessed via subchannel 0 in subchannel set 1. +The subchannel named 'defunct' does not represent any real subchannel on the +system; it is a pseudo subchannel where disconnnected ccw devices are moved to +if they are displaced by another ccw device becoming operational on their +former subchannel. The ccw devices will be moved again to a proper subchannel +if they become operational again on that subchannel. + You should address a ccw device via its bus id (e.g. 0.0.4711); the device can be found under bus/ccw/devices/. -- cgit v0.10.2 From f4eb07c17df2e6cf9bd58bfcd9cc9e05e9489d07 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Fri, 8 Dec 2006 15:56:07 +0100 Subject: [S390] Virtual memmap for s390. Virtual memmap support for s390. Inspired by the ia64 implementation. Unlike ia64 we need a mechanism which allows us to dynamically attach shared memory regions. These memory regions are accessed via the dcss device driver. dcss implements the 'direct_access' operation, which requires struct pages for every single shared page. Therefore this implementation provides an interface to attach/detach shared memory: int add_shared_memory(unsigned long start, unsigned long size); int remove_shared_memory(unsigned long start, unsigned long size); The purpose of the add_shared_memory function is to add the given memory range to the 1:1 mapping and to make sure that the corresponding range in the vmemmap is backed with physical pages. It also initialises the new struct pages. remove_shared_memory in turn only invalidates the page table entries in the 1:1 mapping. The page tables and the memory used for struct pages in the vmemmap are currently not freed. They will be reused when the next segment will be attached. Given that the maximum size of a shared memory region is 2GB and in addition all regions must reside below 2GB this is not too much of a restriction, but there is room for improvement. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index a08e910..f12ca8fb 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -235,6 +235,9 @@ config WARN_STACK_SIZE source "mm/Kconfig" +config HOLES_IN_ZONE + def_bool y + comment "I/O subsystem configuration" config MACHCHK_WARNING diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index b928fec..b8a1ce2 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -64,7 +64,7 @@ unsigned int console_devno = -1; unsigned int console_irq = -1; unsigned long machine_flags = 0; -struct mem_chunk memory_chunk[MEMORY_CHUNKS]; +struct mem_chunk __initdata memory_chunk[MEMORY_CHUNKS]; volatile int __cpu_logical_map[NR_CPUS]; /* logical cpu to cpu address */ unsigned long __initdata zholes_size[MAX_NR_ZONES]; static unsigned long __initdata memory_end; diff --git a/arch/s390/mm/Makefile b/arch/s390/mm/Makefile index aa9a42b..8e09db1 100644 --- a/arch/s390/mm/Makefile +++ b/arch/s390/mm/Makefile @@ -2,6 +2,6 @@ # Makefile for the linux s390-specific parts of the memory manager. # -obj-y := init.o fault.o ioremap.o extmem.o mmap.o +obj-y := init.o fault.o ioremap.o extmem.o mmap.o vmem.o obj-$(CONFIG_CMM) += cmm.o diff --git a/arch/s390/mm/extmem.c b/arch/s390/mm/extmem.c index 9e9bc48..775bf19 100644 --- a/arch/s390/mm/extmem.c +++ b/arch/s390/mm/extmem.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -238,65 +239,6 @@ query_segment_type (struct dcss_segment *seg) } /* - * check if the given segment collides with guest storage. - * returns 1 if this is the case, 0 if no collision was found - */ -static int -segment_overlaps_storage(struct dcss_segment *seg) -{ - int i; - - for (i = 0; i < MEMORY_CHUNKS && memory_chunk[i].size > 0; i++) { - if (memory_chunk[i].type != CHUNK_READ_WRITE) - continue; - if ((memory_chunk[i].addr >> 20) > (seg->end >> 20)) - continue; - if (((memory_chunk[i].addr + memory_chunk[i].size - 1) >> 20) - < (seg->start_addr >> 20)) - continue; - return 1; - } - return 0; -} - -/* - * check if segment collides with other segments that are currently loaded - * returns 1 if this is the case, 0 if no collision was found - */ -static int -segment_overlaps_others (struct dcss_segment *seg) -{ - struct list_head *l; - struct dcss_segment *tmp; - - BUG_ON(!mutex_is_locked(&dcss_lock)); - list_for_each(l, &dcss_list) { - tmp = list_entry(l, struct dcss_segment, list); - if ((tmp->start_addr >> 20) > (seg->end >> 20)) - continue; - if ((tmp->end >> 20) < (seg->start_addr >> 20)) - continue; - if (seg == tmp) - continue; - return 1; - } - return 0; -} - -/* - * check if segment exceeds the kernel mapping range (detected or set via mem=) - * returns 1 if this is the case, 0 if segment fits into the range - */ -static inline int -segment_exceeds_range (struct dcss_segment *seg) -{ - int seg_last_pfn = (seg->end) >> PAGE_SHIFT; - if (seg_last_pfn > max_pfn) - return 1; - return 0; -} - -/* * get info about a segment * possible return values: * -ENOSYS : we are not running on VM @@ -341,24 +283,26 @@ __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long rc = query_segment_type (seg); if (rc < 0) goto out_free; - if (segment_exceeds_range(seg)) { - PRINT_WARN ("segment_load: not loading segment %s - exceeds" - " kernel mapping range\n",name); - rc = -ERANGE; + + rc = add_shared_memory(seg->start_addr, seg->end - seg->start_addr + 1); + + switch (rc) { + case 0: + break; + case -ENOSPC: + PRINT_WARN("segment_load: not loading segment %s - overlaps " + "storage/segment\n", name); goto out_free; - } - if (segment_overlaps_storage(seg)) { - PRINT_WARN ("segment_load: not loading segment %s - overlaps" - " storage\n",name); - rc = -ENOSPC; + case -ERANGE: + PRINT_WARN("segment_load: not loading segment %s - exceeds " + "kernel mapping range\n", name); goto out_free; - } - if (segment_overlaps_others(seg)) { - PRINT_WARN ("segment_load: not loading segment %s - overlaps" - " other segments\n",name); - rc = -EBUSY; + default: + PRINT_WARN("segment_load: not loading segment %s (rc: %d)\n", + name, rc); goto out_free; } + if (do_nonshared) dcss_command = DCSS_LOADNSR; else @@ -372,7 +316,7 @@ __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long rc = dcss_diag_translate_rc (seg->end); dcss_diag(DCSS_PURGESEG, seg->dcss_name, &seg->start_addr, &seg->end); - goto out_free; + goto out_shared; } seg->do_nonshared = do_nonshared; atomic_set(&seg->ref_count, 1); @@ -391,6 +335,8 @@ __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long (void*)seg->start_addr, (void*)seg->end, segtype_string[seg->vm_segtype]); goto out; + out_shared: + remove_shared_memory(seg->start_addr, seg->end - seg->start_addr + 1); out_free: kfree(seg); out: @@ -530,12 +476,12 @@ segment_unload(char *name) "please report to linux390@de.ibm.com\n",name); goto out_unlock; } - if (atomic_dec_return(&seg->ref_count) == 0) { - list_del(&seg->list); - dcss_diag(DCSS_PURGESEG, seg->dcss_name, - &dummy, &dummy); - kfree(seg); - } + if (atomic_dec_return(&seg->ref_count) != 0) + goto out_unlock; + remove_shared_memory(seg->start_addr, seg->end - seg->start_addr + 1); + list_del(&seg->list); + dcss_diag(DCSS_PURGESEG, seg->dcss_name, &dummy, &dummy); + kfree(seg); out_unlock: mutex_unlock(&dcss_lock); } diff --git a/arch/s390/mm/init.c b/arch/s390/mm/init.c index e1881c3..5ea12a5 100644 --- a/arch/s390/mm/init.c +++ b/arch/s390/mm/init.c @@ -69,6 +69,8 @@ void show_mem(void) printk("Free swap: %6ldkB\n", nr_swap_pages<<(PAGE_SHIFT-10)); i = max_mapnr; while (i-- > 0) { + if (!pfn_valid(i)) + continue; page = pfn_to_page(i); total++; if (PageReserved(page)) @@ -84,67 +86,53 @@ void show_mem(void) printk("%d pages swap cached\n",cached); } +static void __init setup_ro_region(void) +{ + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; + pte_t new_pte; + unsigned long address, end; + + address = ((unsigned long)&__start_rodata) & PAGE_MASK; + end = PFN_ALIGN((unsigned long)&__end_rodata); + + for (; address < end; address += PAGE_SIZE) { + pgd = pgd_offset_k(address); + pmd = pmd_offset(pgd, address); + pte = pte_offset_kernel(pmd, address); + new_pte = mk_pte_phys(address, __pgprot(_PAGE_RO)); + set_pte(pte, new_pte); + } +} + extern unsigned long __initdata zholes_size[]; +extern void vmem_map_init(void); /* * paging_init() sets up the page tables */ - -#ifndef CONFIG_64BIT void __init paging_init(void) { - pgd_t * pg_dir; - pte_t * pg_table; - pte_t pte; - int i; - unsigned long tmp; - unsigned long pfn = 0; - unsigned long pgdir_k = (__pa(swapper_pg_dir) & PAGE_MASK) | _KERNSEG_TABLE; - static const int ssm_mask = 0x04000000L; - unsigned long ro_start_pfn, ro_end_pfn; + pgd_t *pg_dir; + int i; + unsigned long pgdir_k; + static const int ssm_mask = 0x04000000L; unsigned long zones_size[MAX_NR_ZONES]; + unsigned long dma_pfn, high_pfn; - ro_start_pfn = PFN_DOWN((unsigned long)&__start_rodata); - ro_end_pfn = PFN_UP((unsigned long)&__end_rodata); - - memset(zones_size, 0, sizeof(zones_size)); - zones_size[ZONE_DMA] = max_low_pfn; - free_area_init_node(0, &contig_page_data, zones_size, - __pa(PAGE_OFFSET) >> PAGE_SHIFT, - zholes_size); - - /* unmap whole virtual address space */ + pg_dir = swapper_pg_dir; - pg_dir = swapper_pg_dir; - +#ifdef CONFIG_64BIT + pgdir_k = (__pa(swapper_pg_dir) & PAGE_MASK) | _KERN_REGION_TABLE; for (i = 0; i < PTRS_PER_PGD; i++) - pmd_clear((pmd_t *) pg_dir++); - - /* - * map whole physical memory to virtual memory (identity mapping) - */ - - pg_dir = swapper_pg_dir; - - while (pfn < max_low_pfn) { - /* - * pg_table is physical at this point - */ - pg_table = (pte_t *) alloc_bootmem_pages(PAGE_SIZE); - - pmd_populate_kernel(&init_mm, (pmd_t *) pg_dir, pg_table); - pg_dir++; - - for (tmp = 0 ; tmp < PTRS_PER_PTE ; tmp++,pg_table++) { - if (pfn >= ro_start_pfn && pfn < ro_end_pfn) - pte = pfn_pte(pfn, __pgprot(_PAGE_RO)); - else - pte = pfn_pte(pfn, PAGE_KERNEL); - if (pfn >= max_low_pfn) - pte_val(pte) = _PAGE_TYPE_EMPTY; - set_pte(pg_table, pte); - pfn++; - } - } + pgd_clear(pg_dir + i); +#else + pgdir_k = (__pa(swapper_pg_dir) & PAGE_MASK) | _KERNSEG_TABLE; + for (i = 0; i < PTRS_PER_PGD; i++) + pmd_clear((pmd_t *)(pg_dir + i)); +#endif + vmem_map_init(); + setup_ro_region(); S390_lowcore.kernel_asce = pgdir_k; @@ -154,31 +142,9 @@ void __init paging_init(void) __ctl_load(pgdir_k, 13, 13); __raw_local_irq_ssm(ssm_mask); - local_flush_tlb(); -} - -#else /* CONFIG_64BIT */ - -void __init paging_init(void) -{ - pgd_t * pg_dir; - pmd_t * pm_dir; - pte_t * pt_dir; - pte_t pte; - int i,j,k; - unsigned long pfn = 0; - unsigned long pgdir_k = (__pa(swapper_pg_dir) & PAGE_MASK) | - _KERN_REGION_TABLE; - static const int ssm_mask = 0x04000000L; - unsigned long zones_size[MAX_NR_ZONES]; - unsigned long dma_pfn, high_pfn; - unsigned long ro_start_pfn, ro_end_pfn; - memset(zones_size, 0, sizeof(zones_size)); dma_pfn = MAX_DMA_ADDRESS >> PAGE_SHIFT; high_pfn = max_low_pfn; - ro_start_pfn = PFN_DOWN((unsigned long)&__start_rodata); - ro_end_pfn = PFN_UP((unsigned long)&__end_rodata); if (dma_pfn > high_pfn) zones_size[ZONE_DMA] = high_pfn; @@ -190,56 +156,7 @@ void __init paging_init(void) /* Initialize mem_map[]. */ free_area_init_node(0, &contig_page_data, zones_size, __pa(PAGE_OFFSET) >> PAGE_SHIFT, zholes_size); - - /* - * map whole physical memory to virtual memory (identity mapping) - */ - - pg_dir = swapper_pg_dir; - - for (i = 0 ; i < PTRS_PER_PGD ; i++,pg_dir++) { - - if (pfn >= max_low_pfn) { - pgd_clear(pg_dir); - continue; - } - - pm_dir = (pmd_t *) alloc_bootmem_pages(PAGE_SIZE * 4); - pgd_populate(&init_mm, pg_dir, pm_dir); - - for (j = 0 ; j < PTRS_PER_PMD ; j++,pm_dir++) { - if (pfn >= max_low_pfn) { - pmd_clear(pm_dir); - continue; - } - - pt_dir = (pte_t *) alloc_bootmem_pages(PAGE_SIZE); - pmd_populate_kernel(&init_mm, pm_dir, pt_dir); - - for (k = 0 ; k < PTRS_PER_PTE ; k++,pt_dir++) { - if (pfn >= ro_start_pfn && pfn < ro_end_pfn) - pte = pfn_pte(pfn, __pgprot(_PAGE_RO)); - else - pte = pfn_pte(pfn, PAGE_KERNEL); - if (pfn >= max_low_pfn) - pte_val(pte) = _PAGE_TYPE_EMPTY; - set_pte(pt_dir, pte); - pfn++; - } - } - } - - S390_lowcore.kernel_asce = pgdir_k; - - /* enable virtual mapping in kernel mode */ - __ctl_load(pgdir_k, 1, 1); - __ctl_load(pgdir_k, 7, 7); - __ctl_load(pgdir_k, 13, 13); - __raw_local_irq_ssm(ssm_mask); - - local_flush_tlb(); } -#endif /* CONFIG_64BIT */ void __init mem_init(void) { @@ -269,6 +186,8 @@ void __init mem_init(void) printk("Write protected kernel read-only data: %#lx - %#lx\n", (unsigned long)&__start_rodata, PFN_ALIGN((unsigned long)&__end_rodata) - 1); + printk("Virtual memmap size: %ldk\n", + (max_pfn * sizeof(struct page)) >> 10); } void free_initmem(void) diff --git a/arch/s390/mm/vmem.c b/arch/s390/mm/vmem.c new file mode 100644 index 0000000..7f2944d --- /dev/null +++ b/arch/s390/mm/vmem.c @@ -0,0 +1,381 @@ +/* + * arch/s390/mm/vmem.c + * + * Copyright IBM Corp. 2006 + * Author(s): Heiko Carstens + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +unsigned long vmalloc_end; +EXPORT_SYMBOL(vmalloc_end); + +static struct page *vmem_map; +static DEFINE_MUTEX(vmem_mutex); + +struct memory_segment { + struct list_head list; + unsigned long start; + unsigned long size; +}; + +static LIST_HEAD(mem_segs); + +void memmap_init(unsigned long size, int nid, unsigned long zone, + unsigned long start_pfn) +{ + struct page *start, *end; + struct page *map_start, *map_end; + int i; + + start = pfn_to_page(start_pfn); + end = start + size; + + for (i = 0; i < MEMORY_CHUNKS && memory_chunk[i].size > 0; i++) { + unsigned long cstart, cend; + + cstart = PFN_DOWN(memory_chunk[i].addr); + cend = cstart + PFN_DOWN(memory_chunk[i].size); + + map_start = mem_map + cstart; + map_end = mem_map + cend; + + if (map_start < start) + map_start = start; + if (map_end > end) + map_end = end; + + map_start -= ((unsigned long) map_start & (PAGE_SIZE - 1)) + / sizeof(struct page); + map_end += ((PFN_ALIGN((unsigned long) map_end) + - (unsigned long) map_end) + / sizeof(struct page)); + + if (map_start < map_end) + memmap_init_zone((unsigned long)(map_end - map_start), + nid, zone, page_to_pfn(map_start)); + } +} + +static inline void *vmem_alloc_pages(unsigned int order) +{ + if (slab_is_available()) + return (void *)__get_free_pages(GFP_KERNEL, order); + return alloc_bootmem_pages((1 << order) * PAGE_SIZE); +} + +static inline pmd_t *vmem_pmd_alloc(void) +{ + pmd_t *pmd; + int i; + + pmd = vmem_alloc_pages(PMD_ALLOC_ORDER); + if (!pmd) + return NULL; + for (i = 0; i < PTRS_PER_PMD; i++) + pmd_clear(pmd + i); + return pmd; +} + +static inline pte_t *vmem_pte_alloc(void) +{ + pte_t *pte; + pte_t empty_pte; + int i; + + pte = vmem_alloc_pages(PTE_ALLOC_ORDER); + if (!pte) + return NULL; + pte_val(empty_pte) = _PAGE_TYPE_EMPTY; + for (i = 0; i < PTRS_PER_PTE; i++) + set_pte(pte + i, empty_pte); + return pte; +} + +/* + * Add a physical memory range to the 1:1 mapping. + */ +static int vmem_add_range(unsigned long start, unsigned long size) +{ + unsigned long address; + pgd_t *pg_dir; + pmd_t *pm_dir; + pte_t *pt_dir; + pte_t pte; + int ret = -ENOMEM; + + for (address = start; address < start + size; address += PAGE_SIZE) { + pg_dir = pgd_offset_k(address); + if (pgd_none(*pg_dir)) { + pm_dir = vmem_pmd_alloc(); + if (!pm_dir) + goto out; + pgd_populate(&init_mm, pg_dir, pm_dir); + } + + pm_dir = pmd_offset(pg_dir, address); + if (pmd_none(*pm_dir)) { + pt_dir = vmem_pte_alloc(); + if (!pt_dir) + goto out; + pmd_populate_kernel(&init_mm, pm_dir, pt_dir); + } + + pt_dir = pte_offset_kernel(pm_dir, address); + pte = pfn_pte(address >> PAGE_SHIFT, PAGE_KERNEL); + set_pte(pt_dir, pte); + } + ret = 0; +out: + flush_tlb_kernel_range(start, start + size); + return ret; +} + +/* + * Remove a physical memory range from the 1:1 mapping. + * Currently only invalidates page table entries. + */ +static void vmem_remove_range(unsigned long start, unsigned long size) +{ + unsigned long address; + pgd_t *pg_dir; + pmd_t *pm_dir; + pte_t *pt_dir; + pte_t pte; + + pte_val(pte) = _PAGE_TYPE_EMPTY; + for (address = start; address < start + size; address += PAGE_SIZE) { + pg_dir = pgd_offset_k(address); + if (pgd_none(*pg_dir)) + continue; + pm_dir = pmd_offset(pg_dir, address); + if (pmd_none(*pm_dir)) + continue; + pt_dir = pte_offset_kernel(pm_dir, address); + set_pte(pt_dir, pte); + } + flush_tlb_kernel_range(start, start + size); +} + +/* + * Add a backed mem_map array to the virtual mem_map array. + */ +static int vmem_add_mem_map(unsigned long start, unsigned long size) +{ + unsigned long address, start_addr, end_addr; + struct page *map_start, *map_end; + pgd_t *pg_dir; + pmd_t *pm_dir; + pte_t *pt_dir; + pte_t pte; + int ret = -ENOMEM; + + map_start = vmem_map + PFN_DOWN(start); + map_end = vmem_map + PFN_DOWN(start + size); + + start_addr = (unsigned long) map_start & PAGE_MASK; + end_addr = PFN_ALIGN((unsigned long) map_end); + + for (address = start_addr; address < end_addr; address += PAGE_SIZE) { + pg_dir = pgd_offset_k(address); + if (pgd_none(*pg_dir)) { + pm_dir = vmem_pmd_alloc(); + if (!pm_dir) + goto out; + pgd_populate(&init_mm, pg_dir, pm_dir); + } + + pm_dir = pmd_offset(pg_dir, address); + if (pmd_none(*pm_dir)) { + pt_dir = vmem_pte_alloc(); + if (!pt_dir) + goto out; + pmd_populate_kernel(&init_mm, pm_dir, pt_dir); + } + + pt_dir = pte_offset_kernel(pm_dir, address); + if (pte_none(*pt_dir)) { + unsigned long new_page; + + new_page =__pa(vmem_alloc_pages(0)); + if (!new_page) + goto out; + pte = pfn_pte(new_page >> PAGE_SHIFT, PAGE_KERNEL); + set_pte(pt_dir, pte); + } + } + ret = 0; +out: + flush_tlb_kernel_range(start_addr, end_addr); + return ret; +} + +static int vmem_add_mem(unsigned long start, unsigned long size) +{ + int ret; + + ret = vmem_add_range(start, size); + if (ret) + return ret; + return vmem_add_mem_map(start, size); +} + +/* + * Add memory segment to the segment list if it doesn't overlap with + * an already present segment. + */ +static int insert_memory_segment(struct memory_segment *seg) +{ + struct memory_segment *tmp; + + if (PFN_DOWN(seg->start + seg->size) > max_pfn || + seg->start + seg->size < seg->start) + return -ERANGE; + + list_for_each_entry(tmp, &mem_segs, list) { + if (seg->start >= tmp->start + tmp->size) + continue; + if (seg->start + seg->size <= tmp->start) + continue; + return -ENOSPC; + } + list_add(&seg->list, &mem_segs); + return 0; +} + +/* + * Remove memory segment from the segment list. + */ +static void remove_memory_segment(struct memory_segment *seg) +{ + list_del(&seg->list); +} + +static void __remove_shared_memory(struct memory_segment *seg) +{ + remove_memory_segment(seg); + vmem_remove_range(seg->start, seg->size); +} + +int remove_shared_memory(unsigned long start, unsigned long size) +{ + struct memory_segment *seg; + int ret; + + mutex_lock(&vmem_mutex); + + ret = -ENOENT; + list_for_each_entry(seg, &mem_segs, list) { + if (seg->start == start && seg->size == size) + break; + } + + if (seg->start != start || seg->size != size) + goto out; + + ret = 0; + __remove_shared_memory(seg); + kfree(seg); +out: + mutex_unlock(&vmem_mutex); + return ret; +} + +int add_shared_memory(unsigned long start, unsigned long size) +{ + struct memory_segment *seg; + struct page *page; + unsigned long pfn, num_pfn, end_pfn; + int ret; + + mutex_lock(&vmem_mutex); + ret = -ENOMEM; + seg = kzalloc(sizeof(*seg), GFP_KERNEL); + if (!seg) + goto out; + seg->start = start; + seg->size = size; + + ret = insert_memory_segment(seg); + if (ret) + goto out_free; + + ret = vmem_add_mem(start, size); + if (ret) + goto out_remove; + + pfn = PFN_DOWN(start); + num_pfn = PFN_DOWN(size); + end_pfn = pfn + num_pfn; + + page = pfn_to_page(pfn); + memset(page, 0, num_pfn * sizeof(struct page)); + + for (; pfn < end_pfn; pfn++) { + page = pfn_to_page(pfn); + init_page_count(page); + reset_page_mapcount(page); + SetPageReserved(page); + INIT_LIST_HEAD(&page->lru); + } + goto out; + +out_remove: + __remove_shared_memory(seg); +out_free: + kfree(seg); +out: + mutex_unlock(&vmem_mutex); + return ret; +} + +/* + * map whole physical memory to virtual memory (identity mapping) + */ +void __init vmem_map_init(void) +{ + unsigned long map_size; + int i; + + map_size = ALIGN(max_low_pfn, MAX_ORDER_NR_PAGES) * sizeof(struct page); + vmalloc_end = PFN_ALIGN(VMALLOC_END_INIT) - PFN_ALIGN(map_size); + vmem_map = (struct page *) vmalloc_end; + NODE_DATA(0)->node_mem_map = vmem_map; + + for (i = 0; i < MEMORY_CHUNKS && memory_chunk[i].size > 0; i++) + vmem_add_mem(memory_chunk[i].addr, memory_chunk[i].size); +} + +/* + * Convert memory chunk array to a memory segment list so there is a single + * list that contains both r/w memory and shared memory segments. + */ +static int __init vmem_convert_memory_chunk(void) +{ + struct memory_segment *seg; + int i; + + mutex_lock(&vmem_mutex); + for (i = 0; i < MEMORY_CHUNKS && memory_chunk[i].size > 0; i++) { + if (!memory_chunk[i].size) + continue; + seg = kzalloc(sizeof(*seg), GFP_KERNEL); + if (!seg) + panic("Out of memory...\n"); + seg->start = memory_chunk[i].addr; + seg->size = memory_chunk[i].size; + insert_memory_segment(seg); + } + mutex_unlock(&vmem_mutex); + return 0; +} + +core_initcall(vmem_convert_memory_chunk); diff --git a/include/asm-s390/page.h b/include/asm-s390/page.h index 363ea76..05ea6f1 100644 --- a/include/asm-s390/page.h +++ b/include/asm-s390/page.h @@ -127,6 +127,26 @@ page_get_storage_key(unsigned long addr) return skey; } +extern unsigned long max_pfn; + +static inline int pfn_valid(unsigned long pfn) +{ + unsigned long dummy; + int ccode; + + if (pfn >= max_pfn) + return 0; + + asm volatile( + " lra %0,0(%2)\n" + " ipm %1\n" + " srl %1,28\n" + : "=d" (dummy), "=d" (ccode) + : "a" (pfn << PAGE_SHIFT) + : "cc"); + return !ccode; +} + #endif /* !__ASSEMBLY__ */ /* to align the pointer to the (next) page boundary */ @@ -138,8 +158,6 @@ page_get_storage_key(unsigned long addr) #define __va(x) (void *)(unsigned long)(x) #define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT) #define page_to_phys(page) (page_to_pfn(page) << PAGE_SHIFT) - -#define pfn_valid(pfn) ((pfn) < max_mapnr) #define virt_addr_valid(kaddr) pfn_valid(__pa(kaddr) >> PAGE_SHIFT) #define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \ diff --git a/include/asm-s390/pgalloc.h b/include/asm-s390/pgalloc.h index 28619de..0707a7e 100644 --- a/include/asm-s390/pgalloc.h +++ b/include/asm-s390/pgalloc.h @@ -25,8 +25,11 @@ extern void diag10(unsigned long addr); * Page allocation orders. */ #ifndef __s390x__ +# define PTE_ALLOC_ORDER 0 +# define PMD_ALLOC_ORDER 0 # define PGD_ALLOC_ORDER 1 #else /* __s390x__ */ +# define PTE_ALLOC_ORDER 0 # define PMD_ALLOC_ORDER 2 # define PGD_ALLOC_ORDER 2 #endif /* __s390x__ */ diff --git a/include/asm-s390/pgtable.h b/include/asm-s390/pgtable.h index 2d968a6..ae61aca 100644 --- a/include/asm-s390/pgtable.h +++ b/include/asm-s390/pgtable.h @@ -107,23 +107,25 @@ extern char empty_zero_page[PAGE_SIZE]; * The vmalloc() routines leaves a hole of 4kB between each vmalloced * area for the same reason. ;) */ +extern unsigned long vmalloc_end; #define VMALLOC_OFFSET (8*1024*1024) #define VMALLOC_START (((unsigned long) high_memory + VMALLOC_OFFSET) \ & ~(VMALLOC_OFFSET-1)) +#define VMALLOC_END vmalloc_end /* * We need some free virtual space to be able to do vmalloc. * VMALLOC_MIN_SIZE defines the minimum size of the vmalloc * area. On a machine with 2GB memory we make sure that we * have at least 128MB free space for vmalloc. On a machine - * with 4TB we make sure we have at least 1GB. + * with 4TB we make sure we have at least 128GB. */ #ifndef __s390x__ #define VMALLOC_MIN_SIZE 0x8000000UL -#define VMALLOC_END 0x80000000UL +#define VMALLOC_END_INIT 0x80000000UL #else /* __s390x__ */ -#define VMALLOC_MIN_SIZE 0x40000000UL -#define VMALLOC_END 0x40000000000UL +#define VMALLOC_MIN_SIZE 0x2000000000UL +#define VMALLOC_END_INIT 0x40000000000UL #endif /* __s390x__ */ /* @@ -815,11 +817,17 @@ static inline pte_t mk_swap_pte(unsigned long type, unsigned long offset) #define kern_addr_valid(addr) (1) +extern int add_shared_memory(unsigned long start, unsigned long size); +extern int remove_shared_memory(unsigned long start, unsigned long size); + /* * No page table caches to initialise */ #define pgtable_cache_init() do { } while (0) +#define __HAVE_ARCH_MEMMAP_INIT +extern void memmap_init(unsigned long, int, unsigned long, unsigned long); + #define __HAVE_ARCH_PTEP_ESTABLISH #define __HAVE_ARCH_PTEP_SET_ACCESS_FLAGS #define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_YOUNG -- cgit v0.10.2 From 39b742f957a287a7514a8a35c9f516cdf30b9ff5 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Fri, 8 Dec 2006 15:56:10 +0100 Subject: [S390] Use add_active_range() and free_area_init_nodes(). Size zones and holes in an architecture independent manner for s390. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index f12ca8fb..04f5a02 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -233,6 +233,9 @@ config WARN_STACK_SIZE This allows you to specify the maximum frame size a function may have without the compiler complaining about it. +config ARCH_POPULATES_NODE_MAP + def_bool y + source "mm/Kconfig" config HOLES_IN_ZONE diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index b8a1ce2..49ef206 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -66,7 +66,6 @@ unsigned long machine_flags = 0; struct mem_chunk __initdata memory_chunk[MEMORY_CHUNKS]; volatile int __cpu_logical_map[NR_CPUS]; /* logical cpu to cpu address */ -unsigned long __initdata zholes_size[MAX_NR_ZONES]; static unsigned long __initdata memory_end; /* @@ -354,21 +353,6 @@ void machine_power_off(void) */ void (*pm_power_off)(void) = machine_power_off; -static void __init -add_memory_hole(unsigned long start, unsigned long end) -{ - unsigned long dma_pfn = MAX_DMA_ADDRESS >> PAGE_SHIFT; - - if (end <= dma_pfn) - zholes_size[ZONE_DMA] += end - start + 1; - else if (start > dma_pfn) - zholes_size[ZONE_NORMAL] += end - start + 1; - else { - zholes_size[ZONE_DMA] += dma_pfn - start + 1; - zholes_size[ZONE_NORMAL] += end - dma_pfn; - } -} - static int __init early_parse_mem(char *p) { memory_end = memparse(p, &p); @@ -521,7 +505,6 @@ setup_memory(void) { unsigned long bootmap_size; unsigned long start_pfn, end_pfn, init_pfn; - unsigned long last_rw_end; int i; /* @@ -577,39 +560,27 @@ setup_memory(void) /* * Register RAM areas with the bootmem allocator. */ - last_rw_end = start_pfn; for (i = 0; i < MEMORY_CHUNKS && memory_chunk[i].size > 0; i++) { - unsigned long start_chunk, end_chunk; + unsigned long start_chunk, end_chunk, pfn; if (memory_chunk[i].type != CHUNK_READ_WRITE) continue; - start_chunk = (memory_chunk[i].addr + PAGE_SIZE - 1); - start_chunk >>= PAGE_SHIFT; - end_chunk = (memory_chunk[i].addr + memory_chunk[i].size); - end_chunk >>= PAGE_SHIFT; - if (start_chunk < start_pfn) - start_chunk = start_pfn; - if (end_chunk > end_pfn) - end_chunk = end_pfn; - if (start_chunk < end_chunk) { - /* Initialize storage key for RAM pages */ - for (init_pfn = start_chunk ; init_pfn < end_chunk; - init_pfn++) - page_set_storage_key(init_pfn << PAGE_SHIFT, - PAGE_DEFAULT_KEY); - free_bootmem(start_chunk << PAGE_SHIFT, - (end_chunk - start_chunk) << PAGE_SHIFT); - if (last_rw_end < start_chunk) - add_memory_hole(last_rw_end, start_chunk - 1); - last_rw_end = end_chunk; - } + start_chunk = PFN_DOWN(memory_chunk[i].addr); + end_chunk = start_chunk + PFN_DOWN(memory_chunk[i].size) - 1; + end_chunk = min(end_chunk, end_pfn); + if (start_chunk >= end_chunk) + continue; + add_active_range(0, start_chunk, end_chunk); + pfn = max(start_chunk, start_pfn); + for (; pfn <= end_chunk; pfn++) + page_set_storage_key(PFN_PHYS(pfn), PAGE_DEFAULT_KEY); } psw_set_key(PAGE_DEFAULT_KEY); - if (last_rw_end < end_pfn - 1) - add_memory_hole(last_rw_end, end_pfn - 1); + free_bootmem_with_active_regions(0, max_pfn); + reserve_bootmem(0, PFN_PHYS(start_pfn)); /* * Reserve the bootmem bitmap itself as well. We do this in two diff --git a/arch/s390/mm/init.c b/arch/s390/mm/init.c index 5ea12a5..aa39591 100644 --- a/arch/s390/mm/init.c +++ b/arch/s390/mm/init.c @@ -106,8 +106,8 @@ static void __init setup_ro_region(void) } } -extern unsigned long __initdata zholes_size[]; extern void vmem_map_init(void); + /* * paging_init() sets up the page tables */ @@ -117,8 +117,7 @@ void __init paging_init(void) int i; unsigned long pgdir_k; static const int ssm_mask = 0x04000000L; - unsigned long zones_size[MAX_NR_ZONES]; - unsigned long dma_pfn, high_pfn; + unsigned long max_zone_pfns[MAX_NR_ZONES]; pg_dir = swapper_pg_dir; @@ -142,20 +141,10 @@ void __init paging_init(void) __ctl_load(pgdir_k, 13, 13); __raw_local_irq_ssm(ssm_mask); - memset(zones_size, 0, sizeof(zones_size)); - dma_pfn = MAX_DMA_ADDRESS >> PAGE_SHIFT; - high_pfn = max_low_pfn; - - if (dma_pfn > high_pfn) - zones_size[ZONE_DMA] = high_pfn; - else { - zones_size[ZONE_DMA] = dma_pfn; - zones_size[ZONE_NORMAL] = high_pfn - dma_pfn; - } - - /* Initialize mem_map[]. */ - free_area_init_node(0, &contig_page_data, zones_size, - __pa(PAGE_OFFSET) >> PAGE_SHIFT, zholes_size); + memset(max_zone_pfns, 0, sizeof(max_zone_pfns)); + max_zone_pfns[ZONE_DMA] = PFN_DOWN(MAX_DMA_ADDRESS); + max_zone_pfns[ZONE_NORMAL] = max_low_pfn; + free_area_init_nodes(max_zone_pfns); } void __init mem_init(void) -- cgit v0.10.2 From 028d9b3cc62cb9dd31f1b5929edb3c23612cfccc Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Fri, 8 Dec 2006 15:56:13 +0100 Subject: [S390] Poison init section before freeing it. The data patterns should allow us to easily tell if somebody accesses initdata/code after it was freed. Same code as on various other architectures. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky diff --git a/arch/s390/mm/init.c b/arch/s390/mm/init.c index aa39591..4bb21be 100644 --- a/arch/s390/mm/init.c +++ b/arch/s390/mm/init.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -187,6 +188,7 @@ void free_initmem(void) for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) { ClearPageReserved(virt_to_page(addr)); init_page_count(virt_to_page(addr)); + memset((void *)addr, POISON_FREE_INITMEM, PAGE_SIZE); free_page(addr); totalram_pages++; } -- cgit v0.10.2 From 44853a81ed3b1c4b3cee19622e2fc5687158c46f Mon Sep 17 00:00:00 2001 From: Andrew Victor Date: Fri, 8 Dec 2006 11:24:18 +0100 Subject: [ARM] 4010/1: AT91SAM9260-EK board: Prepare for MACB Ethernet support Add PHY IRQ pin definition for AT91SAM9260-EK board. Signed-off-by: Wojtek Kaniewski Signed-off-by: Andrew Victor Signed-off-by: Russell King diff --git a/arch/arm/mach-at91rm9200/board-sam9260ek.c b/arch/arm/mach-at91rm9200/board-sam9260ek.c index ffca9bd..da5d58a 100644 --- a/arch/arm/mach-at91rm9200/board-sam9260ek.c +++ b/arch/arm/mach-at91rm9200/board-sam9260ek.c @@ -119,6 +119,7 @@ static struct spi_board_info ek_spi_devices[] = { * MACB Ethernet device */ static struct __initdata eth_platform_data ek_macb_data = { + .phy_irq_pin = AT91_PIN_PA7, .is_rmii = 1, }; -- cgit v0.10.2 From 8df12925a04194e77081a855d688d6f5638acd9d Mon Sep 17 00:00:00 2001 From: Andrew Victor Date: Fri, 8 Dec 2006 11:30:29 +0100 Subject: [ARM] 4011/1: AT91SAM9260: Fix compilation with NAND driver Add missing include for NAND device support on AT91SAM9260. Signed-off-by: Wojtek Kaniewski Signed-off-by: Andrew Victor Signed-off-by: Russell King diff --git a/arch/arm/mach-at91rm9200/at91sam9260_devices.c b/arch/arm/mach-at91rm9200/at91sam9260_devices.c index a6c596d..f42d3a4 100644 --- a/arch/arm/mach-at91rm9200/at91sam9260_devices.c +++ b/arch/arm/mach-at91rm9200/at91sam9260_devices.c @@ -18,6 +18,7 @@ #include #include #include +#include #include "generic.h" -- cgit v0.10.2 From efe90d273b6f365d37c0f82fbbd68a40982c3265 Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 8 Dec 2006 15:22:20 +0000 Subject: [ARM] Handle HWCAP_VFP in VFP support code Don't set HWCAP_VFP in the processor support file; not only does it depend on the processor features, but it also depends on the support code being present. Therefore, only set it if the support code detects that we have a VFP coprocessor attached. Also, move the VFP handling of the coprocessor access register into the VFP support code. Signed-off-by: Russell King diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index 238dd9b..cf2bd42 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -354,9 +354,6 @@ static void __init setup_processor(void) #ifndef CONFIG_ARM_THUMB elf_hwcap &= ~HWCAP_THUMB; #endif -#ifndef CONFIG_VFP - elf_hwcap &= ~HWCAP_VFP; -#endif cpu_proc_init(); } diff --git a/arch/arm/mm/proc-arm926.S b/arch/arm/mm/proc-arm926.S index 8628ed2..080efac 100644 --- a/arch/arm/mm/proc-arm926.S +++ b/arch/arm/mm/proc-arm926.S @@ -480,7 +480,7 @@ __arm926_proc_info: b __arm926_setup .long cpu_arch_name .long cpu_elf_name - .long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_VFP|HWCAP_EDSP|HWCAP_JAVA + .long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP|HWCAP_JAVA .long cpu_arm926_name .long arm926_processor_functions .long v4wbi_tlb_fns diff --git a/arch/arm/mm/proc-v6.S b/arch/arm/mm/proc-v6.S index b440c8a..c40baf8 100644 --- a/arch/arm/mm/proc-v6.S +++ b/arch/arm/mm/proc-v6.S @@ -207,11 +207,6 @@ __v6_setup: #endif mcr p15, 0, r4, c2, c0, 1 @ load TTB1 #endif /* CONFIG_MMU */ -#ifdef CONFIG_VFP - mrc p15, 0, r0, c1, c0, 2 - orr r0, r0, #(0xf << 20) - mcr p15, 0, r0, c1, c0, 2 @ Enable full access to VFP -#endif adr r5, v6_crval ldmia r5, {r5, r6} mrc p15, 0, r0, c1, c0, 0 @ read control register @@ -273,7 +268,7 @@ __v6_proc_info: b __v6_setup .long cpu_arch_name .long cpu_elf_name - .long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_VFP|HWCAP_EDSP|HWCAP_JAVA + .long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP|HWCAP_JAVA .long cpu_v6_name .long v6_processor_functions .long v6wbi_tlb_fns diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c index f08eafb..e26cc1f 100644 --- a/arch/arm/vfp/vfpmodule.c +++ b/arch/arm/vfp/vfpmodule.c @@ -263,13 +263,24 @@ void VFP9_bounce(u32 trigger, u32 fpexc, struct pt_regs *regs) if (exceptions) vfp_raise_exceptions(exceptions, trigger, orig_fpscr, regs); } - + /* * VFP support code initialisation. */ static int __init vfp_init(void) { unsigned int vfpsid; + unsigned int cpu_arch = cpu_architecture(); + u32 access = 0; + + if (cpu_arch >= CPU_ARCH_ARMv6) { + access = get_copro_access(); + + /* + * Enable full access to VFP (cp10 and cp11) + */ + set_copro_access(access | CPACC_FULL(10) | CPACC_FULL(11)); + } /* * First check that there is a VFP that we can use. @@ -281,6 +292,12 @@ static int __init vfp_init(void) printk(KERN_INFO "VFP support v0.3: "); if (VFP_arch) { printk("not present\n"); + + /* + * Restore the copro access register. + */ + if (cpu_arch >= CPU_ARCH_ARMv6) + set_copro_access(access); } else if (vfpsid & FPSID_NODOUBLE) { printk("no double precision support\n"); } else { @@ -291,9 +308,16 @@ static int __init vfp_init(void) (vfpsid & FPSID_PART_MASK) >> FPSID_PART_BIT, (vfpsid & FPSID_VARIANT_MASK) >> FPSID_VARIANT_BIT, (vfpsid & FPSID_REV_MASK) >> FPSID_REV_BIT); + vfp_vector = vfp_support_entry; thread_register_notifier(&vfp_notifier_block); + + /* + * We detected VFP, and the support code is + * in place; report VFP support to userspace. + */ + elf_hwcap |= HWCAP_VFP; } return 0; } diff --git a/include/asm-arm/system.h b/include/asm-arm/system.h index f05fbe3..f60facc 100644 --- a/include/asm-arm/system.h +++ b/include/asm-arm/system.h @@ -139,19 +139,36 @@ static inline int cpu_is_xsc3(void) #define cpu_is_xscale() 1 #endif -#define set_cr(x) \ - __asm__ __volatile__( \ - "mcr p15, 0, %0, c1, c0, 0 @ set CR" \ - : : "r" (x) : "cc") - -#define get_cr() \ - ({ \ - unsigned int __val; \ - __asm__ __volatile__( \ - "mrc p15, 0, %0, c1, c0, 0 @ get CR" \ - : "=r" (__val) : : "cc"); \ - __val; \ - }) +static inline unsigned int get_cr(void) +{ + unsigned int val; + asm("mrc p15, 0, %0, c1, c0, 0 @ get CR" : "=r" (val) : : "cc"); + return val; +} + +static inline void set_cr(unsigned int val) +{ + asm volatile("mcr p15, 0, %0, c1, c0, 0 @ set CR" + : : "r" (val) : "cc"); +} + +#define CPACC_FULL(n) (3 << (n * 2)) +#define CPACC_SVC(n) (1 << (n * 2)) +#define CPACC_DISABLE(n) (0 << (n * 2)) + +static inline unsigned int get_copro_access(void) +{ + unsigned int val; + asm("mrc p15, 0, %0, c1, c0, 2 @ get copro access" + : "=r" (val) : : "cc"); + return val; +} + +static inline void set_copro_access(unsigned int val) +{ + asm volatile("mcr p15, 0, %0, c1, c0, 2 @ set copro access" + : : "r" (val) : "cc"); +} extern unsigned long cr_no_alignment; /* defined in entry-armv.S */ extern unsigned long cr_alignment; /* defined in entry-armv.S */ -- cgit v0.10.2 From 94b1e96d9dfbb8cc19b09b68a3621243752c0586 Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 8 Dec 2006 15:32:25 +0000 Subject: [ARM] Formalise the ARMv6 processor name string Signed-off-by: Russell King diff --git a/arch/arm/mm/proc-v6.S b/arch/arm/mm/proc-v6.S index c40baf8..513c6c2 100644 --- a/arch/arm/mm/proc-v6.S +++ b/arch/arm/mm/proc-v6.S @@ -156,7 +156,7 @@ ENTRY(cpu_v6_set_pte) cpu_v6_name: - .asciz "Some Random V6 Processor" + .asciz "ARMv6-compatible processor" .align .section ".text.init", #alloc, #execinstr -- cgit v0.10.2 From 9fddda232ca2de4d40ba9c3890e27bdb4f4f8bbd Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Fri, 8 Dec 2006 00:08:33 +0100 Subject: [ARM] 4004/1: S3C24XX: UDC remove implict addition of VA to regs Remove the implicit addition of a virtual address to the UDC registers. This should have been done by ioremap() in the driver, not by a static map. Signed-off-by: Ben Dooks Signed-off-by: Russell King diff --git a/include/asm-arm/arch-s3c2410/regs-udc.h b/include/asm-arm/arch-s3c2410/regs-udc.h index 487861d..3c83546 100644 --- a/include/asm-arm/arch-s3c2410/regs-udc.h +++ b/include/asm-arm/arch-s3c2410/regs-udc.h @@ -11,8 +11,7 @@ #ifndef __ASM_ARCH_REGS_UDC_H #define __ASM_ARCH_REGS_UDC_H - -#define S3C2410_USBDREG(x) ((x) + S3C24XX_VA_USBDEV) +#define S3C2410_USBDREG(x) (x) #define S3C2410_UDC_FUNC_ADDR_REG S3C2410_USBDREG(0x0140) #define S3C2410_UDC_PWR_REG S3C2410_USBDREG(0x0144) @@ -136,8 +135,8 @@ #define S3C2410_UDC_OCSR2_ISO (1<<6) // R/W #define S3C2410_UDC_OCSR2_DMAIEN (1<<5) // R/W -#define S3C2410_UDC_SETIX(x) \ - __raw_writel(S3C2410_UDC_INDEX_ ## x, S3C2410_UDC_INDEX_REG); +#define S3C2410_UDC_SETIX(base,x) \ + writel(S3C2410_UDC_INDEX_ ## x, base+S3C2410_UDC_INDEX_REG); #define S3C2410_UDC_EP0_CSR_OPKRDY (1<<0) -- cgit v0.10.2 From 01a732eb22dab153732b94962467eaabb1fd3797 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 8 Dec 2006 02:35:52 -0800 Subject: [PATCH] ext4 calls journal_stop journal_stop() is not defined for ext4; change to ext4_journal_stop(). Signed-off-by: Randy Dunlap Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 1d85d4e..a127cc0 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -1232,7 +1232,7 @@ retry: from, to, NULL, do_journal_get_write_access); if (ret) /* fatal error, just put the handle and return */ - journal_stop(handle); + ext4_journal_stop(handle); } return ret; -- cgit v0.10.2 From b8b50b6519afa9891b753c4fffa89d89e04df66a Mon Sep 17 00:00:00 2001 From: Paul Jackson Date: Fri, 8 Dec 2006 02:35:53 -0800 Subject: [PATCH] mm: fallback_alloc cpuset_zone_allowed irq fix fallback_alloc() could end up calling cpuset_zone_allowed() with interrupts disabled (by code in kmem_cache_alloc_node()), but without __GFP_HARDWALL set, leading to a possible call of a sleeping function with interrupts disabled. This results in the BUG report: BUG: sleeping function called from invalid context at kernel/cpuset.c:1520 in_atomic():0, irqs_disabled():1 Thanks to Paul Menage for catching this one. Signed-off-by: Paul Jackson Cc: Paul Menage Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/mm/slab.c b/mm/slab.c index 068cb45..e90b610 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -3182,7 +3182,7 @@ retry: for (z = zonelist->zones; *z && !obj; z++) { nid = zone_to_nid(*z); - if (cpuset_zone_allowed(*z, flags) && + if (cpuset_zone_allowed(*z, flags | __GFP_HARDWALL) && cache->nodelists[nid] && cache->nodelists[nid]->free_objects) obj = ____cache_alloc_node(cache, -- cgit v0.10.2 From 8b03a632ef673bf1069ac9c96c97ff2830289312 Mon Sep 17 00:00:00 2001 From: Hirokazu Takata Date: Fri, 8 Dec 2006 02:35:54 -0800 Subject: [PATCH] m32r: make userspace headers platform-independent The m32r kernel 2.6.18-rc1 or after cause build errors of "unknown isa configuration" for userspace application programs, such as glibc, gdb, etc. This is because the recent kernel do not include linux/config.h not to expose kernel headers for userspace. To fix the above compile errors, this patch fixes two headers ptrace.h and sigcontext.h for m32r and makes them platform-independent. Signed-off-by: Hirokazu Takata Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/arch/m32r/kernel/entry.S b/arch/m32r/kernel/entry.S index ac6d840..5b01fd2 100644 --- a/arch/m32r/kernel/entry.S +++ b/arch/m32r/kernel/entry.S @@ -23,35 +23,35 @@ * updated in fork.c:copy_thread, signal.c:do_signal, * ptrace.c and ptrace.h * - * M32Rx/M32R2 M32R - * @(sp) - r4 ditto - * @(0x04,sp) - r5 ditto - * @(0x08,sp) - r6 ditto - * @(0x0c,sp) - *pt_regs ditto - * @(0x10,sp) - r0 ditto - * @(0x14,sp) - r1 ditto - * @(0x18,sp) - r2 ditto - * @(0x1c,sp) - r3 ditto - * @(0x20,sp) - r7 ditto - * @(0x24,sp) - r8 ditto - * @(0x28,sp) - r9 ditto - * @(0x2c,sp) - r10 ditto - * @(0x30,sp) - r11 ditto - * @(0x34,sp) - r12 ditto - * @(0x38,sp) - syscall_nr ditto - * @(0x3c,sp) - acc0h @(0x3c,sp) - acch - * @(0x40,sp) - acc0l @(0x40,sp) - accl - * @(0x44,sp) - acc1h @(0x44,sp) - dummy_acc1h - * @(0x48,sp) - acc1l @(0x48,sp) - dummy_acc1l - * @(0x4c,sp) - psw ditto - * @(0x50,sp) - bpc ditto - * @(0x54,sp) - bbpsw ditto - * @(0x58,sp) - bbpc ditto - * @(0x5c,sp) - spu (cr3) ditto - * @(0x60,sp) - fp (r13) ditto - * @(0x64,sp) - lr (r14) ditto - * @(0x68,sp) - spi (cr2) ditto - * @(0x6c,sp) - orig_r0 ditto + * M32R/M32Rx/M32R2 + * @(sp) - r4 + * @(0x04,sp) - r5 + * @(0x08,sp) - r6 + * @(0x0c,sp) - *pt_regs + * @(0x10,sp) - r0 + * @(0x14,sp) - r1 + * @(0x18,sp) - r2 + * @(0x1c,sp) - r3 + * @(0x20,sp) - r7 + * @(0x24,sp) - r8 + * @(0x28,sp) - r9 + * @(0x2c,sp) - r10 + * @(0x30,sp) - r11 + * @(0x34,sp) - r12 + * @(0x38,sp) - syscall_nr + * @(0x3c,sp) - acc0h + * @(0x40,sp) - acc0l + * @(0x44,sp) - acc1h ; ISA_DSP_LEVEL2 only + * @(0x48,sp) - acc1l ; ISA_DSP_LEVEL2 only + * @(0x4c,sp) - psw + * @(0x50,sp) - bpc + * @(0x54,sp) - bbpsw + * @(0x58,sp) - bbpc + * @(0x5c,sp) - spu (cr3) + * @(0x60,sp) - fp (r13) + * @(0x64,sp) - lr (r14) + * @(0x68,sp) - spi (cr2) + * @(0x6c,sp) - orig_r0 */ #include @@ -95,17 +95,10 @@ #define R11(reg) @(0x30,reg) #define R12(reg) @(0x34,reg) #define SYSCALL_NR(reg) @(0x38,reg) -#if defined(CONFIG_ISA_M32R2) && defined(CONFIG_ISA_DSP_LEVEL2) #define ACC0H(reg) @(0x3C,reg) #define ACC0L(reg) @(0x40,reg) #define ACC1H(reg) @(0x44,reg) #define ACC1L(reg) @(0x48,reg) -#elif defined(CONFIG_ISA_M32R2) || defined(CONFIG_ISA_M32R) -#define ACCH(reg) @(0x3C,reg) -#define ACCL(reg) @(0x40,reg) -#else -#error unknown isa configuration -#endif #define PSW(reg) @(0x4C,reg) #define BPC(reg) @(0x50,reg) #define BBPSW(reg) @(0x54,reg) diff --git a/include/asm-m32r/ptrace.h b/include/asm-m32r/ptrace.h index 2d2a6c9..632b4ce 100644 --- a/include/asm-m32r/ptrace.h +++ b/include/asm-m32r/ptrace.h @@ -33,21 +33,10 @@ #define PT_R15 PT_SP /* processor status and miscellaneous context registers. */ -#if defined(CONFIG_ISA_M32R2) && defined(CONFIG_ISA_DSP_LEVEL2) #define PT_ACC0H 15 #define PT_ACC0L 16 -#define PT_ACC1H 17 -#define PT_ACC1L 18 -#define PT_ACCH PT_ACC0H -#define PT_ACCL PT_ACC0L -#elif defined(CONFIG_ISA_M32R2) || defined(CONFIG_ISA_M32R) -#define PT_ACCH 15 -#define PT_ACCL 16 -#define PT_DUMMY_ACC1H 17 -#define PT_DUMMY_ACC1L 18 -#else -#error unknown isa conifiguration -#endif +#define PT_ACC1H 17 /* ISA_DSP_LEVEL2 only */ +#define PT_ACC1L 18 /* ISA_DSP_LEVEL2 only */ #define PT_PSW 19 #define PT_BPC 20 #define PT_BBPSW 21 @@ -103,19 +92,10 @@ struct pt_regs { long syscall_nr; /* Saved main processor status and miscellaneous context registers. */ -#if defined(CONFIG_ISA_M32R2) && defined(CONFIG_ISA_DSP_LEVEL2) unsigned long acc0h; unsigned long acc0l; - unsigned long acc1h; - unsigned long acc1l; -#elif defined(CONFIG_ISA_M32R2) || defined(CONFIG_ISA_M32R) - unsigned long acch; - unsigned long accl; - unsigned long dummy_acc1h; - unsigned long dummy_acc1l; -#else -#error unknown isa configuration -#endif + unsigned long acc1h; /* ISA_DSP_LEVEL2 only */ + unsigned long acc1l; /* ISA_DSP_LEVEL2 only */ unsigned long psw; unsigned long bpc; /* saved PC for TRAP syscalls */ unsigned long bbpsw; diff --git a/include/asm-m32r/sigcontext.h b/include/asm-m32r/sigcontext.h index 73025c0..62537dc 100644 --- a/include/asm-m32r/sigcontext.h +++ b/include/asm-m32r/sigcontext.h @@ -23,19 +23,10 @@ struct sigcontext { unsigned long sc_r12; /* Saved main processor status and miscellaneous context registers. */ -#if defined(CONFIG_ISA_M32R2) && defined(CONFIG_ISA_DSP_LEVEL2) unsigned long sc_acc0h; unsigned long sc_acc0l; - unsigned long sc_acc1h; - unsigned long sc_acc1l; -#elif defined(CONFIG_ISA_M32R2) || defined(CONFIG_ISA_M32R) - unsigned long sc_acch; - unsigned long sc_accl; - unsigned long sc_dummy_acc1h; - unsigned long sc_dummy_acc1l; -#else -#error unknown isa configuration -#endif + unsigned long sc_acc1h; /* ISA_DSP_LEVEL2 only */ + unsigned long sc_acc1l; /* ISA_DSP_LEVEL2 only */ unsigned long sc_psw; unsigned long sc_bpc; /* saved PC for TRAP syscalls */ unsigned long sc_bbpsw; -- cgit v0.10.2 From f894cb5c938de467e208e5934c90cb9deee7dc46 Mon Sep 17 00:00:00 2001 From: Hirokazu Takata Date: Fri, 8 Dec 2006 02:35:55 -0800 Subject: [PATCH] m32r: support a synthesizable M32700 core This patch is for supporting a synthesizable M32700 core for the Mappi-II FPGA board. On the core, location of MFT (Multi-Function Timer) registers is slightly different from the M32700 chip. Signed-off-by: Hirokazu Takata Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/include/asm-m32r/m32102.h b/include/asm-m32r/m32102.h index a1f0d1f..52807f8 100644 --- a/include/asm-m32r/m32102.h +++ b/include/asm-m32r/m32102.h @@ -104,7 +104,8 @@ #define M32R_MFT5RLD_PORTL (0x0C+M32R_MFT5_OFFSET) /* MFT4 reload */ #define M32R_MFT5CMPRLD_PORTL (0x10+M32R_MFT5_OFFSET) /* MFT4 compare reload */ -#if defined(CONFIG_CHIP_M32700) || defined(CONFIG_CHIP_M32104) +#if (defined(CONFIG_CHIP_M32700) && !defined(CONFIG_PLAT_MAPPI2)) \ + || defined(CONFIG_CHIP_M32104) #define M32R_MFTCR_MFT0MSK (1UL<<31) /* b0 */ #define M32R_MFTCR_MFT1MSK (1UL<<30) /* b1 */ #define M32R_MFTCR_MFT2MSK (1UL<<29) /* b2 */ @@ -117,7 +118,7 @@ #define M32R_MFTCR_MFT3EN (1UL<<20) /* b11 */ #define M32R_MFTCR_MFT4EN (1UL<<19) /* b12 */ #define M32R_MFTCR_MFT5EN (1UL<<18) /* b13 */ -#else /* not CONFIG_CHIP_M32700 && not CONFIG_CHIP_M32104 */ +#else #define M32R_MFTCR_MFT0MSK (1UL<<15) /* b16 */ #define M32R_MFTCR_MFT1MSK (1UL<<14) /* b17 */ #define M32R_MFTCR_MFT2MSK (1UL<<13) /* b18 */ @@ -130,7 +131,7 @@ #define M32R_MFTCR_MFT3EN (1UL<<4) /* b27 */ #define M32R_MFTCR_MFT4EN (1UL<<3) /* b28 */ #define M32R_MFTCR_MFT5EN (1UL<<2) /* b29 */ -#endif /* not CONFIG_CHIP_M32700 && not CONFIG_CHIP_M32104 */ +#endif #define M32R_MFTMOD_CC_MASK (1UL<<15) /* b16 */ #define M32R_MFTMOD_TCCR (1UL<<13) /* b18 */ -- cgit v0.10.2 From 6b8bd3f4b2e9d0fbfd0e7d75ba1cdc79c6876ecc Mon Sep 17 00:00:00 2001 From: Hirokazu Takata Date: Fri, 8 Dec 2006 02:35:56 -0800 Subject: [PATCH] m32r: fix ace_handler to pass full 32-bit address Don't mask the lower 12-bit of the page fault address. In the current m32r kernel implementation, we use an access exception to detect page faults. This patch fixes ace_handler (access exception handler) for m32r. In order to check userspace address in do_page_fault, we have to pass full 32-bit address to do_page_fault. Signed-off-by: Hirokazu Takata Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/arch/m32r/kernel/entry.S b/arch/m32r/kernel/entry.S index 5b01fd2..a2c472c 100644 --- a/arch/m32r/kernel/entry.S +++ b/arch/m32r/kernel/entry.S @@ -596,8 +596,6 @@ ENTRY(ace_handler) beqz r1, inst oprand: ld r2, @(low(MDEVA_offset),r2) ; set address - srli r2, #12 - slli r2, #12 srli r1, #1 bra 1f inst: diff --git a/arch/m32r/mm/fault.c b/arch/m32r/mm/fault.c index 8d5f551..9b9feb0 100644 --- a/arch/m32r/mm/fault.c +++ b/arch/m32r/mm/fault.c @@ -173,7 +173,7 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code, goto good_area; if (!(vma->vm_flags & VM_GROWSDOWN)) goto bad_area; -#if 0 + if (error_code & ACE_USERMODE) { /* * accessing the stack below "spu" is always a bug. @@ -184,7 +184,7 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code, if (address + 4 < regs->spu) goto bad_area; } -#endif + if (expand_stack(vma, address)) goto bad_area; /* -- cgit v0.10.2 From d93f7de8c5dfefb030a5e65d0857176879bf78e9 Mon Sep 17 00:00:00 2001 From: Hirokazu Takata Date: Fri, 8 Dec 2006 02:35:57 -0800 Subject: [PATCH] m32r: bootloader support for OPSPUT platform This patch supports "m32r-g00ff" bootloader for an OPSPUT platform. Applying this patch, it is possible to do ATA-boot from an IDE drive or HTTP-boot from network by m32r-g00ff. * arch/m32r/boot/compressed/m32r_sio.c: Fix hangup on OPSPUT at boot. * arch/m32r/kernel/io_opsput.c: IDE support for OPSPUT. * arch/m32r/kernel/setup_opsput.c: ditto. * include/asm-m32r/ide.h: ditto. Signed-off-by: Kazuhiro Inaoka Signed-off-by: Hirokazu Takata Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/arch/m32r/boot/compressed/m32r_sio.c b/arch/m32r/boot/compressed/m32r_sio.c index bce8af5..ee3c8be 100644 --- a/arch/m32r/boot/compressed/m32r_sio.c +++ b/arch/m32r/boot/compressed/m32r_sio.c @@ -2,6 +2,7 @@ * arch/m32r/boot/compressed/m32r_sio.c * * 2003-02-12: Takeo Takahashi + * 2006-11-30: OPSPUT support by Kazuhiro Inaoka * */ @@ -16,7 +17,7 @@ static int puts(const char *s) return 0; } -#if defined(CONFIG_PLAT_M32700UT_Alpha) || defined(CONFIG_PLAT_M32700UT) +#if defined(CONFIG_PLAT_M32700UT_Alpha) || defined(CONFIG_PLAT_M32700UT) || defined(CONFIG_PLAT_OPSPUT) #include #include @@ -31,7 +32,11 @@ static int puts(const char *s) #define BOOT_SIO0TXB (volatile unsigned short *)(0x02c00000 + 0x2000c) #else #undef PLD_BASE +#if defined(CONFIG_PLAT_OPSPUT) +#define PLD_BASE 0x1cc00000 +#else #define PLD_BASE 0xa4c00000 +#endif #define BOOT_SIO0STS PLD_ESIO0STS #define BOOT_SIO0TXB PLD_ESIO0TXB #endif diff --git a/arch/m32r/kernel/io_opsput.c b/arch/m32r/kernel/io_opsput.c index da6c5f5..3cbb1f7 100644 --- a/arch/m32r/kernel/io_opsput.c +++ b/arch/m32r/kernel/io_opsput.c @@ -30,14 +30,34 @@ extern void pcc_iowrite_byte(int, unsigned long, void *, size_t, size_t, int); extern void pcc_iowrite_word(int, unsigned long, void *, size_t, size_t, int); #endif /* CONFIG_PCMCIA && CONFIG_M32R_CFC */ -#define PORT2ADDR(port) _port2addr(port) -#define PORT2ADDR_USB(port) _port2addr_usb(port) +#define PORT2ADDR(port) _port2addr(port) +#define PORT2ADDR_USB(port) _port2addr_usb(port) static inline void *_port2addr(unsigned long port) { return (void *)(port | NONCACHE_OFFSET); } +#if defined(CONFIG_IDE) && !defined(CONFIG_M32R_CFC) +static inline void *__port2addr_ata(unsigned long port) +{ + static int dummy_reg; + + switch (port) { + case 0x1f0: return (void *)(0x0c002000 | NONCACHE_OFFSET); + case 0x1f1: return (void *)(0x0c012800 | NONCACHE_OFFSET); + case 0x1f2: return (void *)(0x0c012002 | NONCACHE_OFFSET); + case 0x1f3: return (void *)(0x0c012802 | NONCACHE_OFFSET); + case 0x1f4: return (void *)(0x0c012004 | NONCACHE_OFFSET); + case 0x1f5: return (void *)(0x0c012804 | NONCACHE_OFFSET); + case 0x1f6: return (void *)(0x0c012006 | NONCACHE_OFFSET); + case 0x1f7: return (void *)(0x0c012806 | NONCACHE_OFFSET); + case 0x3f6: return (void *)(0x0c01200e | NONCACHE_OFFSET); + default: return (void *)&dummy_reg; + } +} +#endif + /* * OPSPUT-LAN is located in the extended bus space * from 0x10000000 to 0x13ffffff on physical address. @@ -97,6 +117,12 @@ unsigned char _inb(unsigned long port) { if (port >= LAN_IOSTART && port < LAN_IOEND) return _ne_inb(PORT2ADDR_NE(port)); + +#if defined(CONFIG_IDE) && !defined(CONFIG_M32R_CFC) + else if ((port >= 0x1f0 && port <=0x1f7) || port == 0x3f6) { + return *(volatile unsigned char *)__port2addr_ata(port); + } +#endif #if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { unsigned char b; @@ -112,6 +138,11 @@ unsigned short _inw(unsigned long port) { if (port >= LAN_IOSTART && port < LAN_IOEND) return _ne_inw(PORT2ADDR_NE(port)); +#if defined(CONFIG_IDE) && !defined(CONFIG_M32R_CFC) + else if ((port >= 0x1f0 && port <=0x1f7) || port == 0x3f6) { + return *(volatile unsigned short *)__port2addr_ata(port); + } +#endif #if defined(CONFIG_USB) else if(port >= 0x340 && port < 0x3a0) return *(volatile unsigned short *)PORT2ADDR_USB(port); @@ -164,6 +195,11 @@ void _outb(unsigned char b, unsigned long port) if (port >= LAN_IOSTART && port < LAN_IOEND) _ne_outb(b, PORT2ADDR_NE(port)); else +#if defined(CONFIG_IDE) && !defined(CONFIG_M32R_CFC) + if ((port >= 0x1f0 && port <=0x1f7) || port == 0x3f6) { + *(volatile unsigned char *)__port2addr_ata(port) = b; + } else +#endif #if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { pcc_iowrite_byte(0, port, &b, sizeof(b), 1, 0); @@ -177,6 +213,11 @@ void _outw(unsigned short w, unsigned long port) if (port >= LAN_IOSTART && port < LAN_IOEND) _ne_outw(w, PORT2ADDR_NE(port)); else +#if defined(CONFIG_IDE) && !defined(CONFIG_M32R_CFC) + if ((port >= 0x1f0 && port <=0x1f7) || port == 0x3f6) { + *(volatile unsigned short *)__port2addr_ata(port) = w; + } else +#endif #if defined(CONFIG_USB) if(port >= 0x340 && port < 0x3a0) *(volatile unsigned short *)PORT2ADDR_USB(port) = w; @@ -222,6 +263,14 @@ void _insb(unsigned int port, void *addr, unsigned long count) { if (port >= LAN_IOSTART && port < LAN_IOEND) _ne_insb(PORT2ADDR_NE(port), addr, count); +#if defined(CONFIG_IDE) && !defined(CONFIG_M32R_CFC) + else if ((port >= 0x1f0 && port <=0x1f7) || port == 0x3f6) { + unsigned char *buf = addr; + unsigned char *portp = __port2addr_ata(port); + while (count--) + *buf++ = *(volatile unsigned char *)portp; + } +#endif #if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { pcc_ioread_byte(0, port, (void *)addr, sizeof(unsigned char), @@ -254,6 +303,12 @@ void _insw(unsigned int port, void *addr, unsigned long count) pcc_ioread_word(9, port, (void *)addr, sizeof(unsigned short), count, 1); #endif +#if defined(CONFIG_IDE) && !defined(CONFIG_M32R_CFC) + } else if ((port >= 0x1f0 && port <=0x1f7) || port == 0x3f6) { + portp = __port2addr_ata(port); + while (count--) + *buf++ = *(volatile unsigned short *)portp; +#endif } else { portp = PORT2ADDR(port); while (count--) @@ -280,6 +335,12 @@ void _outsb(unsigned int port, const void *addr, unsigned long count) portp = PORT2ADDR_NE(port); while (count--) _ne_outb(*buf++, portp); +#if defined(CONFIG_IDE) && !defined(CONFIG_M32R_CFC) + } else if ((port >= 0x1f0 && port <=0x1f7) || port == 0x3f6) { + portp = __port2addr_ata(port); + while (count--) + *(volatile unsigned char *)portp = *buf++; +#endif #if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) } else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { pcc_iowrite_byte(0, port, (void *)addr, sizeof(unsigned char), @@ -305,6 +366,12 @@ void _outsw(unsigned int port, const void *addr, unsigned long count) portp = PORT2ADDR_NE(port); while (count--) *(volatile unsigned short *)portp = *buf++; +#if defined(CONFIG_IDE) && !defined(CONFIG_M32R_CFC) + } else if ((port >= 0x1f0 && port <=0x1f7) || port == 0x3f6) { + portp = __port2addr_ata(port); + while (count--) + *(volatile unsigned short *)portp = *buf++; +#endif #if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) } else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { pcc_iowrite_word(9, port, (void *)addr, sizeof(unsigned short), diff --git a/arch/m32r/kernel/setup_opsput.c b/arch/m32r/kernel/setup_opsput.c index 61d3b01..62d6b71 100644 --- a/arch/m32r/kernel/setup_opsput.c +++ b/arch/m32r/kernel/setup_opsput.c @@ -218,13 +218,13 @@ static void shutdown_opsput_lanpld_irq(unsigned int irq) static struct hw_interrupt_type opsput_lanpld_irq_type = { - "OPSPUT-PLD-LAN-IRQ", - startup_opsput_lanpld_irq, - shutdown_opsput_lanpld_irq, - enable_opsput_lanpld_irq, - disable_opsput_lanpld_irq, - mask_and_ack_opsput_lanpld, - end_opsput_lanpld_irq + .typename = "OPSPUT-PLD-LAN-IRQ", + .startup = startup_opsput_lanpld_irq, + .shutdown = shutdown_opsput_lanpld_irq, + .enable = enable_opsput_lanpld_irq, + .disable = disable_opsput_lanpld_irq, + .ack = mask_and_ack_opsput_lanpld, + .end = end_opsput_lanpld_irq }; /* @@ -374,7 +374,6 @@ void __init init_IRQ(void) disable_opsput_pld_irq(PLD_IRQ_SIO0_SND); #endif /* CONFIG_SERIAL_M32R_PLDSIO */ -#if defined(CONFIG_M32R_CFC) /* INT#1: CFC IREQ on PLD */ irq_desc[PLD_IRQ_CFIREQ].status = IRQ_DISABLED; irq_desc[PLD_IRQ_CFIREQ].chip = &opsput_pld_irq_type; @@ -398,8 +397,6 @@ void __init init_IRQ(void) irq_desc[PLD_IRQ_CFC_EJECT].depth = 1; /* disable nested irq */ pld_icu_data[irq2pldirq(PLD_IRQ_CFC_EJECT)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD02; /* 'H' edge sense */ disable_opsput_pld_irq(PLD_IRQ_CFC_EJECT); -#endif /* CONFIG_M32R_CFC */ - /* * INT0# is used for LAN, DIO diff --git a/include/asm-m32r/ide.h b/include/asm-m32r/ide.h index 219a0f7..c82ebe8 100644 --- a/include/asm-m32r/ide.h +++ b/include/asm-m32r/ide.h @@ -32,7 +32,8 @@ static __inline__ int ide_default_irq(unsigned long base) { switch (base) { -#if defined(CONFIG_PLAT_M32700UT) || defined(CONFIG_PLAT_MAPPI2) +#if defined(CONFIG_PLAT_M32700UT) || defined(CONFIG_PLAT_MAPPI2) \ + || defined(CONFIG_PLAT_OPSPUT) case 0x1f0: return PLD_IRQ_CFIREQ; default: return 0; -- cgit v0.10.2 From aad094701c6355cb2b3d74a07ec0496f4a48c787 Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Fri, 8 Dec 2006 02:35:57 -0800 Subject: [PATCH] move kallsyms data to .rodata Kallsyms data is never written to, so it can as well benefit from CONFIG_DEBUG_RODATA. Signed-off-by: Jan Beulich Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/kernel/kallsyms.c b/kernel/kallsyms.c index ab63cfc..6f294ff 100644 --- a/kernel/kallsyms.c +++ b/kernel/kallsyms.c @@ -31,14 +31,14 @@ #endif /* These will be re-linked against their real values during the second link stage */ -extern unsigned long kallsyms_addresses[] __attribute__((weak)); -extern unsigned long kallsyms_num_syms __attribute__((weak,section("data"))); -extern u8 kallsyms_names[] __attribute__((weak)); +extern const unsigned long kallsyms_addresses[] __attribute__((weak)); +extern const unsigned long kallsyms_num_syms __attribute__((weak)); +extern const u8 kallsyms_names[] __attribute__((weak)); -extern u8 kallsyms_token_table[] __attribute__((weak)); -extern u16 kallsyms_token_index[] __attribute__((weak)); +extern const u8 kallsyms_token_table[] __attribute__((weak)); +extern const u16 kallsyms_token_index[] __attribute__((weak)); -extern unsigned long kallsyms_markers[] __attribute__((weak)); +extern const unsigned long kallsyms_markers[] __attribute__((weak)); static inline int is_kernel_inittext(unsigned long addr) { @@ -84,7 +84,7 @@ static int is_ksym_addr(unsigned long addr) static unsigned int kallsyms_expand_symbol(unsigned int off, char *result) { int len, skipped_first = 0; - u8 *tptr, *data; + const u8 *tptr, *data; /* get the compressed symbol length from the first symbol byte */ data = &kallsyms_names[off]; @@ -132,7 +132,7 @@ static char kallsyms_get_symbol_type(unsigned int off) * kallsyms array */ static unsigned int get_symbol_offset(unsigned long pos) { - u8 *name; + const u8 *name; int i; /* use the closest marker we have. We have markers every 256 positions, diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c index f359b73..8b809b2 100644 --- a/scripts/kallsyms.c +++ b/scripts/kallsyms.c @@ -265,7 +265,7 @@ static void write_src(void) printf("#define ALGN .align 4\n"); printf("#endif\n"); - printf(".data\n"); + printf("\t.section .rodata, \"a\"\n"); /* Provide proper symbols relocatability by their '_text' * relativeness. The symbol names cannot be used to construct -- cgit v0.10.2 From 6e2ac66470976ad7f57e0948572669b2bdfea2d0 Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Fri, 8 Dec 2006 02:35:58 -0800 Subject: [PATCH] CPEI gets warning at kernel/irq/migration.c:27/move_masked_irq() While running my MCA test (hardware error injection) on 2.6.19, I got some warning like following: > BUG: warning at kernel/irq/migration.c:27/move_masked_irq() > > Call Trace: > [] show_stack+0x40/0xa0 > sp=e00000006b2578d0 bsp=e00000006b2510b0 > [] dump_stack+0x30/0x60 > sp=e00000006b257aa0 bsp=e00000006b251098 > [] move_masked_irq+0xb0/0x240 > sp=e00000006b257aa0 bsp=e00000006b251070 > [] move_native_irq+0xe0/0x180 > sp=e00000006b257aa0 bsp=e00000006b251040 > [] iosapic_end_level_irq+0x30/0xe0 > sp=e00000006b257aa0 bsp=e00000006b251020 > [] __do_IRQ+0x170/0x400 > sp=e00000006b257aa0 bsp=e00000006b250fd8 > [] ia64_handle_irq+0x1b0/0x260 > sp=e00000006b257aa0 bsp=e00000006b250fa8 > [] ia64_leave_kernel+0x0/0x280 > sp=e00000006b257aa0 bsp=e00000006b250fa8 > [] _spin_unlock_irqrestore+0x30/0x60 > sp=e00000006b257c70 bsp=e00000006b250f90 It comes from: [kernel/irq/migration.c] 26 if (CHECK_IRQ_PER_CPU(desc->status)) { 27 WARN_ON(1); 28 return; 29 } By putting some printk in kernel, I found that irqbalance is trying to move CPEI which is handled as PER_CPU irq. That's why. CPEI(Corrected Platform Error Interrupt) is ia64 specific irq, is allowed to pin to particular processor which selected by the platform, and even it is PER_CPU but it has set_affinity handler (=iosapic_set_affinity) as same as other IO-SAPIC-level interrupts. (I don't know why, but I guess that there would be typical situation where the handler for migration is needed, such as hotplug - the processor going to be offline/hot-removed.) To shut up this warning, there are 2 way at least: a) fix CPEI stuff b) prohibit setting affinity to PER_CPU irq I'm not sure what stuff of CPEI need to be fixed, but I think that returning error to attempting move PER_CPU irq is useful for all applications since it will never work. Following small patch takes b) style. It works, the warning disappeared and irqbalance still runs well. Signed-off-by: Hidetoshi Seto Cc: Arjan van de Ven Acked-by: Ingo Molnar Acked-by: Thomas Gleixner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/kernel/irq/proc.c b/kernel/irq/proc.c index 9a35266..61f5c71 100644 --- a/kernel/irq/proc.c +++ b/kernel/irq/proc.c @@ -54,7 +54,8 @@ static int irq_affinity_write_proc(struct file *file, const char __user *buffer, unsigned int irq = (int)(long)data, full_count = count, err; cpumask_t new_value, tmp; - if (!irq_desc[irq].chip->set_affinity || no_irq_affinity) + if (!irq_desc[irq].chip->set_affinity || no_irq_affinity || + CHECK_IRQ_PER_CPU(irq_desc[irq].status)) return -EIO; err = cpumask_parse_user(buffer, count, new_value); -- cgit v0.10.2 From a2ee8649ba6d71416712e798276bf7c40b64e6e5 Mon Sep 17 00:00:00 2001 From: Herbert Poetzl Date: Fri, 8 Dec 2006 02:36:00 -0800 Subject: [PATCH] Fix linux banner utsname information utsname information is shown in the linux banner, which also is used for /proc/version (which can have different utsname values inside a uts namespaces). this patch makes the varying data arguments and changes the string to a format string, using those arguments. Signed-off-by: Herbert Poetzl Cc: "Eric W. Biederman" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/fs/proc/proc_misc.c b/fs/proc/proc_misc.c index 51815ce..9397ff6 100644 --- a/fs/proc/proc_misc.c +++ b/fs/proc/proc_misc.c @@ -252,8 +252,8 @@ static int version_read_proc(char *page, char **start, off_t off, { int len; - strcpy(page, linux_banner); - len = strlen(page); + len = sprintf(page, linux_banner, + utsname()->release, utsname()->version); return proc_calc_metrics(page, start, off, count, eof, len); } diff --git a/init/Makefile b/init/Makefile index 633a268..d6c764d 100644 --- a/init/Makefile +++ b/init/Makefile @@ -15,6 +15,7 @@ clean-files := ../include/linux/compile.h # dependencies on generated files need to be listed explicitly +$(obj)/main.o: include/linux/compile.h $(obj)/version.o: include/linux/compile.h # compile.h changes depending on hostname, generation number, etc, diff --git a/init/main.c b/init/main.c index 1174ae3..4cdcd06 100644 --- a/init/main.c +++ b/init/main.c @@ -50,6 +50,8 @@ #include #include #include +#include +#include #include #include @@ -506,7 +508,7 @@ asmlinkage void __init start_kernel(void) boot_cpu_init(); page_address_init(); printk(KERN_NOTICE); - printk(linux_banner); + printk(linux_banner, UTS_RELEASE, UTS_VERSION); setup_arch(&command_line); unwind_setup(); setup_per_cpu_areas(); diff --git a/init/version.c b/init/version.c index 8f28344..2a5dfcd 100644 --- a/init/version.c +++ b/init/version.c @@ -35,5 +35,6 @@ struct uts_namespace init_uts_ns = { EXPORT_SYMBOL_GPL(init_uts_ns); const char linux_banner[] = - "Linux version " UTS_RELEASE " (" LINUX_COMPILE_BY "@" - LINUX_COMPILE_HOST ") (" LINUX_COMPILER ") " UTS_VERSION "\n"; + "Linux version %s (" LINUX_COMPILE_BY "@" + LINUX_COMPILE_HOST ") (" LINUX_COMPILER ") %s\n"; + -- cgit v0.10.2 From 562f9c574e0707f9159a729ea41faf53b221cd30 Mon Sep 17 00:00:00 2001 From: john stultz Date: Fri, 8 Dec 2006 02:36:02 -0800 Subject: [PATCH] time: re-add verify_pmtmr_rate This patch re-adds the verify_pmtmr_rate functionality from 2.6.17 that I dropped 2.6.18. This resolves problems seen on older K6 ASUS boards where the ACPI PM timer runs too fast. See: https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=211902 http://bugme.osdl.org/show_bug.cgi?id=2375 Thanks to Ian Campbell for re-reporting this and testing the fix! Signed-off-by: John Stultz Cc: Andi Kleen Cc: Ian Campbell Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Roman Zippel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/drivers/clocksource/acpi_pm.c b/drivers/clocksource/acpi_pm.c index 7fcb77a..8ab61ef 100644 --- a/drivers/clocksource/acpi_pm.c +++ b/drivers/clocksource/acpi_pm.c @@ -142,6 +142,39 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_LE, acpi_pm_check_graylist); #endif +#ifndef CONFIG_X86_64 +#include "mach_timer.h" +#define PMTMR_EXPECTED_RATE \ + ((CALIBRATE_LATCH * (PMTMR_TICKS_PER_SEC >> 10)) / (CLOCK_TICK_RATE>>10)) +/* + * Some boards have the PMTMR running way too fast. We check + * the PMTMR rate against PIT channel 2 to catch these cases. + */ +static int verify_pmtmr_rate(void) +{ + u32 value1, value2; + unsigned long count, delta; + + mach_prepare_counter(); + value1 = read_pmtmr(); + mach_countup(&count); + value2 = read_pmtmr(); + delta = (value2 - value1) & ACPI_PM_MASK; + + /* Check that the PMTMR delta is within 5% of what we expect */ + if (delta < (PMTMR_EXPECTED_RATE * 19) / 20 || + delta > (PMTMR_EXPECTED_RATE * 21) / 20) { + printk(KERN_INFO "PM-Timer running at invalid rate: %lu%% " + "of normal - aborting.\n", + 100UL * delta / PMTMR_EXPECTED_RATE); + return -1; + } + + return 0; +} +#else +#define verify_pmtmr_rate() (0) +#endif static int __init init_acpi_pm_clocksource(void) { @@ -173,6 +206,9 @@ static int __init init_acpi_pm_clocksource(void) return -ENODEV; pm_good: + if (verify_pmtmr_rate() != 0) + return -ENODEV; + return clocksource_register(&clocksource_acpi_pm); } -- cgit v0.10.2 From 24ec839c431eb79bb8f6abc00c4e1eb3b8c4d517 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Fri, 8 Dec 2006 02:36:04 -0800 Subject: [PATCH] tty: ->signal->tty locking Fix the locking of signal->tty. Use ->sighand->siglock to protect ->signal->tty; this lock is already used by most other members of ->signal/->sighand. And unless we are 'current' or the tasklist_lock is held we need ->siglock to access ->signal anyway. (NOTE: sys_unshare() is broken wrt ->sighand locking rules) Note that tty_mutex is held over tty destruction, so while holding tty_mutex any tty pointer remains valid. Otherwise the lifetime of ttys are governed by their open file handles. This leaves some holes for tty access from signal->tty (or any other non file related tty access). It solves the tty SLAB scribbles we were seeing. (NOTE: the change from group_send_sig_info to __group_send_sig_info needs to be examined by someone familiar with the security framework, I think it is safe given the SEND_SIG_PRIV from other __group_send_sig_info invocations) [schwidefsky@de.ibm.com: 3270 fix] [akpm@osdl.org: various post-viro fixes] Signed-off-by: Peter Zijlstra Acked-by: Alan Cox Cc: Oleg Nesterov Cc: Prarit Bhargava Cc: Chris Wright Cc: Roland McGrath Cc: Stephen Smalley Cc: James Morris Cc: "David S. Miller" Cc: Jeff Dike Cc: Martin Schwidefsky Cc: Jan Kara Signed-off-by: Martin Schwidefsky Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/arch/sparc64/solaris/misc.c b/arch/sparc64/solaris/misc.c index 9ed9979..e84241d 100644 --- a/arch/sparc64/solaris/misc.c +++ b/arch/sparc64/solaris/misc.c @@ -423,9 +423,7 @@ asmlinkage int solaris_procids(int cmd, s32 pid, s32 pgid) Solaris setpgrp and setsid? */ ret = sys_setpgid(0, 0); if (ret) return ret; - mutex_lock(&tty_mutex); - current->signal->tty = NULL; - mutex_unlock(&tty_mutex); + proc_clear_tty(current); return process_group(current); } case 2: /* getsid */ diff --git a/arch/um/kernel/exec.c b/arch/um/kernel/exec.c index 0561c43..8d56ec6 100644 --- a/arch/um/kernel/exec.c +++ b/arch/um/kernel/exec.c @@ -39,12 +39,13 @@ static long execve1(char *file, char __user * __user *argv, char __user *__user *env) { long error; + struct tty_struct *tty; #ifdef CONFIG_TTY_LOG mutex_lock(&tty_mutex); - task_lock(current); /* FIXME: is this needed ? */ - log_exec(argv, current->signal->tty); - task_unlock(current); + tty = get_current_tty(); + if (tty) + log_exec(argv, tty); mutex_unlock(&tty_mutex); #endif error = do_execve(file, argv, env, ¤t->thread.regs); diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index b3cfc8b..0c856a6 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -126,7 +126,7 @@ EXPORT_SYMBOL(tty_std_termios); LIST_HEAD(tty_drivers); /* linked list of tty drivers */ -/* Semaphore to protect creating and releasing a tty. This is shared with +/* Mutex to protect creating and releasing a tty. This is shared with vt.c for deeply disgusting hack reasons */ DEFINE_MUTEX(tty_mutex); EXPORT_SYMBOL(tty_mutex); @@ -250,7 +250,7 @@ static int check_tty_count(struct tty_struct *tty, const char *routine) "!= #fd's(%d) in %s\n", tty->name, tty->count, count, routine); return count; - } + } #endif return 0; } @@ -259,18 +259,6 @@ static int check_tty_count(struct tty_struct *tty, const char *routine) * Tty buffer allocation management */ - -/** - * tty_buffer_free_all - free buffers used by a tty - * @tty: tty to free from - * - * Remove all the buffers pending on a tty whether queued with data - * or in the free ring. Must be called when the tty is no longer in use - * - * Locking: none - */ - - /** * tty_buffer_free_all - free buffers used by a tty * @tty: tty to free from @@ -614,7 +602,7 @@ EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags); * they are not on hot paths so a little discipline won't do * any harm. * - * Locking: takes termios_sem + * Locking: takes termios_mutex */ static void tty_set_termios_ldisc(struct tty_struct *tty, int num) @@ -915,7 +903,7 @@ static void tty_ldisc_enable(struct tty_struct *tty) * context. * * Locking: takes tty_ldisc_lock. - * called functions take termios_sem + * called functions take termios_mutex */ static int tty_set_ldisc(struct tty_struct *tty, int ldisc) @@ -1267,12 +1255,12 @@ EXPORT_SYMBOL_GPL(tty_ldisc_flush); * * Locking: * BKL - * redirect lock for undoing redirection - * file list lock for manipulating list of ttys - * tty_ldisc_lock from called functions - * termios_sem resetting termios data - * tasklist_lock to walk task list for hangup event - * + * redirect lock for undoing redirection + * file list lock for manipulating list of ttys + * tty_ldisc_lock from called functions + * termios_mutex resetting termios data + * tasklist_lock to walk task list for hangup event + * ->siglock to protect ->signal/->sighand */ static void do_tty_hangup(struct work_struct *work) { @@ -1354,14 +1342,18 @@ static void do_tty_hangup(struct work_struct *work) read_lock(&tasklist_lock); if (tty->session > 0) { do_each_task_pid(tty->session, PIDTYPE_SID, p) { + spin_lock_irq(&p->sighand->siglock); if (p->signal->tty == tty) p->signal->tty = NULL; - if (!p->signal->leader) + if (!p->signal->leader) { + spin_unlock_irq(&p->sighand->siglock); continue; - group_send_sig_info(SIGHUP, SEND_SIG_PRIV, p); - group_send_sig_info(SIGCONT, SEND_SIG_PRIV, p); + } + __group_send_sig_info(SIGHUP, SEND_SIG_PRIV, p); + __group_send_sig_info(SIGCONT, SEND_SIG_PRIV, p); if (tty->pgrp > 0) p->signal->tty_old_pgrp = tty->pgrp; + spin_unlock_irq(&p->sighand->siglock); } while_each_task_pid(tty->session, PIDTYPE_SID, p); } read_unlock(&tasklist_lock); @@ -1453,6 +1445,14 @@ int tty_hung_up_p(struct file * filp) EXPORT_SYMBOL(tty_hung_up_p); +static void session_clear_tty(pid_t session) +{ + struct task_struct *p; + do_each_task_pid(session, PIDTYPE_SID, p) { + proc_clear_tty(p); + } while_each_task_pid(session, PIDTYPE_SID, p); +} + /** * disassociate_ctty - disconnect controlling tty * @on_exit: true if exiting so need to "hang up" the session @@ -1469,31 +1469,35 @@ EXPORT_SYMBOL(tty_hung_up_p); * The argument on_exit is set to 1 if called when a process is * exiting; it is 0 if called by the ioctl TIOCNOTTY. * - * Locking: tty_mutex is taken to protect current->signal->tty + * Locking: * BKL is taken for hysterical raisins - * Tasklist lock is taken (under tty_mutex) to walk process - * lists for the session. + * tty_mutex is taken to protect tty + * ->siglock is taken to protect ->signal/->sighand + * tasklist_lock is taken to walk process list for sessions + * ->siglock is taken to protect ->signal/->sighand */ void disassociate_ctty(int on_exit) { struct tty_struct *tty; - struct task_struct *p; int tty_pgrp = -1; + int session; lock_kernel(); mutex_lock(&tty_mutex); - tty = current->signal->tty; + tty = get_current_tty(); if (tty) { tty_pgrp = tty->pgrp; mutex_unlock(&tty_mutex); + /* XXX: here we race, there is nothing protecting tty */ if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY) tty_vhangup(tty); } else { - if (current->signal->tty_old_pgrp) { - kill_pg(current->signal->tty_old_pgrp, SIGHUP, on_exit); - kill_pg(current->signal->tty_old_pgrp, SIGCONT, on_exit); + pid_t old_pgrp = current->signal->tty_old_pgrp; + if (old_pgrp) { + kill_pg(old_pgrp, SIGHUP, on_exit); + kill_pg(old_pgrp, SIGCONT, on_exit); } mutex_unlock(&tty_mutex); unlock_kernel(); @@ -1505,19 +1509,29 @@ void disassociate_ctty(int on_exit) kill_pg(tty_pgrp, SIGCONT, on_exit); } - /* Must lock changes to tty_old_pgrp */ - mutex_lock(&tty_mutex); + spin_lock_irq(¤t->sighand->siglock); current->signal->tty_old_pgrp = 0; - tty->session = 0; - tty->pgrp = -1; + session = current->signal->session; + spin_unlock_irq(¤t->sighand->siglock); + + mutex_lock(&tty_mutex); + /* It is possible that do_tty_hangup has free'd this tty */ + tty = get_current_tty(); + if (tty) { + tty->session = 0; + tty->pgrp = 0; + } else { +#ifdef TTY_DEBUG_HANGUP + printk(KERN_DEBUG "error attempted to write to tty [0x%p]" + " = NULL", tty); +#endif + } + mutex_unlock(&tty_mutex); /* Now clear signal->tty under the lock */ read_lock(&tasklist_lock); - do_each_task_pid(current->signal->session, PIDTYPE_SID, p) { - p->signal->tty = NULL; - } while_each_task_pid(current->signal->session, PIDTYPE_SID, p); + session_clear_tty(session); read_unlock(&tasklist_lock); - mutex_unlock(&tty_mutex); unlock_kernel(); } @@ -2337,16 +2351,10 @@ static void release_dev(struct file * filp) * tty. */ if (tty_closing || o_tty_closing) { - struct task_struct *p; - read_lock(&tasklist_lock); - do_each_task_pid(tty->session, PIDTYPE_SID, p) { - p->signal->tty = NULL; - } while_each_task_pid(tty->session, PIDTYPE_SID, p); + session_clear_tty(tty->session); if (o_tty) - do_each_task_pid(o_tty->session, PIDTYPE_SID, p) { - p->signal->tty = NULL; - } while_each_task_pid(o_tty->session, PIDTYPE_SID, p); + session_clear_tty(o_tty->session); read_unlock(&tasklist_lock); } @@ -2443,9 +2451,9 @@ static void release_dev(struct file * filp) * The termios state of a pty is reset on first open so that * settings don't persist across reuse. * - * Locking: tty_mutex protects current->signal->tty, get_tty_driver and - * init_dev work. tty->count should protect the rest. - * task_lock is held to update task details for sessions + * Locking: tty_mutex protects tty, get_tty_driver and init_dev work. + * tty->count should protect the rest. + * ->siglock protects ->signal/->sighand */ static int tty_open(struct inode * inode, struct file * filp) @@ -2467,12 +2475,13 @@ retry_open: mutex_lock(&tty_mutex); if (device == MKDEV(TTYAUX_MAJOR,0)) { - if (!current->signal->tty) { + tty = get_current_tty(); + if (!tty) { mutex_unlock(&tty_mutex); return -ENXIO; } - driver = current->signal->tty->driver; - index = current->signal->tty->index; + driver = tty->driver; + index = tty->index; filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */ /* noctty = 1; */ goto got_driver; @@ -2547,17 +2556,16 @@ got_driver: filp->f_op = &tty_fops; goto retry_open; } + + mutex_lock(&tty_mutex); + spin_lock_irq(¤t->sighand->siglock); if (!noctty && current->signal->leader && !current->signal->tty && - tty->session == 0) { - task_lock(current); - current->signal->tty = tty; - task_unlock(current); - current->signal->tty_old_pgrp = 0; - tty->session = current->signal->session; - tty->pgrp = process_group(current); - } + tty->session == 0) + __proc_set_tty(current, tty); + spin_unlock_irq(¤t->sighand->siglock); + mutex_unlock(&tty_mutex); return 0; } @@ -2747,7 +2755,7 @@ static int tiocsti(struct tty_struct *tty, char __user *p) * * Copies the kernel idea of the window size into the user buffer. * - * Locking: tty->termios_sem is taken to ensure the winsize data + * Locking: tty->termios_mutex is taken to ensure the winsize data * is consistent. */ @@ -2774,8 +2782,8 @@ static int tiocgwinsz(struct tty_struct *tty, struct winsize __user * arg) * Locking: * Called function use the console_sem is used to ensure we do * not try and resize the console twice at once. - * The tty->termios_sem is used to ensure we don't double - * resize and get confused. Lock order - tty->termios.sem before + * The tty->termios_mutex is used to ensure we don't double + * resize and get confused. Lock order - tty->termios_mutex before * console sem */ @@ -2880,25 +2888,28 @@ static int fionbio(struct file *file, int __user *p) * leader to set this tty as the controlling tty for the session. * * Locking: - * Takes tasklist lock internally to walk sessions - * Takes task_lock() when updating signal->tty * Takes tty_mutex() to protect tty instance - * + * Takes tasklist_lock internally to walk sessions + * Takes ->siglock() when updating signal->tty */ static int tiocsctty(struct tty_struct *tty, int arg) { - struct task_struct *p; - + int ret = 0; if (current->signal->leader && (current->signal->session == tty->session)) - return 0; + return ret; + + mutex_lock(&tty_mutex); /* * The process must be a session leader and * not have a controlling tty already. */ - if (!current->signal->leader || current->signal->tty) - return -EPERM; + if (!current->signal->leader || current->signal->tty) { + ret = -EPERM; + goto unlock; + } + if (tty->session > 0) { /* * This tty is already the controlling @@ -2908,24 +2919,18 @@ static int tiocsctty(struct tty_struct *tty, int arg) /* * Steal it away */ - read_lock(&tasklist_lock); - do_each_task_pid(tty->session, PIDTYPE_SID, p) { - p->signal->tty = NULL; - } while_each_task_pid(tty->session, PIDTYPE_SID, p); + session_clear_tty(tty->session); read_unlock(&tasklist_lock); - } else - return -EPERM; + } else { + ret = -EPERM; + goto unlock; + } } - mutex_lock(&tty_mutex); - task_lock(current); - current->signal->tty = tty; - task_unlock(current); + proc_set_tty(current, tty); +unlock: mutex_unlock(&tty_mutex); - current->signal->tty_old_pgrp = 0; - tty->session = current->signal->session; - tty->pgrp = process_group(current); - return 0; + return ret; } /** @@ -2937,7 +2942,7 @@ static int tiocsctty(struct tty_struct *tty, int arg) * Obtain the process group of the tty. If there is no process group * return an error. * - * Locking: none. Reference to ->signal->tty is safe. + * Locking: none. Reference to current->signal->tty is safe. */ static int tiocgpgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p) @@ -2995,7 +3000,7 @@ static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t * Obtain the session id of the tty. If there is no session * return an error. * - * Locking: none. Reference to ->signal->tty is safe. + * Locking: none. Reference to current->signal->tty is safe. */ static int tiocgsid(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p) @@ -3214,14 +3219,11 @@ int tty_ioctl(struct inode * inode, struct file * file, clear_bit(TTY_EXCLUSIVE, &tty->flags); return 0; case TIOCNOTTY: - /* FIXME: taks lock or tty_mutex ? */ if (current->signal->tty != tty) return -ENOTTY; if (current->signal->leader) disassociate_ctty(0); - task_lock(current); - current->signal->tty = NULL; - task_unlock(current); + proc_clear_tty(current); return 0; case TIOCSCTTY: return tiocsctty(tty, arg); @@ -3321,7 +3323,7 @@ static void __do_SAK(struct work_struct *work) if (!tty) return; - session = tty->session; + session = tty->session; /* We don't want an ldisc switch during this */ disc = tty_ldisc_ref(tty); @@ -3834,9 +3836,52 @@ int tty_unregister_driver(struct tty_driver *driver) cdev_del(&driver->cdev); return 0; } - EXPORT_SYMBOL(tty_unregister_driver); +dev_t tty_devnum(struct tty_struct *tty) +{ + return MKDEV(tty->driver->major, tty->driver->minor_start) + tty->index; +} +EXPORT_SYMBOL(tty_devnum); + +void proc_clear_tty(struct task_struct *p) +{ + spin_lock_irq(&p->sighand->siglock); + p->signal->tty = NULL; + spin_unlock_irq(&p->sighand->siglock); +} +EXPORT_SYMBOL(proc_clear_tty); + +void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty) +{ + if (tty) { + tty->session = tsk->signal->session; + tty->pgrp = process_group(tsk); + } + tsk->signal->tty = tty; + tsk->signal->tty_old_pgrp = 0; +} + +void proc_set_tty(struct task_struct *tsk, struct tty_struct *tty) +{ + spin_lock_irq(&tsk->sighand->siglock); + __proc_set_tty(tsk, tty); + spin_unlock_irq(&tsk->sighand->siglock); +} + +struct tty_struct *get_current_tty(void) +{ + struct tty_struct *tty; + WARN_ON_ONCE(!mutex_is_locked(&tty_mutex)); + tty = current->signal->tty; + /* + * session->tty can be changed/cleared from under us, make sure we + * issue the load. The obtained pointer, when not NULL, is valid as + * long as we hold tty_mutex. + */ + barrier(); + return tty; +} /* * Initialize the console device. This is called *early*, so diff --git a/drivers/s390/char/fs3270.c b/drivers/s390/char/fs3270.c index 78f8bda..ef205dd 100644 --- a/drivers/s390/char/fs3270.c +++ b/drivers/s390/char/fs3270.c @@ -424,11 +424,15 @@ fs3270_open(struct inode *inode, struct file *filp) minor = iminor(filp->f_dentry->d_inode); /* Check for minor 0 multiplexer. */ if (minor == 0) { - if (!current->signal->tty) + struct tty_struct *tty; + mutex_lock(&tty_mutex); + tty = get_current_tty(); + if (!tty || tty->driver->major != IBM_TTY3270_MAJOR) { + mutex_unlock(&tty_mutex); return -ENODEV; - if (current->signal->tty->driver->major != IBM_TTY3270_MAJOR) - return -ENODEV; - minor = current->signal->tty->index + RAW3270_FIRSTMINOR; + } + minor = tty->index + RAW3270_FIRSTMINOR; + mutex_unlock(&tty_mutex); } /* Check if some other program is already using fullscreen mode. */ fp = (struct fs3270 *) raw3270_find_view(&fs3270_fn, minor); diff --git a/fs/dquot.c b/fs/dquot.c index f9cd5e2..89066b1 100644 --- a/fs/dquot.c +++ b/fs/dquot.c @@ -828,6 +828,7 @@ static inline int need_print_warning(struct dquot *dquot) static void print_warning(struct dquot *dquot, const char warntype) { char *msg = NULL; + struct tty_struct *tty; int flag = (warntype == BHARDWARN || warntype == BSOFTLONGWARN) ? DQ_BLKS_B : ((warntype == IHARDWARN || warntype == ISOFTLONGWARN) ? DQ_INODES_B : 0); @@ -835,14 +836,15 @@ static void print_warning(struct dquot *dquot, const char warntype) return; mutex_lock(&tty_mutex); - if (!current->signal->tty) + tty = get_current_tty(); + if (!tty) goto out_lock; - tty_write_message(current->signal->tty, dquot->dq_sb->s_id); + tty_write_message(tty, dquot->dq_sb->s_id); if (warntype == ISOFTWARN || warntype == BSOFTWARN) - tty_write_message(current->signal->tty, ": warning, "); + tty_write_message(tty, ": warning, "); else - tty_write_message(current->signal->tty, ": write failed, "); - tty_write_message(current->signal->tty, quotatypes[dquot->dq_type]); + tty_write_message(tty, ": write failed, "); + tty_write_message(tty, quotatypes[dquot->dq_type]); switch (warntype) { case IHARDWARN: msg = " file limit reached.\r\n"; @@ -863,7 +865,7 @@ static void print_warning(struct dquot *dquot, const char warntype) msg = " block quota exceeded.\r\n"; break; } - tty_write_message(current->signal->tty, msg); + tty_write_message(tty, msg); out_lock: mutex_unlock(&tty_mutex); } diff --git a/fs/open.c b/fs/open.c index 89e0c23..3b56192 100644 --- a/fs/open.c +++ b/fs/open.c @@ -1087,6 +1087,7 @@ EXPORT_SYMBOL(sys_close); asmlinkage long sys_vhangup(void) { if (capable(CAP_SYS_TTY_CONFIG)) { + /* XXX: this needs locking */ tty_vhangup(current->signal->tty); return 0; } diff --git a/include/linux/tty.h b/include/linux/tty.h index f717f08..1d29999 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -309,6 +309,12 @@ extern void tty_ldisc_flush(struct tty_struct *tty); extern int tty_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); +extern dev_t tty_devnum(struct tty_struct *tty); +extern void proc_clear_tty(struct task_struct *p); +extern void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty); +extern void proc_set_tty(struct task_struct *tsk, struct tty_struct *tty); +extern struct tty_struct *get_current_tty(void); + extern struct mutex tty_mutex; /* n_tty.c */ @@ -335,10 +341,5 @@ extern void console_print(const char *); extern int vt_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg); -static inline dev_t tty_devnum(struct tty_struct *tty) -{ - return MKDEV(tty->driver->major, tty->driver->minor_start) + tty->index; -} - #endif /* __KERNEL__ */ #endif diff --git a/kernel/acct.c b/kernel/acct.c index dc12db8..ca56190 100644 --- a/kernel/acct.c +++ b/kernel/acct.c @@ -428,6 +428,7 @@ static void do_acct_process(struct file *file) u64 elapsed; u64 run_time; struct timespec uptime; + struct tty_struct *tty; /* * First check to see if there is enough free_space to continue @@ -485,12 +486,8 @@ static void do_acct_process(struct file *file) #endif mutex_lock(&tty_mutex); - /* FIXME: Whoever is responsible for current->signal locking needs - to use the same locking all over the kernel and document it */ - read_lock(&tasklist_lock); - ac.ac_tty = current->signal->tty ? - old_encode_dev(tty_devnum(current->signal->tty)) : 0; - read_unlock(&tasklist_lock); + tty = get_current_tty(); + ac.ac_tty = tty ? old_encode_dev(tty_devnum(tty)) : 0; mutex_unlock(&tty_mutex); spin_lock_irq(¤t->sighand->siglock); diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 40722e2..b6cb802 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -826,10 +826,12 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts context->return_code); mutex_lock(&tty_mutex); + read_lock(&tasklist_lock); if (tsk->signal && tsk->signal->tty && tsk->signal->tty->name) tty = tsk->signal->tty->name; else tty = "(none)"; + read_unlock(&tasklist_lock); audit_log_format(ab, " a0=%lx a1=%lx a2=%lx a3=%lx items=%d" " ppid=%d pid=%d auid=%u uid=%u gid=%u" diff --git a/kernel/exit.c b/kernel/exit.c index 4e3f919..fa23577 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -384,9 +384,7 @@ void daemonize(const char *name, ...) exit_mm(current); set_special_pids(1, 1); - mutex_lock(&tty_mutex); - current->signal->tty = NULL; - mutex_unlock(&tty_mutex); + proc_clear_tty(current); /* Block and flush all signals */ sigfillset(&blocked); diff --git a/kernel/sys.c b/kernel/sys.c index a0c1a29..1ac2d1c5 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -1484,7 +1484,6 @@ asmlinkage long sys_setsid(void) pid_t session; int err = -EPERM; - mutex_lock(&tty_mutex); write_lock_irq(&tasklist_lock); /* Fail if I am already a session leader */ @@ -1504,12 +1503,15 @@ asmlinkage long sys_setsid(void) group_leader->signal->leader = 1; __set_special_pids(session, session); + + spin_lock(&group_leader->sighand->siglock); group_leader->signal->tty = NULL; group_leader->signal->tty_old_pgrp = 0; + spin_unlock(&group_leader->sighand->siglock); + err = process_group(group_leader); out: write_unlock_irq(&tasklist_lock); - mutex_unlock(&tty_mutex); return err; } diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 44e9cd4..f5df8c7 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -1695,9 +1695,10 @@ static inline void flush_unauthorized_files(struct files_struct * files) struct tty_struct *tty; struct fdtable *fdt; long j = -1; + int drop_tty = 0; mutex_lock(&tty_mutex); - tty = current->signal->tty; + tty = get_current_tty(); if (tty) { file_list_lock(); file = list_entry(tty->tty_files.next, typeof(*file), f_u.fu_list); @@ -1710,12 +1711,14 @@ static inline void flush_unauthorized_files(struct files_struct * files) struct inode *inode = file->f_dentry->d_inode; if (inode_has_perm(current, inode, FILE__READ | FILE__WRITE, NULL)) { - /* Reset controlling tty. */ - current->signal->tty = NULL; - current->signal->tty_old_pgrp = 0; + drop_tty = 1; } } file_list_unlock(); + + /* Reset controlling tty. */ + if (drop_tty) + proc_set_tty(current, NULL); } mutex_unlock(&tty_mutex); -- cgit v0.10.2 From 915935041281c64589e2b7fe38437be22567fb6f Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Fri, 8 Dec 2006 02:36:07 -0800 Subject: [PATCH] do_task_stat(): don't take tty_mutex ->signal->tty is protected by ->siglock, no need to take the global tty_mutex. Signed-off-by: Oleg Nesterov Acked-by: Peter Zijlstra Cc: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/fs/proc/array.c b/fs/proc/array.c index 25e917f..b0cd014 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -346,20 +346,13 @@ static int do_task_stat(struct task_struct *task, char * buffer, int whole) sigemptyset(&sigcatch); cutime = cstime = utime = stime = cputime_zero; - mutex_lock(&tty_mutex); rcu_read_lock(); if (lock_task_sighand(task, &flags)) { struct signal_struct *sig = task->signal; - struct tty_struct *tty = sig->tty; - - if (tty) { - /* - * sig->tty is not stable, but tty_mutex - * protects us from release_dev(tty) - */ - barrier(); - tty_pgrp = tty->pgrp; - tty_nr = new_encode_dev(tty_devnum(tty)); + + if (sig->tty) { + tty_pgrp = sig->tty->pgrp; + tty_nr = new_encode_dev(tty_devnum(sig->tty)); } num_threads = atomic_read(&sig->count); @@ -395,7 +388,6 @@ static int do_task_stat(struct task_struct *task, char * buffer, int whole) unlock_task_sighand(task, &flags); } rcu_read_unlock(); - mutex_unlock(&tty_mutex); if (!whole || num_threads<2) wchan = get_wchan(task); -- cgit v0.10.2 From 7bcfa95e561f11a17720162935e4f704c5d6fda3 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Fri, 8 Dec 2006 02:36:07 -0800 Subject: [PATCH] do_acct_process(): don't take tty_mutex No need to take the global tty_mutex, signal->tty->driver can't go away while we are holding ->siglock. Signed-off-by: Oleg Nesterov Acked-by: Peter Zijlstra Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/kernel/acct.c b/kernel/acct.c index ca56190..69a40c9 100644 --- a/kernel/acct.c +++ b/kernel/acct.c @@ -485,12 +485,9 @@ static void do_acct_process(struct file *file) ac.ac_ppid = current->parent->tgid; #endif - mutex_lock(&tty_mutex); - tty = get_current_tty(); - ac.ac_tty = tty ? old_encode_dev(tty_devnum(tty)) : 0; - mutex_unlock(&tty_mutex); - spin_lock_irq(¤t->sighand->siglock); + tty = current->signal->tty; + ac.ac_tty = tty ? old_encode_dev(tty_devnum(tty)) : 0; ac.ac_utime = encode_comp_t(jiffies_to_AHZ(cputime_to_jiffies(pacct->ac_utime))); ac.ac_stime = encode_comp_t(jiffies_to_AHZ(cputime_to_jiffies(pacct->ac_stime))); ac.ac_flag = pacct->ac_flag; -- cgit v0.10.2 From ae424ae4b5bcd820ad6ee6f0b986c4e14ed4d6cf Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Fri, 8 Dec 2006 02:36:08 -0800 Subject: [PATCH] make set_special_pids() static Make set_special_pids() static, the only caller is daemonize(). Signed-off-by: Oleg Nesterov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/include/linux/sched.h b/include/linux/sched.h index dede82c..5e8a0ba 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1240,7 +1240,6 @@ extern struct mm_struct init_mm; #define find_task_by_pid(nr) find_task_by_pid_type(PIDTYPE_PID, nr) extern struct task_struct *find_task_by_pid_type(int type, int pid); -extern void set_special_pids(pid_t session, pid_t pgrp); extern void __set_special_pids(pid_t session, pid_t pgrp); /* per-UID process charging. */ diff --git a/kernel/exit.c b/kernel/exit.c index fa23577..fa0495e 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -314,7 +314,7 @@ void __set_special_pids(pid_t session, pid_t pgrp) } } -void set_special_pids(pid_t session, pid_t pgrp) +static void set_special_pids(pid_t session, pid_t pgrp) { write_lock_irq(&tasklist_lock); __set_special_pids(session, pgrp); -- cgit v0.10.2 From dae3c5a0b7052ad7dd9fa78c51ecfab828c5007b Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Fri, 8 Dec 2006 02:36:09 -0800 Subject: [PATCH] sys_unshare: remove a broken CLONE_SIGHAND code sys_unshare(CLONE_SIGHAND) is broken, the code under 'if (new_sigh)' is never executed but very wrong. Just remove it to avoid a confusion, task_lock() has nothing to do with ->sighand changing. Also, change the comment in unshare_sighand(). Yes, CLONE_THREAD implies CLONE_SIGHAND, but still it looks confusing. Also, we don't need to check current->sighand != NULL. Signed-off-by: Oleg Nesterov Acked-by: Peter Zijlstra Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/kernel/fork.c b/kernel/fork.c index 7f2e31b..f387a13 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1544,15 +1544,13 @@ static int unshare_namespace(unsigned long unshare_flags, struct namespace **new } /* - * Unsharing of sighand for tasks created with CLONE_SIGHAND is not - * supported yet + * Unsharing of sighand is not supported yet */ static int unshare_sighand(unsigned long unshare_flags, struct sighand_struct **new_sighp) { struct sighand_struct *sigh = current->sighand; - if ((unshare_flags & CLONE_SIGHAND) && - (sigh && atomic_read(&sigh->count) > 1)) + if ((unshare_flags & CLONE_SIGHAND) && atomic_read(&sigh->count) > 1) return -EINVAL; else return 0; @@ -1626,7 +1624,7 @@ asmlinkage long sys_unshare(unsigned long unshare_flags) int err = 0; struct fs_struct *fs, *new_fs = NULL; struct namespace *ns, *new_ns = NULL; - struct sighand_struct *sigh, *new_sigh = NULL; + struct sighand_struct *new_sigh = NULL; struct mm_struct *mm, *new_mm = NULL, *active_mm = NULL; struct files_struct *fd, *new_fd = NULL; struct sem_undo_list *new_ulist = NULL; @@ -1671,7 +1669,7 @@ asmlinkage long sys_unshare(unsigned long unshare_flags) } } - if (new_fs || new_ns || new_sigh || new_mm || new_fd || new_ulist || + if (new_fs || new_ns || new_mm || new_fd || new_ulist || new_uts || new_ipc) { task_lock(current); @@ -1693,12 +1691,6 @@ asmlinkage long sys_unshare(unsigned long unshare_flags) new_ns = ns; } - if (new_sigh) { - sigh = current->sighand; - rcu_assign_pointer(current->sighand, new_sigh); - new_sigh = sigh; - } - if (new_mm) { mm = current->mm; active_mm = current->active_mm; -- cgit v0.10.2 From adb9250a07edb7d41a17ba3b96fcb84c4d8e4260 Mon Sep 17 00:00:00 2001 From: Thomas Maier Date: Fri, 8 Dec 2006 02:36:10 -0800 Subject: [PATCH] pktcdvd: reusability of procfs functions This patch makes some of the procfs functions reusable (for coming sysfs patch e.g.): pkt_setup_dev() pkt_remove_dev() ... Signed-off-by: Thomas Maier Signed-off-by: Peter Osterlund Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c index e45eaa2..7e7b892 100644 --- a/drivers/block/pktcdvd.c +++ b/drivers/block/pktcdvd.c @@ -1,6 +1,7 @@ /* * Copyright (C) 2000 Jens Axboe * Copyright (C) 2001-2004 Peter Osterlund + * Copyright (C) 2006 Thomas Maier * * May be copied or modified under the terms of the GNU General Public * License. See linux/COPYING for more information. @@ -2436,36 +2437,33 @@ static struct block_device_operations pktcdvd_ops = { /* * Set up mapping from pktcdvd device to CD-ROM device. */ -static int pkt_setup_dev(struct pkt_ctrl_command *ctrl_cmd) +static int pkt_setup_dev(dev_t dev, dev_t* pkt_dev) { int idx; int ret = -ENOMEM; struct pktcdvd_device *pd; struct gendisk *disk; - dev_t dev = new_decode_dev(ctrl_cmd->dev); + + mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); for (idx = 0; idx < MAX_WRITERS; idx++) if (!pkt_devs[idx]) break; if (idx == MAX_WRITERS) { printk(DRIVER_NAME": max %d writers supported\n", MAX_WRITERS); - return -EBUSY; + ret = -EBUSY; + goto out_mutex; } pd = kzalloc(sizeof(struct pktcdvd_device), GFP_KERNEL); if (!pd) - return ret; + goto out_mutex; pd->rb_pool = mempool_create_kmalloc_pool(PKT_RB_POOL_SIZE, sizeof(struct pkt_rb_node)); if (!pd->rb_pool) goto out_mem; - disk = alloc_disk(1); - if (!disk) - goto out_mem; - pd->disk = disk; - INIT_LIST_HEAD(&pd->cdrw.pkt_free_list); INIT_LIST_HEAD(&pd->cdrw.pkt_active_list); spin_lock_init(&pd->cdrw.active_list_lock); @@ -2476,11 +2474,15 @@ static int pkt_setup_dev(struct pkt_ctrl_command *ctrl_cmd) init_waitqueue_head(&pd->wqueue); pd->bio_queue = RB_ROOT; + disk = alloc_disk(1); + if (!disk) + goto out_mem; + pd->disk = disk; disk->major = pktdev_major; disk->first_minor = idx; disk->fops = &pktcdvd_ops; disk->flags = GENHD_FL_REMOVABLE; - sprintf(disk->disk_name, DRIVER_NAME"%d", idx); + strcpy(disk->disk_name, pd->name); disk->private_data = pd; disk->queue = blk_alloc_queue(GFP_KERNEL); if (!disk->queue) @@ -2492,8 +2494,12 @@ static int pkt_setup_dev(struct pkt_ctrl_command *ctrl_cmd) goto out_new_dev; add_disk(disk); + pkt_devs[idx] = pd; - ctrl_cmd->pkt_dev = new_encode_dev(pd->pkt_dev); + if (pkt_dev) + *pkt_dev = pd->pkt_dev; + + mutex_unlock(&ctl_mutex); return 0; out_new_dev: @@ -2504,17 +2510,22 @@ out_mem: if (pd->rb_pool) mempool_destroy(pd->rb_pool); kfree(pd); +out_mutex: + mutex_unlock(&ctl_mutex); + printk(DRIVER_NAME": setup of pktcdvd device failed\n"); return ret; } /* * Tear down mapping from pktcdvd device to CD-ROM device. */ -static int pkt_remove_dev(struct pkt_ctrl_command *ctrl_cmd) +static int pkt_remove_dev(dev_t pkt_dev) { struct pktcdvd_device *pd; int idx; - dev_t pkt_dev = new_decode_dev(ctrl_cmd->pkt_dev); + int ret = 0; + + mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); for (idx = 0; idx < MAX_WRITERS; idx++) { pd = pkt_devs[idx]; @@ -2523,12 +2534,14 @@ static int pkt_remove_dev(struct pkt_ctrl_command *ctrl_cmd) } if (idx == MAX_WRITERS) { DPRINTK(DRIVER_NAME": dev not setup\n"); - return -ENXIO; + ret = -ENXIO; + goto out; } - if (pd->refcnt > 0) - return -EBUSY; - + if (pd->refcnt > 0) { + ret = -EBUSY; + goto out; + } if (!IS_ERR(pd->cdrw.thread)) kthread_stop(pd->cdrw.thread); @@ -2547,12 +2560,19 @@ static int pkt_remove_dev(struct pkt_ctrl_command *ctrl_cmd) /* This is safe: open() is still holding a reference. */ module_put(THIS_MODULE); - return 0; + +out: + mutex_unlock(&ctl_mutex); + return ret; } static void pkt_get_status(struct pkt_ctrl_command *ctrl_cmd) { - struct pktcdvd_device *pd = pkt_find_dev_from_minor(ctrl_cmd->dev_index); + struct pktcdvd_device *pd; + + mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); + + pd = pkt_find_dev_from_minor(ctrl_cmd->dev_index); if (pd) { ctrl_cmd->dev = new_encode_dev(pd->bdev->bd_dev); ctrl_cmd->pkt_dev = new_encode_dev(pd->pkt_dev); @@ -2561,6 +2581,8 @@ static void pkt_get_status(struct pkt_ctrl_command *ctrl_cmd) ctrl_cmd->pkt_dev = 0; } ctrl_cmd->num_devices = MAX_WRITERS; + + mutex_unlock(&ctl_mutex); } static int pkt_ctl_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) @@ -2568,6 +2590,7 @@ static int pkt_ctl_ioctl(struct inode *inode, struct file *file, unsigned int cm void __user *argp = (void __user *)arg; struct pkt_ctrl_command ctrl_cmd; int ret = 0; + dev_t pkt_dev = 0; if (cmd != PACKET_CTRL_CMD) return -ENOTTY; @@ -2579,21 +2602,16 @@ static int pkt_ctl_ioctl(struct inode *inode, struct file *file, unsigned int cm case PKT_CTRL_CMD_SETUP: if (!capable(CAP_SYS_ADMIN)) return -EPERM; - mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); - ret = pkt_setup_dev(&ctrl_cmd); - mutex_unlock(&ctl_mutex); + ret = pkt_setup_dev(new_decode_dev(ctrl_cmd.dev), &pkt_dev); + ctrl_cmd.pkt_dev = new_encode_dev(pkt_dev); break; case PKT_CTRL_CMD_TEARDOWN: if (!capable(CAP_SYS_ADMIN)) return -EPERM; - mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); - ret = pkt_remove_dev(&ctrl_cmd); - mutex_unlock(&ctl_mutex); + ret = pkt_remove_dev(new_decode_dev(ctrl_cmd.pkt_dev)); break; case PKT_CTRL_CMD_STATUS: - mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); pkt_get_status(&ctrl_cmd); - mutex_unlock(&ctl_mutex); break; default: return -ENOTTY; -- cgit v0.10.2 From 2d4eeec563a0472b68de3597c17f2d3b11c49c00 Mon Sep 17 00:00:00 2001 From: Thomas Maier Date: Fri, 8 Dec 2006 02:36:10 -0800 Subject: [PATCH] pktcdvd: make procfs interface optional pktcdvd: Update Kconfig help text. Signed-off-by: Thomas Maier Signed-off-by: Peter Osterlund Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index 8507244..ce9cfcb 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig @@ -431,14 +431,18 @@ config CDROM_PKTCDVD tristate "Packet writing on CD/DVD media" depends on !UML help - If you have a CDROM drive that supports packet writing, say Y to - include preliminary support. It should work with any MMC/Mt Fuji - compliant ATAPI or SCSI drive, which is just about any newer CD - writer. + If you have a CDROM/DVD drive that supports packet writing, say + Y to include support. It should work with any MMC/Mt Fuji + compliant ATAPI or SCSI drive, which is just about any newer + DVD/CD writer. - Currently only writing to CD-RW, DVD-RW and DVD+RW discs is possible. + Currently only writing to CD-RW, DVD-RW, DVD+RW and DVDRAM discs + is possible. DVD-RW disks must be in restricted overwrite mode. + See the file + for further information on the use of this driver. + To compile this driver as a module, choose M here: the module will be called pktcdvd. -- cgit v0.10.2 From 0a0fc9601dd1024ec7171993bf075a789246e1ed Mon Sep 17 00:00:00 2001 From: Thomas Maier Date: Fri, 8 Dec 2006 02:36:11 -0800 Subject: [PATCH] pktcdvd: bio write congestion using congestion_wait() This adds a bio write queue congestion control to the pktcdvd driver with fixed on/off marks. It prevents that the driver consumes a unlimited amount of write requests. [akpm@osdl.org: sync with congestion_wait() renaming] Signed-off-by: Thomas Maier Cc: Peter Osterlund Cc: Jens Axboe Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c index 7e7b892..91fbe7f 100644 --- a/drivers/block/pktcdvd.c +++ b/drivers/block/pktcdvd.c @@ -84,6 +84,8 @@ static struct pktcdvd_device *pkt_devs[MAX_WRITERS]; static struct proc_dir_entry *pkt_proc; static int pktdev_major; +static int write_congestion_on = PKT_WRITE_CONGESTION_ON; +static int write_congestion_off = PKT_WRITE_CONGESTION_OFF; static struct mutex ctl_mutex; /* Serialize open/close/setup/teardown */ static mempool_t *psd_pool; @@ -894,6 +896,7 @@ static int pkt_handle_queue(struct pktcdvd_device *pd) sector_t zone = 0; /* Suppress gcc warning */ struct pkt_rb_node *node, *first_node; struct rb_node *n; + int wakeup; VPRINTK("handle_queue\n"); @@ -966,7 +969,13 @@ try_next_bio: pkt->write_size += bio->bi_size / CD_FRAMESIZE; spin_unlock(&pkt->lock); } + /* check write congestion marks, and if bio_queue_size is + below, wake up any waiters */ + wakeup = (pd->write_congestion_on > 0 + && pd->bio_queue_size <= pd->write_congestion_off); spin_unlock(&pd->lock); + if (wakeup) + blk_clear_queue_congested(pd->disk->queue, WRITE); pkt->sleep_time = max(PACKET_WAIT_TIME, 1); pkt_set_state(pkt, PACKET_WAITING_STATE); @@ -2179,6 +2188,23 @@ static int pkt_make_request(request_queue_t *q, struct bio *bio) } spin_unlock(&pd->cdrw.active_list_lock); + /* + * Test if there is enough room left in the bio work queue + * (queue size >= congestion on mark). + * If not, wait till the work queue size is below the congestion off mark. + */ + spin_lock(&pd->lock); + if (pd->write_congestion_on > 0 + && pd->bio_queue_size >= pd->write_congestion_on) { + blk_set_queue_congested(q, WRITE); + do { + spin_unlock(&pd->lock); + congestion_wait(WRITE, HZ); + spin_lock(&pd->lock); + } while(pd->bio_queue_size > pd->write_congestion_off); + } + spin_unlock(&pd->lock); + /* * No matching packet found. Store the bio in the work queue. */ @@ -2298,6 +2324,9 @@ static int pkt_seq_show(struct seq_file *m, void *p) seq_printf(m, "\tstate:\t\t\ti:%d ow:%d rw:%d ww:%d rec:%d fin:%d\n", states[0], states[1], states[2], states[3], states[4], states[5]); + seq_printf(m, "\twrite congestion marks:\toff=%d on=%d\n", + pd->write_congestion_off, + pd->write_congestion_on); return 0; } @@ -2474,6 +2503,9 @@ static int pkt_setup_dev(dev_t dev, dev_t* pkt_dev) init_waitqueue_head(&pd->wqueue); pd->bio_queue = RB_ROOT; + pd->write_congestion_on = write_congestion_on; + pd->write_congestion_off = write_congestion_off; + disk = alloc_disk(1); if (!disk) goto out_mem; diff --git a/include/linux/pktcdvd.h b/include/linux/pktcdvd.h index 8a94c71..9b1a185 100644 --- a/include/linux/pktcdvd.h +++ b/include/linux/pktcdvd.h @@ -112,6 +112,12 @@ struct pkt_ctrl_command { #include #include + +/* default bio write queue congestion marks */ +#define PKT_WRITE_CONGESTION_ON 10000 +#define PKT_WRITE_CONGESTION_OFF 9000 + + struct packet_settings { __u32 size; /* packet size in (512 byte) sectors */ @@ -271,6 +277,9 @@ struct pktcdvd_device struct packet_iosched iosched; struct gendisk *disk; + + int write_congestion_off; + int write_congestion_on; }; #endif /* __KERNEL__ */ -- cgit v0.10.2 From 32694850a91bd4fedcdd4a46292f870588be81d1 Mon Sep 17 00:00:00 2001 From: Thomas Maier Date: Fri, 8 Dec 2006 02:36:12 -0800 Subject: [PATCH] pktcdvd: add sysfs and debugfs interface Add a sysfs and debugfs interface to the pktcdvd driver. Look into the Documentation/ABI/testing/* files in the patch for more info. Signed-off-by: Thomas Maier Signed-off-by: Peter Osterlund Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/Documentation/ABI/testing/debugfs-pktcdvd b/Documentation/ABI/testing/debugfs-pktcdvd new file mode 100644 index 0000000..03dbd88 --- /dev/null +++ b/Documentation/ABI/testing/debugfs-pktcdvd @@ -0,0 +1,20 @@ +What: /debug/pktcdvd/pktcdvd[0-7] +Date: Oct. 2006 +KernelVersion: 2.6.19 +Contact: Thomas Maier +Description: + +debugfs interface +----------------- + +The pktcdvd module (packet writing driver) creates +these files in debugfs: + +/debug/pktcdvd/pktcdvd[0-7]/ + info (0444) Lots of human readable driver + statistics and infos. Multiple lines! + +Example: +------- + +cat /debug/pktcdvd/pktcdvd0/info diff --git a/Documentation/ABI/testing/sysfs-class-pktcdvd b/Documentation/ABI/testing/sysfs-class-pktcdvd new file mode 100644 index 0000000..c4c55ed --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-pktcdvd @@ -0,0 +1,72 @@ +What: /sys/class/pktcdvd/ +Date: Oct. 2006 +KernelVersion: 2.6.19 +Contact: Thomas Maier +Description: + +sysfs interface +--------------- + +The pktcdvd module (packet writing driver) creates +these files in the sysfs: +( is in format major:minor ) + +/sys/class/pktcdvd/ + add (0200) Write a block device id (major:minor) + to create a new pktcdvd device and map + it to the block device. + + remove (0200) Write the pktcdvd device id (major:minor) + to it to remove the pktcdvd device. + + device_map (0444) Shows the device mapping in format: + pktcdvd[0-7] + +/sys/class/pktcdvd/pktcdvd[0-7]/ + dev (0444) Device id + uevent (0200) To send an uevent. + +/sys/class/pktcdvd/pktcdvd[0-7]/stat/ + packets_started (0444) Number of started packets. + packets_finished (0444) Number of finished packets. + + kb_written (0444) kBytes written. + kb_read (0444) kBytes read. + kb_read_gather (0444) kBytes read to fill write packets. + + reset (0200) Write any value to it to reset + pktcdvd device statistic values, like + bytes read/written. + +/sys/class/pktcdvd/pktcdvd[0-7]/write_queue/ + size (0444) Contains the size of the bio write + queue. + + congestion_off (0644) If bio write queue size is below + this mark, accept new bio requests + from the block layer. + + congestion_on (0644) If bio write queue size is higher + as this mark, do no longer accept + bio write requests from the block + layer and wait till the pktcdvd + device has processed enough bio's + so that bio write queue size is + below congestion off mark. + A value of <= 0 disables congestion + control. + + +Example: +-------- +To use the pktcdvd sysfs interface directly, you can do: + +# create a new pktcdvd device mapped to /dev/hdc +echo "22:0" >/sys/class/pktcdvd/add +cat /sys/class/pktcdvd/device_map +# assuming device pktcdvd0 was created, look at stat's +cat /sys/class/pktcdvd/pktcdvd0/stat/kb_written +# print the device id of the mapped block device +fgrep pktcdvd0 /sys/class/pktcdvd/device_map +# remove device, using pktcdvd0 device id 253:0 +echo "253:0" >/sys/class/pktcdvd/remove diff --git a/Documentation/cdrom/packet-writing.txt b/Documentation/cdrom/packet-writing.txt index 3d44c56..7715d22 100644 --- a/Documentation/cdrom/packet-writing.txt +++ b/Documentation/cdrom/packet-writing.txt @@ -90,6 +90,41 @@ Notes to create an ext2 filesystem on the disc. +Using the pktcdvd sysfs interface +--------------------------------- + +Since Linux 2.6.19, the pktcdvd module has a sysfs interface +and can be controlled by it. For example the "pktcdvd" tool uses +this interface. (see http://people.freenet.de/BalaGi#pktcdvd ) + +"pktcdvd" works similar to "pktsetup", e.g.: + + # pktcdvd -a dev_name /dev/hdc + # mkudffs /dev/pktcdvd/dev_name + # mount -t udf -o rw,noatime /dev/pktcdvd/dev_name /dvdram + # cp files /dvdram + # umount /dvdram + # pktcdvd -r dev_name + + +For a description of the sysfs interface look into the file: + + Documentation/ABI/testing/sysfs-block-pktcdvd + + +Using the pktcdvd debugfs interface +----------------------------------- + +To read pktcdvd device infos in human readable form, do: + + # cat /debug/pktcdvd/pktcdvd[0-7]/info + +For a description of the debugfs interface look into the file: + + Documentation/ABI/testing/debugfs-pktcdvd + + + Links ----- diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c index 91fbe7f..7c95c76 100644 --- a/drivers/block/pktcdvd.c +++ b/drivers/block/pktcdvd.c @@ -60,6 +60,8 @@ #include #include #include +#include +#include #include @@ -89,6 +91,419 @@ static int write_congestion_off = PKT_WRITE_CONGESTION_OFF; static struct mutex ctl_mutex; /* Serialize open/close/setup/teardown */ static mempool_t *psd_pool; +static struct class *class_pktcdvd = NULL; /* /sys/class/pktcdvd */ +static struct dentry *pkt_debugfs_root = NULL; /* /debug/pktcdvd */ + +/* forward declaration */ +static int pkt_setup_dev(dev_t dev, dev_t* pkt_dev); +static int pkt_remove_dev(dev_t pkt_dev); +static int pkt_seq_show(struct seq_file *m, void *p); + + + +/* + * create and register a pktcdvd kernel object. + */ +static struct pktcdvd_kobj* pkt_kobj_create(struct pktcdvd_device *pd, + const char* name, + struct kobject* parent, + struct kobj_type* ktype) +{ + struct pktcdvd_kobj *p; + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) + return NULL; + kobject_set_name(&p->kobj, "%s", name); + p->kobj.parent = parent; + p->kobj.ktype = ktype; + p->pd = pd; + if (kobject_register(&p->kobj) != 0) + return NULL; + return p; +} +/* + * remove a pktcdvd kernel object. + */ +static void pkt_kobj_remove(struct pktcdvd_kobj *p) +{ + if (p) + kobject_unregister(&p->kobj); +} +/* + * default release function for pktcdvd kernel objects. + */ +static void pkt_kobj_release(struct kobject *kobj) +{ + kfree(to_pktcdvdkobj(kobj)); +} + + +/********************************************************** + * + * sysfs interface for pktcdvd + * by (C) 2006 Thomas Maier + * + **********************************************************/ + +#define DEF_ATTR(_obj,_name,_mode) \ + static struct attribute _obj = { \ + .name = _name, .owner = THIS_MODULE, .mode = _mode } + +/********************************************************** + /sys/class/pktcdvd/pktcdvd[0-7]/ + stat/reset + stat/packets_started + stat/packets_finished + stat/kb_written + stat/kb_read + stat/kb_read_gather + write_queue/size + write_queue/congestion_off + write_queue/congestion_on + **********************************************************/ + +DEF_ATTR(kobj_pkt_attr_st1, "reset", 0200); +DEF_ATTR(kobj_pkt_attr_st2, "packets_started", 0444); +DEF_ATTR(kobj_pkt_attr_st3, "packets_finished", 0444); +DEF_ATTR(kobj_pkt_attr_st4, "kb_written", 0444); +DEF_ATTR(kobj_pkt_attr_st5, "kb_read", 0444); +DEF_ATTR(kobj_pkt_attr_st6, "kb_read_gather", 0444); + +static struct attribute *kobj_pkt_attrs_stat[] = { + &kobj_pkt_attr_st1, + &kobj_pkt_attr_st2, + &kobj_pkt_attr_st3, + &kobj_pkt_attr_st4, + &kobj_pkt_attr_st5, + &kobj_pkt_attr_st6, + NULL +}; + +DEF_ATTR(kobj_pkt_attr_wq1, "size", 0444); +DEF_ATTR(kobj_pkt_attr_wq2, "congestion_off", 0644); +DEF_ATTR(kobj_pkt_attr_wq3, "congestion_on", 0644); + +static struct attribute *kobj_pkt_attrs_wqueue[] = { + &kobj_pkt_attr_wq1, + &kobj_pkt_attr_wq2, + &kobj_pkt_attr_wq3, + NULL +}; + +/* declares a char buffer[64] _dbuf, copies data from + * _b with length _l into it and ensures that _dbuf ends + * with a \0 character. + */ +#define DECLARE_BUF_AS_STRING(_dbuf, _b, _l) \ + char _dbuf[64]; int dlen = (_l) < 0 ? 0 : (_l); \ + if (dlen >= sizeof(_dbuf)) dlen = sizeof(_dbuf)-1; \ + memcpy(_dbuf, _b, dlen); _dbuf[dlen] = 0 + +static ssize_t kobj_pkt_show(struct kobject *kobj, + struct attribute *attr, char *data) +{ + struct pktcdvd_device *pd = to_pktcdvdkobj(kobj)->pd; + int n = 0; + int v; + if (strcmp(attr->name, "packets_started") == 0) { + n = sprintf(data, "%lu\n", pd->stats.pkt_started); + + } else if (strcmp(attr->name, "packets_finished") == 0) { + n = sprintf(data, "%lu\n", pd->stats.pkt_ended); + + } else if (strcmp(attr->name, "kb_written") == 0) { + n = sprintf(data, "%lu\n", pd->stats.secs_w >> 1); + + } else if (strcmp(attr->name, "kb_read") == 0) { + n = sprintf(data, "%lu\n", pd->stats.secs_r >> 1); + + } else if (strcmp(attr->name, "kb_read_gather") == 0) { + n = sprintf(data, "%lu\n", pd->stats.secs_rg >> 1); + + } else if (strcmp(attr->name, "size") == 0) { + spin_lock(&pd->lock); + v = pd->bio_queue_size; + spin_unlock(&pd->lock); + n = sprintf(data, "%d\n", v); + + } else if (strcmp(attr->name, "congestion_off") == 0) { + spin_lock(&pd->lock); + v = pd->write_congestion_off; + spin_unlock(&pd->lock); + n = sprintf(data, "%d\n", v); + + } else if (strcmp(attr->name, "congestion_on") == 0) { + spin_lock(&pd->lock); + v = pd->write_congestion_on; + spin_unlock(&pd->lock); + n = sprintf(data, "%d\n", v); + } + return n; +} + +static void init_write_congestion_marks(int* lo, int* hi) +{ + if (*hi > 0) { + *hi = max(*hi, 500); + *hi = min(*hi, 1000000); + if (*lo <= 0) + *lo = *hi - 100; + else { + *lo = min(*lo, *hi - 100); + *lo = max(*lo, 100); + } + } else { + *hi = -1; + *lo = -1; + } +} + +static ssize_t kobj_pkt_store(struct kobject *kobj, + struct attribute *attr, + const char *data, size_t len) +{ + struct pktcdvd_device *pd = to_pktcdvdkobj(kobj)->pd; + int val; + DECLARE_BUF_AS_STRING(dbuf, data, len); /* ensure sscanf scans a string */ + + if (strcmp(attr->name, "reset") == 0 && dlen > 0) { + pd->stats.pkt_started = 0; + pd->stats.pkt_ended = 0; + pd->stats.secs_w = 0; + pd->stats.secs_rg = 0; + pd->stats.secs_r = 0; + + } else if (strcmp(attr->name, "congestion_off") == 0 + && sscanf(dbuf, "%d", &val) == 1) { + spin_lock(&pd->lock); + pd->write_congestion_off = val; + init_write_congestion_marks(&pd->write_congestion_off, + &pd->write_congestion_on); + spin_unlock(&pd->lock); + + } else if (strcmp(attr->name, "congestion_on") == 0 + && sscanf(dbuf, "%d", &val) == 1) { + spin_lock(&pd->lock); + pd->write_congestion_on = val; + init_write_congestion_marks(&pd->write_congestion_off, + &pd->write_congestion_on); + spin_unlock(&pd->lock); + } + return len; +} + +static struct sysfs_ops kobj_pkt_ops = { + .show = kobj_pkt_show, + .store = kobj_pkt_store +}; +static struct kobj_type kobj_pkt_type_stat = { + .release = pkt_kobj_release, + .sysfs_ops = &kobj_pkt_ops, + .default_attrs = kobj_pkt_attrs_stat +}; +static struct kobj_type kobj_pkt_type_wqueue = { + .release = pkt_kobj_release, + .sysfs_ops = &kobj_pkt_ops, + .default_attrs = kobj_pkt_attrs_wqueue +}; + +static void pkt_sysfs_dev_new(struct pktcdvd_device *pd) +{ + if (class_pktcdvd) { + pd->clsdev = class_device_create(class_pktcdvd, + NULL, pd->pkt_dev, + NULL, "%s", pd->name); + if (IS_ERR(pd->clsdev)) + pd->clsdev = NULL; + } + if (pd->clsdev) { + pd->kobj_stat = pkt_kobj_create(pd, "stat", + &pd->clsdev->kobj, + &kobj_pkt_type_stat); + pd->kobj_wqueue = pkt_kobj_create(pd, "write_queue", + &pd->clsdev->kobj, + &kobj_pkt_type_wqueue); + } +} + +static void pkt_sysfs_dev_remove(struct pktcdvd_device *pd) +{ + pkt_kobj_remove(pd->kobj_stat); + pkt_kobj_remove(pd->kobj_wqueue); + if (class_pktcdvd) + class_device_destroy(class_pktcdvd, pd->pkt_dev); +} + + +/******************************************************************** + /sys/class/pktcdvd/ + add map block device + remove unmap packet dev + device_map show mappings + *******************************************************************/ + +static void class_pktcdvd_release(struct class *cls) +{ + kfree(cls); +} +static ssize_t class_pktcdvd_show_map(struct class *c, char *data) +{ + int n = 0; + int idx; + mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); + for (idx = 0; idx < MAX_WRITERS; idx++) { + struct pktcdvd_device *pd = pkt_devs[idx]; + if (!pd) + continue; + n += sprintf(data+n, "%s %u:%u %u:%u\n", + pd->name, + MAJOR(pd->pkt_dev), MINOR(pd->pkt_dev), + MAJOR(pd->bdev->bd_dev), + MINOR(pd->bdev->bd_dev)); + } + mutex_unlock(&ctl_mutex); + return n; +} + +static ssize_t class_pktcdvd_store_add(struct class *c, const char *buf, + size_t count) +{ + unsigned int major, minor; + DECLARE_BUF_AS_STRING(dbuf, buf, count); + if (sscanf(dbuf, "%u:%u", &major, &minor) == 2) { + pkt_setup_dev(MKDEV(major, minor), NULL); + return count; + } + return -EINVAL; +} + +static ssize_t class_pktcdvd_store_remove(struct class *c, const char *buf, + size_t count) +{ + unsigned int major, minor; + DECLARE_BUF_AS_STRING(dbuf, buf, count); + if (sscanf(dbuf, "%u:%u", &major, &minor) == 2) { + pkt_remove_dev(MKDEV(major, minor)); + return count; + } + return -EINVAL; +} + +static struct class_attribute class_pktcdvd_attrs[] = { + __ATTR(add, 0200, NULL, class_pktcdvd_store_add), + __ATTR(remove, 0200, NULL, class_pktcdvd_store_remove), + __ATTR(device_map, 0444, class_pktcdvd_show_map, NULL), + __ATTR_NULL +}; + + +static int pkt_sysfs_init(void) +{ + int ret = 0; + + /* + * create control files in sysfs + * /sys/class/pktcdvd/... + */ + class_pktcdvd = kzalloc(sizeof(*class_pktcdvd), GFP_KERNEL); + if (!class_pktcdvd) + return -ENOMEM; + class_pktcdvd->name = DRIVER_NAME; + class_pktcdvd->owner = THIS_MODULE; + class_pktcdvd->class_release = class_pktcdvd_release; + class_pktcdvd->class_attrs = class_pktcdvd_attrs; + ret = class_register(class_pktcdvd); + if (ret) { + kfree(class_pktcdvd); + class_pktcdvd = NULL; + printk(DRIVER_NAME": failed to create class pktcdvd\n"); + return ret; + } + return 0; +} + +static void pkt_sysfs_cleanup(void) +{ + if (class_pktcdvd) + class_destroy(class_pktcdvd); + class_pktcdvd = NULL; +} + +/******************************************************************** + entries in debugfs + + /debugfs/pktcdvd[0-7]/ + info + + *******************************************************************/ + +static int pkt_debugfs_seq_show(struct seq_file *m, void *p) +{ + return pkt_seq_show(m, p); +} + +static int pkt_debugfs_fops_open(struct inode *inode, struct file *file) +{ + return single_open(file, pkt_debugfs_seq_show, inode->i_private); +} + +static struct file_operations debug_fops = { + .open = pkt_debugfs_fops_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static void pkt_debugfs_dev_new(struct pktcdvd_device *pd) +{ + if (!pkt_debugfs_root) + return; + pd->dfs_f_info = NULL; + pd->dfs_d_root = debugfs_create_dir(pd->name, pkt_debugfs_root); + if (IS_ERR(pd->dfs_d_root)) { + pd->dfs_d_root = NULL; + return; + } + pd->dfs_f_info = debugfs_create_file("info", S_IRUGO, + pd->dfs_d_root, pd, &debug_fops); + if (IS_ERR(pd->dfs_f_info)) { + pd->dfs_f_info = NULL; + return; + } +} + +static void pkt_debugfs_dev_remove(struct pktcdvd_device *pd) +{ + if (!pkt_debugfs_root) + return; + if (pd->dfs_f_info) + debugfs_remove(pd->dfs_f_info); + pd->dfs_f_info = NULL; + if (pd->dfs_d_root) + debugfs_remove(pd->dfs_d_root); + pd->dfs_d_root = NULL; +} + +static void pkt_debugfs_init(void) +{ + pkt_debugfs_root = debugfs_create_dir(DRIVER_NAME, NULL); + if (IS_ERR(pkt_debugfs_root)) { + pkt_debugfs_root = NULL; + return; + } +} + +static void pkt_debugfs_cleanup(void) +{ + if (!pkt_debugfs_root) + return; + debugfs_remove(pkt_debugfs_root); + pkt_debugfs_root = NULL; +} + +/* ----------------------------------------------------------*/ + static void pkt_bio_finished(struct pktcdvd_device *pd) { @@ -2527,6 +2942,9 @@ static int pkt_setup_dev(dev_t dev, dev_t* pkt_dev) add_disk(disk); + pkt_sysfs_dev_new(pd); + pkt_debugfs_dev_new(pd); + pkt_devs[idx] = pd; if (pkt_dev) *pkt_dev = pd->pkt_dev; @@ -2577,6 +2995,11 @@ static int pkt_remove_dev(dev_t pkt_dev) if (!IS_ERR(pd->cdrw.thread)) kthread_stop(pd->cdrw.thread); + pkt_devs[idx] = NULL; + + pkt_debugfs_dev_remove(pd); + pkt_sysfs_dev_remove(pd); + blkdev_put(pd->bdev); remove_proc_entry(pd->name, pkt_proc); @@ -2586,7 +3009,6 @@ static int pkt_remove_dev(dev_t pkt_dev) blk_cleanup_queue(pd->disk->queue); put_disk(pd->disk); - pkt_devs[idx] = NULL; mempool_destroy(pd->rb_pool); kfree(pd); @@ -2670,6 +3092,8 @@ static int __init pkt_init(void) { int ret; + mutex_init(&ctl_mutex); + psd_pool = mempool_create_kmalloc_pool(PSD_POOL_SIZE, sizeof(struct packet_stacked_data)); if (!psd_pool) @@ -2683,18 +3107,25 @@ static int __init pkt_init(void) if (!pktdev_major) pktdev_major = ret; + ret = pkt_sysfs_init(); + if (ret) + goto out; + + pkt_debugfs_init(); + ret = misc_register(&pkt_misc); if (ret) { printk(DRIVER_NAME": Unable to register misc device\n"); - goto out; + goto out_misc; } - mutex_init(&ctl_mutex); - pkt_proc = proc_mkdir(DRIVER_NAME, proc_root_driver); return 0; +out_misc: + pkt_debugfs_cleanup(); + pkt_sysfs_cleanup(); out: unregister_blkdev(pktdev_major, DRIVER_NAME); out2: @@ -2706,6 +3137,10 @@ static void __exit pkt_exit(void) { remove_proc_entry(DRIVER_NAME, proc_root_driver); misc_deregister(&pkt_misc); + + pkt_debugfs_cleanup(); + pkt_sysfs_cleanup(); + unregister_blkdev(pktdev_major, DRIVER_NAME); mempool_destroy(psd_pool); } diff --git a/include/linux/pktcdvd.h b/include/linux/pktcdvd.h index 9b1a185..5ea4f05 100644 --- a/include/linux/pktcdvd.h +++ b/include/linux/pktcdvd.h @@ -111,7 +111,8 @@ struct pkt_ctrl_command { #include #include #include - +#include +#include /* default bio write queue congestion marks */ #define PKT_WRITE_CONGESTION_ON 10000 @@ -247,6 +248,14 @@ struct packet_stacked_data }; #define PSD_POOL_SIZE 64 +struct pktcdvd_kobj +{ + struct kobject kobj; + struct pktcdvd_device *pd; +}; +#define to_pktcdvdkobj(_k) \ + ((struct pktcdvd_kobj*)container_of(_k,struct pktcdvd_kobj,kobj)) + struct pktcdvd_device { struct block_device *bdev; /* dev attached */ @@ -280,6 +289,13 @@ struct pktcdvd_device int write_congestion_off; int write_congestion_on; + + struct class_device *clsdev; /* sysfs pktcdvd[0-7] class dev */ + struct pktcdvd_kobj *kobj_stat; /* sysfs pktcdvd[0-7]/stat/ */ + struct pktcdvd_kobj *kobj_wqueue; /* sysfs pktcdvd[0-7]/write_queue/ */ + + struct dentry *dfs_d_root; /* debugfs: devname directory */ + struct dentry *dfs_f_info; /* debugfs: info file */ }; #endif /* __KERNEL__ */ -- cgit v0.10.2 From 2e7b651df113c8a463853e4169951c52c39f9d19 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Fri, 8 Dec 2006 02:36:13 -0800 Subject: [PATCH] remove the old bd_mutex lockdep annotation Remove the old complex and crufty bd_mutex annotation. Signed-off-by: Peter Zijlstra Cc: Neil Brown Cc: Ingo Molnar Cc: Arjan van de Ven Cc: Jason Baron Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/block/ioctl.c b/block/ioctl.c index 58aab63..9fddb99 100644 --- a/block/ioctl.c +++ b/block/ioctl.c @@ -72,7 +72,7 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user bdevp = bdget_disk(disk, part); if (!bdevp) return -ENOMEM; - mutex_lock_nested(&bdevp->bd_mutex, BD_MUTEX_PARTITION); + mutex_lock(&bdevp->bd_mutex); if (bdevp->bd_openers) { mutex_unlock(&bdevp->bd_mutex); bdput(bdevp); @@ -82,7 +82,7 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user fsync_bdev(bdevp); invalidate_bdev(bdevp, 0); - mutex_lock_nested(&bdev->bd_mutex, BD_MUTEX_WHOLE); + mutex_lock(&bdev->bd_mutex); delete_partition(disk, part); mutex_unlock(&bdev->bd_mutex); mutex_unlock(&bdevp->bd_mutex); diff --git a/drivers/md/md.c b/drivers/md/md.c index 6c4345b..d2bcb41 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -1413,7 +1413,7 @@ static int lock_rdev(mdk_rdev_t *rdev, dev_t dev) struct block_device *bdev; char b[BDEVNAME_SIZE]; - bdev = open_partition_by_devnum(dev, FMODE_READ|FMODE_WRITE); + bdev = open_by_devnum(dev, FMODE_READ|FMODE_WRITE); if (IS_ERR(bdev)) { printk(KERN_ERR "md: could not open %s.\n", __bdevname(dev, b)); @@ -1423,7 +1423,7 @@ static int lock_rdev(mdk_rdev_t *rdev, dev_t dev) if (err) { printk(KERN_ERR "md: could not bd_claim %s.\n", bdevname(bdev, b)); - blkdev_put_partition(bdev); + blkdev_put(bdev); return err; } rdev->bdev = bdev; @@ -1437,7 +1437,7 @@ static void unlock_rdev(mdk_rdev_t *rdev) if (!bdev) MD_BUG(); bd_release(bdev); - blkdev_put_partition(bdev); + blkdev_put(bdev); } void md_autodetect_dev(dev_t dev); diff --git a/fs/block_dev.c b/fs/block_dev.c index 13816b4d..36c38f4 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -762,7 +762,7 @@ static int bd_claim_by_kobject(struct block_device *bdev, void *holder, if (!bo) return -ENOMEM; - mutex_lock_nested(&bdev->bd_mutex, BD_MUTEX_PARTITION); + mutex_lock(&bdev->bd_mutex); res = bd_claim(bdev, holder); if (res == 0) { found = find_bd_holder(bdev, bo); @@ -796,7 +796,7 @@ static void bd_release_from_kobject(struct block_device *bdev, if (!kobj) return; - mutex_lock_nested(&bdev->bd_mutex, BD_MUTEX_PARTITION); + mutex_lock(&bdev->bd_mutex); bd_release(bdev); if ((bo = del_bd_holder(bdev, kobj))) free_bd_holder(bo); @@ -854,22 +854,6 @@ struct block_device *open_by_devnum(dev_t dev, unsigned mode) EXPORT_SYMBOL(open_by_devnum); -static int -blkdev_get_partition(struct block_device *bdev, mode_t mode, unsigned flags); - -struct block_device *open_partition_by_devnum(dev_t dev, unsigned mode) -{ - struct block_device *bdev = bdget(dev); - int err = -ENOMEM; - int flags = mode & FMODE_WRITE ? O_RDWR : O_RDONLY; - if (bdev) - err = blkdev_get_partition(bdev, mode, flags); - return err ? ERR_PTR(err) : bdev; -} - -EXPORT_SYMBOL(open_partition_by_devnum); - - /* * This routine checks whether a removable media has been changed, * and invalidates all buffer-cache-entries in that case. This @@ -916,66 +900,7 @@ void bd_set_size(struct block_device *bdev, loff_t size) } EXPORT_SYMBOL(bd_set_size); -static int __blkdev_put(struct block_device *bdev, unsigned int subclass) -{ - int ret = 0; - struct inode *bd_inode = bdev->bd_inode; - struct gendisk *disk = bdev->bd_disk; - - mutex_lock_nested(&bdev->bd_mutex, subclass); - lock_kernel(); - if (!--bdev->bd_openers) { - sync_blockdev(bdev); - kill_bdev(bdev); - } - if (bdev->bd_contains == bdev) { - if (disk->fops->release) - ret = disk->fops->release(bd_inode, NULL); - } else { - mutex_lock_nested(&bdev->bd_contains->bd_mutex, - subclass + 1); - bdev->bd_contains->bd_part_count--; - mutex_unlock(&bdev->bd_contains->bd_mutex); - } - if (!bdev->bd_openers) { - struct module *owner = disk->fops->owner; - - put_disk(disk); - module_put(owner); - - if (bdev->bd_contains != bdev) { - kobject_put(&bdev->bd_part->kobj); - bdev->bd_part = NULL; - } - bdev->bd_disk = NULL; - bdev->bd_inode->i_data.backing_dev_info = &default_backing_dev_info; - if (bdev != bdev->bd_contains) - __blkdev_put(bdev->bd_contains, subclass + 1); - bdev->bd_contains = NULL; - } - unlock_kernel(); - mutex_unlock(&bdev->bd_mutex); - bdput(bdev); - return ret; -} - -int blkdev_put(struct block_device *bdev) -{ - return __blkdev_put(bdev, BD_MUTEX_NORMAL); -} -EXPORT_SYMBOL(blkdev_put); - -int blkdev_put_partition(struct block_device *bdev) -{ - return __blkdev_put(bdev, BD_MUTEX_PARTITION); -} -EXPORT_SYMBOL(blkdev_put_partition); - -static int -blkdev_get_whole(struct block_device *bdev, mode_t mode, unsigned flags); - -static int -do_open(struct block_device *bdev, struct file *file, unsigned int subclass) +static int do_open(struct block_device *bdev, struct file *file) { struct module *owner = NULL; struct gendisk *disk; @@ -992,8 +917,7 @@ do_open(struct block_device *bdev, struct file *file, unsigned int subclass) } owner = disk->fops->owner; - mutex_lock_nested(&bdev->bd_mutex, subclass); - + mutex_lock(&bdev->bd_mutex); if (!bdev->bd_openers) { bdev->bd_disk = disk; bdev->bd_contains = bdev; @@ -1020,11 +944,11 @@ do_open(struct block_device *bdev, struct file *file, unsigned int subclass) ret = -ENOMEM; if (!whole) goto out_first; - ret = blkdev_get_whole(whole, file->f_mode, file->f_flags); + ret = blkdev_get(whole, file->f_mode, file->f_flags); if (ret) goto out_first; bdev->bd_contains = whole; - mutex_lock_nested(&whole->bd_mutex, BD_MUTEX_WHOLE); + mutex_lock(&whole->bd_mutex); whole->bd_part_count++; p = disk->part[part - 1]; bdev->bd_inode->i_data.backing_dev_info = @@ -1052,8 +976,7 @@ do_open(struct block_device *bdev, struct file *file, unsigned int subclass) if (bdev->bd_invalidated) rescan_partitions(bdev->bd_disk, bdev); } else { - mutex_lock_nested(&bdev->bd_contains->bd_mutex, - BD_MUTEX_WHOLE); + mutex_lock(&bdev->bd_contains->bd_mutex); bdev->bd_contains->bd_part_count++; mutex_unlock(&bdev->bd_contains->bd_mutex); } @@ -1067,7 +990,7 @@ out_first: bdev->bd_disk = NULL; bdev->bd_inode->i_data.backing_dev_info = &default_backing_dev_info; if (bdev != bdev->bd_contains) - __blkdev_put(bdev->bd_contains, BD_MUTEX_WHOLE); + blkdev_put(bdev->bd_contains); bdev->bd_contains = NULL; put_disk(disk); module_put(owner); @@ -1094,49 +1017,11 @@ int blkdev_get(struct block_device *bdev, mode_t mode, unsigned flags) fake_file.f_dentry = &fake_dentry; fake_dentry.d_inode = bdev->bd_inode; - return do_open(bdev, &fake_file, BD_MUTEX_NORMAL); + return do_open(bdev, &fake_file); } EXPORT_SYMBOL(blkdev_get); -static int -blkdev_get_whole(struct block_device *bdev, mode_t mode, unsigned flags) -{ - /* - * This crockload is due to bad choice of ->open() type. - * It will go away. - * For now, block device ->open() routine must _not_ - * examine anything in 'inode' argument except ->i_rdev. - */ - struct file fake_file = {}; - struct dentry fake_dentry = {}; - fake_file.f_mode = mode; - fake_file.f_flags = flags; - fake_file.f_dentry = &fake_dentry; - fake_dentry.d_inode = bdev->bd_inode; - - return do_open(bdev, &fake_file, BD_MUTEX_WHOLE); -} - -static int -blkdev_get_partition(struct block_device *bdev, mode_t mode, unsigned flags) -{ - /* - * This crockload is due to bad choice of ->open() type. - * It will go away. - * For now, block device ->open() routine must _not_ - * examine anything in 'inode' argument except ->i_rdev. - */ - struct file fake_file = {}; - struct dentry fake_dentry = {}; - fake_file.f_mode = mode; - fake_file.f_flags = flags; - fake_file.f_dentry = &fake_dentry; - fake_dentry.d_inode = bdev->bd_inode; - - return do_open(bdev, &fake_file, BD_MUTEX_PARTITION); -} - static int blkdev_open(struct inode * inode, struct file * filp) { struct block_device *bdev; @@ -1154,7 +1039,7 @@ static int blkdev_open(struct inode * inode, struct file * filp) if (bdev == NULL) return -ENOMEM; - res = do_open(bdev, filp, BD_MUTEX_NORMAL); + res = do_open(bdev, filp); if (res) return res; @@ -1168,6 +1053,51 @@ static int blkdev_open(struct inode * inode, struct file * filp) return res; } +int blkdev_put(struct block_device *bdev) +{ + int ret = 0; + struct inode *bd_inode = bdev->bd_inode; + struct gendisk *disk = bdev->bd_disk; + + mutex_lock(&bdev->bd_mutex); + lock_kernel(); + if (!--bdev->bd_openers) { + sync_blockdev(bdev); + kill_bdev(bdev); + } + if (bdev->bd_contains == bdev) { + if (disk->fops->release) + ret = disk->fops->release(bd_inode, NULL); + } else { + mutex_lock(&bdev->bd_contains->bd_mutex); + bdev->bd_contains->bd_part_count--; + mutex_unlock(&bdev->bd_contains->bd_mutex); + } + if (!bdev->bd_openers) { + struct module *owner = disk->fops->owner; + + put_disk(disk); + module_put(owner); + + if (bdev->bd_contains != bdev) { + kobject_put(&bdev->bd_part->kobj); + bdev->bd_part = NULL; + } + bdev->bd_disk = NULL; + bdev->bd_inode->i_data.backing_dev_info = &default_backing_dev_info; + if (bdev != bdev->bd_contains) { + blkdev_put(bdev->bd_contains); + } + bdev->bd_contains = NULL; + } + unlock_kernel(); + mutex_unlock(&bdev->bd_mutex); + bdput(bdev); + return ret; +} + +EXPORT_SYMBOL(blkdev_put); + static int blkdev_close(struct inode * inode, struct file * filp) { struct block_device *bdev = I_BDEV(filp->f_mapping->host); diff --git a/include/linux/fs.h b/include/linux/fs.h index 70b99fb..45f2cab 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -482,21 +482,6 @@ struct block_device { }; /* - * bdev->bd_mutex nesting subclasses for the lock validator: - * - * 0: normal - * 1: 'whole' - * 2: 'partition' - */ -enum bdev_bd_mutex_lock_class -{ - BD_MUTEX_NORMAL, - BD_MUTEX_WHOLE, - BD_MUTEX_PARTITION -}; - - -/* * Radix-tree tags, for tagging dirty and writeback pages within the pagecache * radix trees */ @@ -1499,7 +1484,6 @@ extern void bd_set_size(struct block_device *, loff_t size); extern void bd_forget(struct inode *inode); extern void bdput(struct block_device *); extern struct block_device *open_by_devnum(dev_t, unsigned); -extern struct block_device *open_partition_by_devnum(dev_t, unsigned); extern const struct address_space_operations def_blk_aops; #else static inline void bd_forget(struct inode *inode) {} @@ -1517,7 +1501,6 @@ extern int blkdev_driver_ioctl(struct inode *inode, struct file *file, extern long compat_blkdev_ioctl(struct file *, unsigned, unsigned long); extern int blkdev_get(struct block_device *, mode_t, unsigned); extern int blkdev_put(struct block_device *); -extern int blkdev_put_partition(struct block_device *); extern int bd_claim(struct block_device *, void *); extern void bd_release(struct block_device *); #ifdef CONFIG_SYSFS -- cgit v0.10.2 From 35a6027f1ab6594068cb8bca7705e4567753946b Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Fri, 8 Dec 2006 02:36:14 -0800 Subject: [PATCH] new bd_mutex lockdep annotation Use the gendisk partition number to set a lock class. Signed-off-by: Peter Zijlstra Cc: Neil Brown Cc: Ingo Molnar Acked-by: Arjan van de Ven Cc: Jason Baron Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/fs/block_dev.c b/fs/block_dev.c index 36c38f4..528cf05 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -355,10 +355,14 @@ static int bdev_set(struct inode *inode, void *data) static LIST_HEAD(all_bdevs); +static struct lock_class_key bdev_part_lock_key; + struct block_device *bdget(dev_t dev) { struct block_device *bdev; struct inode *inode; + struct gendisk *disk; + int part = 0; inode = iget5_locked(bd_mnt->mnt_sb, hash(dev), bdev_test, bdev_set, &dev); @@ -384,6 +388,11 @@ struct block_device *bdget(dev_t dev) list_add(&bdev->bd_list, &all_bdevs); spin_unlock(&bdev_lock); unlock_new_inode(inode); + mutex_init(&bdev->bd_mutex); + disk = get_gendisk(dev, &part); + if (part) + lockdep_set_class(&bdev->bd_mutex, &bdev_part_lock_key); + put_disk(disk); } return bdev; } -- cgit v0.10.2 From fd27c7a1bfa9801544ca961cdb4ca0b4998580b8 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Fri, 8 Dec 2006 02:36:14 -0800 Subject: [PATCH] lockdep: remove lock_key approach to managing nested bd_mutex locks The extra call to get_gendisk is not good. It causes a ->probe and possible module load before it is really appropriate to do this. Cc: Ingo Molnar Acked-by: Peter Zijlstra Signed-off-by: Neil Brown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/fs/block_dev.c b/fs/block_dev.c index 528cf05..36c38f4 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -355,14 +355,10 @@ static int bdev_set(struct inode *inode, void *data) static LIST_HEAD(all_bdevs); -static struct lock_class_key bdev_part_lock_key; - struct block_device *bdget(dev_t dev) { struct block_device *bdev; struct inode *inode; - struct gendisk *disk; - int part = 0; inode = iget5_locked(bd_mnt->mnt_sb, hash(dev), bdev_test, bdev_set, &dev); @@ -388,11 +384,6 @@ struct block_device *bdget(dev_t dev) list_add(&bdev->bd_list, &all_bdevs); spin_unlock(&bdev_lock); unlock_new_inode(inode); - mutex_init(&bdev->bd_mutex); - disk = get_gendisk(dev, &part); - if (part) - lockdep_set_class(&bdev->bd_mutex, &bdev_part_lock_key); - put_disk(disk); } return bdev; } -- cgit v0.10.2 From 37be41241f43109c55d92cdbf303399eea642f14 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Fri, 8 Dec 2006 02:36:16 -0800 Subject: [PATCH] lockdep: simplify some aspects of bd_mutex nesting When we open (actually blkdev_get) a partition we need to also open (get) the whole device that holds the partition. The involves some limited recursion. This patch tries to simplify some aspects of this. As well as opening the whole device, we need to increment ->bd_part_count when a partition is opened (this is used by rescan_partitions to avoid a rescan if any partition is active, as that would be confusing). The main change this patch makes is to move the inc/dec of bd_part_count into blkdev_{get,put} for the whole rather than doing it in blkdev_{get,put} for the partition. More specifically, we introduce __blkdev_get and __blkdev_put which do exactly what blkdev_{get,put} did, only with an extra "for_part" argument (blkget_{get,put} then call the __ version with a '0' for the extra argument). If for_part is 1, then the blkdev is being get(put) because a partition is being opened(closed) for the first(last) time, and so bd_part_count should be updated (on success). The particular advantage of pushing this function down is that the bd_mutex lock (which is needed to update bd_part_count) is already held at the lower level. Note that this slightly changes the semantics of bd_part_count. Instead of updating it whenever a partition is opened or released, it is now only updated on the first open or last release. This is an adequate semantic as it is only ever tested for "== 0". Having introduced these functions we remove the current bd_part_count updates from do_open (which is really the body of blkdev_get) and call __blkdev_get(... 1). Similarly in blkget_put we remove the old bd_part_count updates and call __blkget_put(..., 1). This call is moved to the end of __blkdev_put to avoid nested locks of bd_mutex. Finally the mutex_lock on whole->bd_mutex in do_open can be removed. It was only really needed to protect bd_part_count, and that is now managed (and protected) within the recursive call. The observation that bd_part_count is central to the locking issues, and the modifications to create __blkdev_put are from Peter Zijlstra. Cc: Ingo Molnar Acked-by: Peter Zijlstra Signed-off-by: Neil Brown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/fs/block_dev.c b/fs/block_dev.c index 36c38f4..19f5f15 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -900,7 +900,10 @@ void bd_set_size(struct block_device *bdev, loff_t size) } EXPORT_SYMBOL(bd_set_size); -static int do_open(struct block_device *bdev, struct file *file) +static int __blkdev_get(struct block_device *bdev, mode_t mode, unsigned flags, + int for_part); + +static int do_open(struct block_device *bdev, struct file *file, int for_part) { struct module *owner = NULL; struct gendisk *disk; @@ -944,25 +947,21 @@ static int do_open(struct block_device *bdev, struct file *file) ret = -ENOMEM; if (!whole) goto out_first; - ret = blkdev_get(whole, file->f_mode, file->f_flags); + BUG_ON(for_part); + ret = __blkdev_get(whole, file->f_mode, file->f_flags, 1); if (ret) goto out_first; bdev->bd_contains = whole; - mutex_lock(&whole->bd_mutex); - whole->bd_part_count++; p = disk->part[part - 1]; bdev->bd_inode->i_data.backing_dev_info = whole->bd_inode->i_data.backing_dev_info; if (!(disk->flags & GENHD_FL_UP) || !p || !p->nr_sects) { - whole->bd_part_count--; - mutex_unlock(&whole->bd_mutex); ret = -ENXIO; goto out_first; } kobject_get(&p->kobj); bdev->bd_part = p; bd_set_size(bdev, (loff_t) p->nr_sects << 9); - mutex_unlock(&whole->bd_mutex); } } else { put_disk(disk); @@ -975,13 +974,11 @@ static int do_open(struct block_device *bdev, struct file *file) } if (bdev->bd_invalidated) rescan_partitions(bdev->bd_disk, bdev); - } else { - mutex_lock(&bdev->bd_contains->bd_mutex); - bdev->bd_contains->bd_part_count++; - mutex_unlock(&bdev->bd_contains->bd_mutex); } } bdev->bd_openers++; + if (for_part) + bdev->bd_part_count++; mutex_unlock(&bdev->bd_mutex); unlock_kernel(); return 0; @@ -1002,7 +999,8 @@ out: return ret; } -int blkdev_get(struct block_device *bdev, mode_t mode, unsigned flags) +static int __blkdev_get(struct block_device *bdev, mode_t mode, unsigned flags, + int for_part) { /* * This crockload is due to bad choice of ->open() type. @@ -1017,9 +1015,13 @@ int blkdev_get(struct block_device *bdev, mode_t mode, unsigned flags) fake_file.f_dentry = &fake_dentry; fake_dentry.d_inode = bdev->bd_inode; - return do_open(bdev, &fake_file); + return do_open(bdev, &fake_file, for_part); } +int blkdev_get(struct block_device *bdev, mode_t mode, unsigned flags) +{ + return __blkdev_get(bdev, mode, flags, 0); +} EXPORT_SYMBOL(blkdev_get); static int blkdev_open(struct inode * inode, struct file * filp) @@ -1039,7 +1041,7 @@ static int blkdev_open(struct inode * inode, struct file * filp) if (bdev == NULL) return -ENOMEM; - res = do_open(bdev, filp); + res = do_open(bdev, filp, 0); if (res) return res; @@ -1053,14 +1055,18 @@ static int blkdev_open(struct inode * inode, struct file * filp) return res; } -int blkdev_put(struct block_device *bdev) +static int __blkdev_put(struct block_device *bdev, int for_part) { int ret = 0; struct inode *bd_inode = bdev->bd_inode; struct gendisk *disk = bdev->bd_disk; + struct block_device *victim = NULL; mutex_lock(&bdev->bd_mutex); lock_kernel(); + if (for_part) + bdev->bd_part_count--; + if (!--bdev->bd_openers) { sync_blockdev(bdev); kill_bdev(bdev); @@ -1068,10 +1074,6 @@ int blkdev_put(struct block_device *bdev) if (bdev->bd_contains == bdev) { if (disk->fops->release) ret = disk->fops->release(bd_inode, NULL); - } else { - mutex_lock(&bdev->bd_contains->bd_mutex); - bdev->bd_contains->bd_part_count--; - mutex_unlock(&bdev->bd_contains->bd_mutex); } if (!bdev->bd_openers) { struct module *owner = disk->fops->owner; @@ -1085,17 +1087,22 @@ int blkdev_put(struct block_device *bdev) } bdev->bd_disk = NULL; bdev->bd_inode->i_data.backing_dev_info = &default_backing_dev_info; - if (bdev != bdev->bd_contains) { - blkdev_put(bdev->bd_contains); - } + if (bdev != bdev->bd_contains) + victim = bdev->bd_contains; bdev->bd_contains = NULL; } unlock_kernel(); mutex_unlock(&bdev->bd_mutex); bdput(bdev); + if (victim) + __blkdev_put(victim, 1); return ret; } +int blkdev_put(struct block_device *bdev) +{ + return __blkdev_put(bdev, 0); +} EXPORT_SYMBOL(blkdev_put); static int blkdev_close(struct inode * inode, struct file * filp) -- cgit v0.10.2 From 6796bf54a64df36f96a42ae222423fffe36c58a5 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Fri, 8 Dec 2006 02:36:16 -0800 Subject: [PATCH] lockdep: use mutex_lock_nested for bd_mutex to avoid lockdep warning Now that the nesting in blkdev_{get,put} is simpler, adding mutex_lock_nested is trivial. Cc: Ingo Molnar Acked-by: Peter Zijlstra Signed-off-by: Neil Brown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/fs/block_dev.c b/fs/block_dev.c index 19f5f15..846f32d 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -920,7 +920,7 @@ static int do_open(struct block_device *bdev, struct file *file, int for_part) } owner = disk->fops->owner; - mutex_lock(&bdev->bd_mutex); + mutex_lock_nested(&bdev->bd_mutex, for_part); if (!bdev->bd_openers) { bdev->bd_disk = disk; bdev->bd_contains = bdev; @@ -1062,7 +1062,7 @@ static int __blkdev_put(struct block_device *bdev, int for_part) struct gendisk *disk = bdev->bd_disk; struct block_device *victim = NULL; - mutex_lock(&bdev->bd_mutex); + mutex_lock_nested(&bdev->bd_mutex, for_part); lock_kernel(); if (for_part) bdev->bd_part_count--; -- cgit v0.10.2 From d63a5a74dee87883fda6b7d170244acaac5b05e8 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Fri, 8 Dec 2006 02:36:17 -0800 Subject: [PATCH] lockdep: avoid lockdep warning in md md_open takes ->reconfig_mutex which causes lockdep to complain. This (normally) doesn't have deadlock potential as the possible conflict is with a reconfig_mutex in a different device. I say "normally" because if a loop were created in the array->member hierarchy a deadlock could happen. However that causes bigger problems than a deadlock and should be fixed independently. So we flag the lock in md_open as a nested lock. This requires defining mutex_lock_interruptible_nested. Cc: Ingo Molnar Acked-by: Peter Zijlstra Acked-by: Ingo Molnar Signed-off-by: Neil Brown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/drivers/md/md.c b/drivers/md/md.c index d2bcb41..3ce7a5d 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -4423,7 +4423,7 @@ static int md_open(struct inode *inode, struct file *file) mddev_t *mddev = inode->i_bdev->bd_disk->private_data; int err; - if ((err = mddev_lock(mddev))) + if ((err = mutex_lock_interruptible_nested(&mddev->reconfig_mutex, 1))) goto out; err = 0; diff --git a/include/linux/mutex.h b/include/linux/mutex.h index b2b91c4..a7544af 100644 --- a/include/linux/mutex.h +++ b/include/linux/mutex.h @@ -125,8 +125,10 @@ extern int fastcall mutex_lock_interruptible(struct mutex *lock); #ifdef CONFIG_DEBUG_LOCK_ALLOC extern void mutex_lock_nested(struct mutex *lock, unsigned int subclass); +extern int mutex_lock_interruptible_nested(struct mutex *lock, unsigned int subclass); #else # define mutex_lock_nested(lock, subclass) mutex_lock(lock) +# define mutex_lock_interruptible_nested(lock, subclass) mutex_lock_interruptible(lock) #endif /* diff --git a/kernel/mutex.c b/kernel/mutex.c index 8c71cf7..e7cbbb8 100644 --- a/kernel/mutex.c +++ b/kernel/mutex.c @@ -206,6 +206,15 @@ mutex_lock_nested(struct mutex *lock, unsigned int subclass) } EXPORT_SYMBOL_GPL(mutex_lock_nested); + +int __sched +mutex_lock_interruptible_nested(struct mutex *lock, unsigned int subclass) +{ + might_sleep(); + return __mutex_lock_common(lock, TASK_INTERRUPTIBLE, subclass); +} + +EXPORT_SYMBOL_GPL(mutex_lock_interruptible_nested); #endif /* -- cgit v0.10.2 From c48f70c3d046f021b1c22438604ef2a583380eca Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Fri, 8 Dec 2006 02:36:18 -0800 Subject: [PATCH] bdev: fix ->bd_part_count leak Don't leak a ->bd_part_count when the partition open fails with -ENXIO. Signed-off-by: Peter Zijlstra Acked-by: Neil Brown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/fs/block_dev.c b/fs/block_dev.c index 846f32d..f3c3a44 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -902,6 +902,7 @@ EXPORT_SYMBOL(bd_set_size); static int __blkdev_get(struct block_device *bdev, mode_t mode, unsigned flags, int for_part); +static int __blkdev_put(struct block_device *bdev, int for_part); static int do_open(struct block_device *bdev, struct file *file, int for_part) { @@ -987,7 +988,7 @@ out_first: bdev->bd_disk = NULL; bdev->bd_inode->i_data.backing_dev_info = &default_backing_dev_info; if (bdev != bdev->bd_contains) - blkdev_put(bdev->bd_contains); + __blkdev_put(bdev->bd_contains, 1); bdev->bd_contains = NULL; put_disk(disk); module_put(owner); -- cgit v0.10.2 From 7664c5a1da4711bb6383117f51b94c8dc8f3f1cd Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Fri, 8 Dec 2006 02:36:19 -0800 Subject: [PATCH] Generic BUG implementation This patch adds common handling for kernel BUGs, for use by architectures as they wish. The code is derived from arch/powerpc. The advantages of having common BUG handling are: - consistent BUG reporting across architectures - shared implementation of out-of-line file/line data - implement CONFIG_DEBUG_BUGVERBOSE consistently This means that in inline impact of BUG is just the illegal instruction itself, which is an improvement for i386 and x86-64. A BUG is represented in the instruction stream as an illegal instruction, which has file/line information associated with it. This extra information is stored in the __bug_table section in the ELF file. When the kernel gets an illegal instruction, it first confirms it might possibly be from a BUG (ie, in kernel mode, the right illegal instruction). It then calls report_bug(). This searches __bug_table for a matching instruction pointer, and if found, prints the corresponding file/line information. If report_bug() determines that it wasn't a BUG which caused the trap, it returns BUG_TRAP_TYPE_NONE. Some architectures (powerpc) implement WARN using the same mechanism; if the illegal instruction was the result of a WARN, then report_bug(Q) returns CONFIG_DEBUG_BUGVERBOSE; otherwise it returns BUG_TRAP_TYPE_BUG. lib/bug.c keeps a list of loaded modules which can be searched for __bug_table entries. The architecture must call module_bug_finalize()/module_bug_cleanup() from its corresponding module_finalize/cleanup functions. Unsetting CONFIG_DEBUG_BUGVERBOSE will reduce the kernel size by some amount. At the very least, filename and line information will not be recorded for each but, but architectures may decide to store no extra information per BUG at all. Unfortunately, gcc doesn't have a general way to mark an asm() as noreturn, so architectures will generally have to include an infinite loop (or similar) in the BUG code, so that gcc knows execution won't continue beyond that point. gcc does have a __builtin_trap() operator which may be useful to achieve the same effect, unfortunately it cannot be used to actually implement the BUG itself, because there's no way to get the instruction's address for use in generating the __bug_table entry. [randy.dunlap@oracle.com: Handle BUG=n, GENERIC_BUG=n to prevent build errors] [bunk@stusta.de: include/linux/bug.h must always #include Cc: Andi Kleen Cc: Hugh Dickens Cc: Michael Ellerman Cc: Paul Mackerras Cc: Benjamin Herrenschmidt Cc: Rusty Russell Signed-off-by: Adrian Bunk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/include/asm-generic/bug.h b/include/asm-generic/bug.h index c92ae0f..47e3561 100644 --- a/include/asm-generic/bug.h +++ b/include/asm-generic/bug.h @@ -4,6 +4,22 @@ #include #ifdef CONFIG_BUG + +#ifdef CONFIG_GENERIC_BUG +#ifndef __ASSEMBLY__ +struct bug_entry { + unsigned long bug_addr; +#ifdef CONFIG_DEBUG_BUGVERBOSE + const char *file; + unsigned short line; +#endif + unsigned short flags; +}; +#endif /* __ASSEMBLY__ */ + +#define BUGFLAG_WARNING (1<<0) +#endif /* CONFIG_GENERIC_BUG */ + #ifndef HAVE_ARCH_BUG #define BUG() do { \ printk("BUG: failure at %s:%d/%s()!\n", __FILE__, __LINE__, __FUNCTION__); \ diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 4d4c62d..6e9fceb 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -218,6 +218,14 @@ .stab.indexstr 0 : { *(.stab.indexstr) } \ .comment 0 : { *(.comment) } +#define BUG_TABLE \ + . = ALIGN(8); \ + __bug_table : AT(ADDR(__bug_table) - LOAD_OFFSET) { \ + __start___bug_table = .; \ + *(__bug_table) \ + __stop___bug_table = .; \ + } + #define NOTES \ .notes : { *(.note.*) } :note diff --git a/include/linux/bug.h b/include/linux/bug.h new file mode 100644 index 0000000..42aa0a5 --- /dev/null +++ b/include/linux/bug.h @@ -0,0 +1,47 @@ +#ifndef _LINUX_BUG_H +#define _LINUX_BUG_H + +#include +#include + +enum bug_trap_type { + BUG_TRAP_TYPE_NONE = 0, + BUG_TRAP_TYPE_WARN = 1, + BUG_TRAP_TYPE_BUG = 2, +}; + +#ifdef CONFIG_GENERIC_BUG +#include + +static inline int is_warning_bug(const struct bug_entry *bug) +{ + return bug->flags & BUGFLAG_WARNING; +} + +const struct bug_entry *find_bug(unsigned long bugaddr); + +enum bug_trap_type report_bug(unsigned long bug_addr); + +int module_bug_finalize(const Elf_Ehdr *, const Elf_Shdr *, + struct module *); +void module_bug_cleanup(struct module *); + +/* These are defined by the architecture */ +int is_valid_bugaddr(unsigned long addr); + +#else /* !CONFIG_GENERIC_BUG */ + +static inline enum bug_trap_type report_bug(unsigned long bug_addr) +{ + return BUG_TRAP_TYPE_BUG; +} +static inline int module_bug_finalize(const Elf_Ehdr *hdr, + const Elf_Shdr *sechdrs, + struct module *mod) +{ + return 0; +} +static inline void module_bug_cleanup(struct module *mod) {} + +#endif /* CONFIG_GENERIC_BUG */ +#endif /* _LINUX_BUG_H */ diff --git a/include/linux/module.h b/include/linux/module.h index d33df24..10f771a 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -319,6 +319,13 @@ struct module unsigned int taints; /* same bits as kernel:tainted */ +#ifdef CONFIG_GENERIC_BUG + /* Support for BUG */ + struct list_head bug_list; + struct bug_entry *bug_table; + unsigned num_bugs; +#endif + #ifdef CONFIG_MODULE_UNLOAD /* Reference counts */ struct module_ref ref[NR_CPUS]; diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index b75fed7..ee46fb3 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -285,7 +285,7 @@ config DEBUG_HIGHMEM config DEBUG_BUGVERBOSE bool "Verbose BUG() reporting (adds 70K)" if DEBUG_KERNEL && EMBEDDED depends on BUG - depends on ARM || ARM26 || AVR32 || M32R || M68K || SPARC32 || SPARC64 || X86_32 || FRV || SUPERH + depends on ARM || ARM26 || AVR32 || M32R || M68K || SPARC32 || SPARC64 || X86_32 || FRV || SUPERH || GENERIC_BUG default !EMBEDDED help Say Y here to make BUG() panics output the file name and line number diff --git a/lib/Makefile b/lib/Makefile index fea8f90..7ab486b 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -55,6 +55,8 @@ obj-$(CONFIG_AUDIT_GENERIC) += audit.o obj-$(CONFIG_SWIOTLB) += swiotlb.o +lib-$(CONFIG_GENERIC_BUG) += bug.o + hostprogs-y := gen_crc32table clean-files := crc32table.h diff --git a/lib/bug.c b/lib/bug.c new file mode 100644 index 0000000..014b582 --- /dev/null +++ b/lib/bug.c @@ -0,0 +1,163 @@ +/* + Generic support for BUG() + + This respects the following config options: + + CONFIG_BUG - emit BUG traps. Nothing happens without this. + CONFIG_GENERIC_BUG - enable this code. + CONFIG_DEBUG_BUGVERBOSE - emit full file+line information for each BUG + + CONFIG_BUG and CONFIG_DEBUG_BUGVERBOSE are potentially user-settable + (though they're generally always on). + + CONFIG_GENERIC_BUG is set by each architecture using this code. + + To use this, your architecture must: + + 1. Set up the config options: + - Enable CONFIG_GENERIC_BUG if CONFIG_BUG + + 2. Implement BUG (and optionally BUG_ON, WARN, WARN_ON) + - Define HAVE_ARCH_BUG + - Implement BUG() to generate a faulting instruction + - NOTE: struct bug_entry does not have "file" or "line" entries + when CONFIG_DEBUG_BUGVERBOSE is not enabled, so you must generate + the values accordingly. + + 3. Implement the trap + - In the illegal instruction trap handler (typically), verify + that the fault was in kernel mode, and call report_bug() + - report_bug() will return whether it was a false alarm, a warning, + or an actual bug. + - You must implement the is_valid_bugaddr(bugaddr) callback which + returns true if the eip is a real kernel address, and it points + to the expected BUG trap instruction. + + Jeremy Fitzhardinge 2006 + */ +#include +#include +#include + +extern const struct bug_entry __start___bug_table[], __stop___bug_table[]; + +#ifdef CONFIG_MODULES +static LIST_HEAD(module_bug_list); + +static const struct bug_entry *module_find_bug(unsigned long bugaddr) +{ + struct module *mod; + + list_for_each_entry(mod, &module_bug_list, bug_list) { + const struct bug_entry *bug = mod->bug_table; + unsigned i; + + for (i = 0; i < mod->num_bugs; ++i, ++bug) + if (bugaddr == bug->bug_addr) + return bug; + } + return NULL; +} + +int module_bug_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs, + struct module *mod) +{ + char *secstrings; + unsigned int i; + + mod->bug_table = NULL; + mod->num_bugs = 0; + + /* Find the __bug_table section, if present */ + secstrings = (char *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; + for (i = 1; i < hdr->e_shnum; i++) { + if (strcmp(secstrings+sechdrs[i].sh_name, "__bug_table")) + continue; + mod->bug_table = (void *) sechdrs[i].sh_addr; + mod->num_bugs = sechdrs[i].sh_size / sizeof(struct bug_entry); + break; + } + + /* + * Strictly speaking this should have a spinlock to protect against + * traversals, but since we only traverse on BUG()s, a spinlock + * could potentially lead to deadlock and thus be counter-productive. + */ + list_add(&mod->bug_list, &module_bug_list); + + return 0; +} + +void module_bug_cleanup(struct module *mod) +{ + list_del(&mod->bug_list); +} + +#else + +static inline const struct bug_entry *module_find_bug(unsigned long bugaddr) +{ + return NULL; +} +#endif + +const struct bug_entry *find_bug(unsigned long bugaddr) +{ + const struct bug_entry *bug; + + for (bug = __start___bug_table; bug < __stop___bug_table; ++bug) + if (bugaddr == bug->bug_addr) + return bug; + + return module_find_bug(bugaddr); +} + +enum bug_trap_type report_bug(unsigned long bugaddr) +{ + const struct bug_entry *bug; + const char *file; + unsigned line, warning; + + if (!is_valid_bugaddr(bugaddr)) + return BUG_TRAP_TYPE_NONE; + + bug = find_bug(bugaddr); + + printk(KERN_EMERG "------------[ cut here ]------------\n"); + + file = NULL; + line = 0; + warning = 0; + + if (bug) { +#ifdef CONFIG_DEBUG_BUGVERBOSE + file = bug->file; + line = bug->line; +#endif + warning = (bug->flags & BUGFLAG_WARNING) != 0; + } + + if (warning) { + /* this is a WARN_ON rather than BUG/BUG_ON */ + if (file) + printk(KERN_ERR "Badness at %s:%u\n", + file, line); + else + printk(KERN_ERR "Badness at %p " + "[verbose debug info unavailable]\n", + (void *)bugaddr); + + dump_stack(); + return BUG_TRAP_TYPE_WARN; + } + + if (file) + printk(KERN_CRIT "kernel BUG at %s:%u!\n", + file, line); + else + printk(KERN_CRIT "Kernel BUG at %p " + "[verbose debug info unavailable]\n", + (void *)bugaddr); + + return BUG_TRAP_TYPE_BUG; +} -- cgit v0.10.2 From 91768d6c2bad0d2766a166f13f2f57e197de3458 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Fri, 8 Dec 2006 02:36:21 -0800 Subject: [PATCH] Generic BUG for i386 This makes i386 use the generic BUG machinery. There are no functional changes from the old i386 implementation. The main advantage in using the generic BUG machinery for i386 is that the inlined overhead of BUG is just the ud2a instruction; the file+line(+function) information are no longer inlined into the instruction stream. This reduces cache pollution, and makes disassembly work properly. Signed-off-by: Jeremy Fitzhardinge Cc: Andi Kleen Cc: Hugh Dickens Cc: Michael Ellerman Cc: Paul Mackerras Cc: Benjamin Herrenschmidt Cc: Rusty Russell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/arch/i386/Kconfig b/arch/i386/Kconfig index ea70359..c2362c7 100644 --- a/arch/i386/Kconfig +++ b/arch/i386/Kconfig @@ -49,6 +49,11 @@ config GENERIC_IOMAP bool default y +config GENERIC_BUG + bool + default y + depends on BUG + config GENERIC_HWEIGHT bool default y diff --git a/arch/i386/kernel/module.c b/arch/i386/kernel/module.c index d7d9c8b2..3db0a544 100644 --- a/arch/i386/kernel/module.c +++ b/arch/i386/kernel/module.c @@ -21,6 +21,7 @@ #include #include #include +#include #if 0 #define DEBUGP printk @@ -141,10 +142,11 @@ int module_finalize(const Elf_Ehdr *hdr, apply_paravirt(pseg, pseg + para->sh_size); } - return 0; + return module_bug_finalize(hdr, sechdrs, me); } void module_arch_cleanup(struct module *mod) { alternatives_smp_module_del(mod); + module_bug_cleanup(mod); } diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c index 68de48e..2b30dbf 100644 --- a/arch/i386/kernel/traps.c +++ b/arch/i386/kernel/traps.c @@ -30,6 +30,7 @@ #include #include #include +#include #ifdef CONFIG_EISA #include @@ -420,43 +421,22 @@ void show_registers(struct pt_regs *regs) printk("\n"); } -static void handle_BUG(struct pt_regs *regs) +int is_valid_bugaddr(unsigned long eip) { - unsigned long eip = regs->eip; unsigned short ud2; if (eip < PAGE_OFFSET) - return; + return 0; if (probe_kernel_address((unsigned short *)eip, ud2)) - return; - if (ud2 != 0x0b0f) - return; - - printk(KERN_EMERG "------------[ cut here ]------------\n"); - -#ifdef CONFIG_DEBUG_BUGVERBOSE - do { - unsigned short line; - char *file; - char c; - - if (probe_kernel_address((unsigned short *)(eip + 2), line)) - break; - if (probe_kernel_address((char **)(eip + 4), file) || - (unsigned long)file < PAGE_OFFSET || - probe_kernel_address(file, c)) - file = ""; + return 0; - printk(KERN_EMERG "kernel BUG at %s:%d!\n", file, line); - return; - } while (0); -#endif - printk(KERN_EMERG "Kernel BUG at [verbose debug info unavailable]\n"); + return ud2 == 0x0b0f; } -/* This is gone through when something in the kernel - * has done something bad and is about to be terminated. -*/ +/* + * This is gone through when something in the kernel has done something bad and + * is about to be terminated. + */ void die(const char * str, struct pt_regs * regs, long err) { static struct { @@ -488,7 +468,8 @@ void die(const char * str, struct pt_regs * regs, long err) unsigned long esp; unsigned short ss; - handle_BUG(regs); + report_bug(regs->eip); + printk(KERN_EMERG "%s: %04lx [#%d]\n", str, err & 0xffff, ++die_counter); #ifdef CONFIG_PREEMPT printk(KERN_EMERG "PREEMPT "); diff --git a/arch/i386/kernel/vmlinux.lds.S b/arch/i386/kernel/vmlinux.lds.S index 56e6ad5c..16d3c71 100644 --- a/arch/i386/kernel/vmlinux.lds.S +++ b/arch/i386/kernel/vmlinux.lds.S @@ -57,6 +57,8 @@ SECTIONS RODATA + BUG_TABLE + . = ALIGN(4); .tracedata : AT(ADDR(.tracedata) - LOAD_OFFSET) { __tracedata_start = .; diff --git a/include/asm-i386/bug.h b/include/asm-i386/bug.h index 8062cdb..b0fd78c 100644 --- a/include/asm-i386/bug.h +++ b/include/asm-i386/bug.h @@ -4,20 +4,32 @@ /* * Tell the user there is some problem. - * The offending file and line are encoded after the "officially - * undefined" opcode for parsing in the trap handler. + * The offending file and line are encoded encoded in the __bug_table section. */ #ifdef CONFIG_BUG #define HAVE_ARCH_BUG + #ifdef CONFIG_DEBUG_BUGVERBOSE -#define BUG() \ - __asm__ __volatile__( "ud2\n" \ - "\t.word %c0\n" \ - "\t.long %c1\n" \ - : : "i" (__LINE__), "i" (__FILE__)) +#define BUG() \ + do { \ + asm volatile("1:\tud2\n" \ + ".pushsection __bug_table,\"a\"\n" \ + "2:\t.long 1b, %c0\n" \ + "\t.word %c1, 0\n" \ + "\t.org 2b+%c2\n" \ + ".popsection" \ + : : "i" (__FILE__), "i" (__LINE__), \ + "i" (sizeof(struct bug_entry))); \ + for(;;) ; \ + } while(0) + #else -#define BUG() __asm__ __volatile__("ud2\n") +#define BUG() \ + do { \ + asm volatile("ud2"); \ + for(;;) ; \ + } while(0) #endif #endif diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index ee46fb3..2c133c0 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -285,7 +285,7 @@ config DEBUG_HIGHMEM config DEBUG_BUGVERBOSE bool "Verbose BUG() reporting (adds 70K)" if DEBUG_KERNEL && EMBEDDED depends on BUG - depends on ARM || ARM26 || AVR32 || M32R || M68K || SPARC32 || SPARC64 || X86_32 || FRV || SUPERH || GENERIC_BUG + depends on ARM || ARM26 || AVR32 || M32R || M68K || SPARC32 || SPARC64 || FRV || SUPERH || GENERIC_BUG default !EMBEDDED help Say Y here to make BUG() panics output the file name and line number -- cgit v0.10.2 From c31a0bf3e1bc581676618db7492f18798fd0a73f Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Fri, 8 Dec 2006 02:36:22 -0800 Subject: [PATCH] Generic BUG for x86-64 This makes x86-64 use the generic BUG machinery. The main advantage in using the generic BUG machinery for x86-64 is that the inlined overhead of BUG is just the ud2a instruction; the file+line information are no longer inlined into the instruction stream. This reduces cache pollution. Signed-off-by: Jeremy Fitzhardinge Cc: Andi Kleen Cc: Hugh Dickens Cc: Michael Ellerman Cc: Paul Mackerras Cc: Benjamin Herrenschmidt Cc: Rusty Russell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/arch/x86_64/Kconfig b/arch/x86_64/Kconfig index bfbb9bc..3254a61 100644 --- a/arch/x86_64/Kconfig +++ b/arch/x86_64/Kconfig @@ -96,6 +96,11 @@ config AUDIT_ARCH bool default y +config GENERIC_BUG + bool + default y + depends on BUG + source "init/Kconfig" diff --git a/arch/x86_64/kernel/module.c b/arch/x86_64/kernel/module.c index 9d0958f..a888e67 100644 --- a/arch/x86_64/kernel/module.c +++ b/arch/x86_64/kernel/module.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -173,10 +174,12 @@ int module_finalize(const Elf_Ehdr *hdr, lseg, lseg + locks->sh_size, tseg, tseg + text->sh_size); } - return 0; + + return module_bug_finalize(hdr, sechdrs, me); } void module_arch_cleanup(struct module *mod) { alternatives_smp_module_del(mod); + module_bug_cleanup(mod); } diff --git a/arch/x86_64/kernel/traps.c b/arch/x86_64/kernel/traps.c index a1641ff..b54ccc0 100644 --- a/arch/x86_64/kernel/traps.c +++ b/arch/x86_64/kernel/traps.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -524,30 +525,15 @@ bad: printk("\n"); } -void handle_BUG(struct pt_regs *regs) -{ - struct bug_frame f; - long len; - const char *prefix = ""; +int is_valid_bugaddr(unsigned long rip) +{ + unsigned short ud2; - if (user_mode(regs)) - return; - if (__copy_from_user(&f, (const void __user *) regs->rip, - sizeof(struct bug_frame))) - return; - if (f.filename >= 0 || - f.ud2[0] != 0x0f || f.ud2[1] != 0x0b) - return; - len = __strnlen_user((char *)(long)f.filename, PATH_MAX) - 1; - if (len < 0 || len >= PATH_MAX) - f.filename = (int)(long)"unmapped filename"; - else if (len > 50) { - f.filename += len - 50; - prefix = "..."; - } - printk("----------- [cut here ] --------- [please bite here ] ---------\n"); - printk(KERN_ALERT "Kernel BUG at %s%.50s:%d\n", prefix, (char *)(long)f.filename, f.line); -} + if (__copy_from_user(&ud2, (const void __user *) rip, sizeof(ud2))) + return 0; + + return ud2 == 0x0b0f; +} #ifdef CONFIG_BUG void out_of_line_bug(void) @@ -627,7 +613,9 @@ void die(const char * str, struct pt_regs * regs, long err) { unsigned long flags = oops_begin(); - handle_BUG(regs); + if (!user_mode(regs)) + report_bug(regs->rip); + __die(str, regs, err); oops_end(flags); do_exit(SIGSEGV); diff --git a/arch/x86_64/kernel/vmlinux.lds.S b/arch/x86_64/kernel/vmlinux.lds.S index 6a1f8f4..6c417788 100644 --- a/arch/x86_64/kernel/vmlinux.lds.S +++ b/arch/x86_64/kernel/vmlinux.lds.S @@ -51,6 +51,8 @@ SECTIONS RODATA + BUG_TABLE + . = ALIGN(PAGE_SIZE); /* Align data segment to page size boundary */ /* Data */ .data : AT(ADDR(.data) - LOAD_OFFSET) { diff --git a/include/asm-x86_64/bug.h b/include/asm-x86_64/bug.h index 80ac1fe..6826064 100644 --- a/include/asm-x86_64/bug.h +++ b/include/asm-x86_64/bug.h @@ -1,30 +1,30 @@ #ifndef __ASM_X8664_BUG_H #define __ASM_X8664_BUG_H 1 -#include - -/* - * Tell the user there is some problem. The exception handler decodes - * this frame. - */ -struct bug_frame { - unsigned char ud2[2]; - unsigned char push; - signed int filename; - unsigned char ret; - unsigned short line; -} __attribute__((packed)); - #ifdef CONFIG_BUG #define HAVE_ARCH_BUG -/* We turn the bug frame into valid instructions to not confuse - the disassembler. Thanks to Jan Beulich & Suresh Siddha - for nice instruction selection. - The magic numbers generate mov $64bitimm,%eax ; ret $offset. */ -#define BUG() \ - asm volatile( \ - "ud2 ; pushq $%c1 ; ret $%c0" :: \ - "i"(__LINE__), "i" (__FILE__)) + +#ifdef CONFIG_DEBUG_BUGVERBOSE +#define BUG() \ + do { \ + asm volatile("1:\tud2\n" \ + ".pushsection __bug_table,\"a\"\n" \ + "2:\t.quad 1b, %c0\n" \ + "\t.word %c1, 0\n" \ + "\t.org 2b+%c2\n" \ + ".popsection" \ + : : "i" (__FILE__), "i" (__LINE__), \ + "i" (sizeof(struct bug_entry))); \ + for(;;) ; \ + } while(0) +#else +#define BUG() \ + do { \ + asm volatile("ud2"); \ + for(;;) ; \ + } while(0) +#endif + void out_of_line_bug(void); #else static inline void out_of_line_bug(void) { } -- cgit v0.10.2 From e182c965b6ce0dffed0967a1be2173825a2ede2f Mon Sep 17 00:00:00 2001 From: Jeff Dike Date: Fri, 8 Dec 2006 02:36:23 -0800 Subject: [PATCH] UML: add generic BUG support The BUG changes in -mm3 need some arch support. This patch adds the UML support needed. For the most part, it was stolen from the underlying architecture. The exception is the kernel eip < PAGE_OFFSET test, which is wrong for skas mode UMLs. Signed-off-by: Jeff Dike Cc: Jeremy Fitzhardinge Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/arch/um/Kconfig b/arch/um/Kconfig index 5ac1f29..d32a80e 100644 --- a/arch/um/Kconfig +++ b/arch/um/Kconfig @@ -47,6 +47,11 @@ config GENERIC_CALIBRATE_DELAY bool default y +config GENERIC_BUG + bool + default y + depends on BUG + # Used in kernel/irq/manage.c and include/linux/irq.h config IRQ_RELEASE_METHOD bool diff --git a/arch/um/sys-i386/Makefile b/arch/um/sys-i386/Makefile index 0e32adf..098720b 100644 --- a/arch/um/sys-i386/Makefile +++ b/arch/um/sys-i386/Makefile @@ -1,4 +1,4 @@ -obj-y = bugs.o checksum.o delay.o fault.o ksyms.o ldt.o ptrace.o \ +obj-y = bug.o bugs.o checksum.o delay.o fault.o ksyms.o ldt.o ptrace.o \ ptrace_user.o setjmp.o signal.o sigcontext.o syscalls.o sysrq.o \ sys_call_table.o tls.o diff --git a/arch/um/sys-i386/bug.c b/arch/um/sys-i386/bug.c new file mode 100644 index 0000000..200c8ba --- /dev/null +++ b/arch/um/sys-i386/bug.c @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2006 Jeff Dike (jdike@addtoit.com) + * Licensed under the GPL V2 + */ + +#include + +/* Mostly copied from i386/x86_86 - eliminated the eip < PAGE_OFFSET because + * that's not relevent in skas mode. + */ + +int is_valid_bugaddr(unsigned long eip) +{ + unsigned short ud2; + + if (probe_kernel_address((unsigned short __user *)eip, ud2)) + return 0; + + return ud2 == 0x0b0f; +} diff --git a/arch/um/sys-x86_64/Makefile b/arch/um/sys-x86_64/Makefile index f41768b..4d9e5ef 100644 --- a/arch/um/sys-x86_64/Makefile +++ b/arch/um/sys-x86_64/Makefile @@ -4,7 +4,7 @@ # Licensed under the GPL # -obj-y = bugs.o delay.o fault.o ldt.o mem.o ptrace.o ptrace_user.o \ +obj-y = bug.o bugs.o delay.o fault.o ldt.o mem.o ptrace.o ptrace_user.o \ setjmp.o sigcontext.o signal.o syscalls.o syscall_table.o sysrq.o \ ksyms.o tls.o diff --git a/arch/um/sys-x86_64/bug.c b/arch/um/sys-x86_64/bug.c new file mode 100644 index 0000000..200c8ba --- /dev/null +++ b/arch/um/sys-x86_64/bug.c @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2006 Jeff Dike (jdike@addtoit.com) + * Licensed under the GPL V2 + */ + +#include + +/* Mostly copied from i386/x86_86 - eliminated the eip < PAGE_OFFSET because + * that's not relevent in skas mode. + */ + +int is_valid_bugaddr(unsigned long eip) +{ + unsigned short ud2; + + if (probe_kernel_address((unsigned short __user *)eip, ud2)) + return 0; + + return ud2 == 0x0b0f; +} diff --git a/include/asm-um/bug.h b/include/asm-um/bug.h index 1e22fa2..3357c5e 100644 --- a/include/asm-um/bug.h +++ b/include/asm-um/bug.h @@ -1,4 +1,6 @@ #ifndef __UM_BUG_H #define __UM_BUG_H -#include + +#include + #endif -- cgit v0.10.2 From a8605aef813f8eb3ef4b80d32cba6a671ef8deb0 Mon Sep 17 00:00:00 2001 From: Judith Lebzelter Date: Fri, 8 Dec 2006 02:36:23 -0800 Subject: [PATCH] use generic BUG for ppc Switch ppc over to using the generic BUG implementation. Signed-off-by: Judith Lebzelter Cc: Jeremy Fitzhardinge Cc: Michael Ellerman Cc: Paul Mackerras Cc: Benjamin Herrenschmidt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/arch/ppc/Kconfig b/arch/ppc/Kconfig index edf71a4..f76a146 100644 --- a/arch/ppc/Kconfig +++ b/arch/ppc/Kconfig @@ -52,6 +52,11 @@ config ARCH_MAY_HAVE_PC_FDC bool default y +config GENERIC_BUG + bool + default y + depends on BUG + source "init/Kconfig" menu "Processor" diff --git a/arch/ppc/kernel/traps.c b/arch/ppc/kernel/traps.c index 2f835b9..810f7aa 100644 --- a/arch/ppc/kernel/traps.c +++ b/arch/ppc/kernel/traps.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -559,64 +560,9 @@ static void emulate_single_step(struct pt_regs *regs) } } -/* - * Look through the list of trap instructions that are used for BUG(), - * BUG_ON() and WARN_ON() and see if we hit one. At this point we know - * that the exception was caused by a trap instruction of some kind. - * Returns 1 if we should continue (i.e. it was a WARN_ON) or 0 - * otherwise. - */ -extern struct bug_entry __start___bug_table[], __stop___bug_table[]; - -#ifndef CONFIG_MODULES -#define module_find_bug(x) NULL -#endif - -struct bug_entry *find_bug(unsigned long bugaddr) -{ - struct bug_entry *bug; - - for (bug = __start___bug_table; bug < __stop___bug_table; ++bug) - if (bugaddr == bug->bug_addr) - return bug; - return module_find_bug(bugaddr); -} - -int check_bug_trap(struct pt_regs *regs) +int is_valid_bugaddr(unsigned long addr) { - struct bug_entry *bug; - unsigned long addr; - - if (regs->msr & MSR_PR) - return 0; /* not in kernel */ - addr = regs->nip; /* address of trap instruction */ - if (addr < PAGE_OFFSET) - return 0; - bug = find_bug(regs->nip); - if (bug == NULL) - return 0; - if (bug->line & BUG_WARNING_TRAP) { - /* this is a WARN_ON rather than BUG/BUG_ON */ -#ifdef CONFIG_XMON - xmon_printf(KERN_ERR "Badness in %s at %s:%ld\n", - bug->function, bug->file, - bug->line & ~BUG_WARNING_TRAP); -#endif /* CONFIG_XMON */ - printk(KERN_ERR "Badness in %s at %s:%ld\n", - bug->function, bug->file, - bug->line & ~BUG_WARNING_TRAP); - dump_stack(); - return 1; - } -#ifdef CONFIG_XMON - xmon_printf(KERN_CRIT "kernel BUG in %s at %s:%ld!\n", - bug->function, bug->file, bug->line); - xmon(regs); -#endif /* CONFIG_XMON */ - printk(KERN_CRIT "kernel BUG in %s at %s:%ld!\n", - bug->function, bug->file, bug->line); - - return 0; + return addr >= PAGE_OFFSET; } void program_check_exception(struct pt_regs *regs) @@ -671,7 +617,9 @@ void program_check_exception(struct pt_regs *regs) /* trap exception */ if (debugger_bpt(regs)) return; - if (check_bug_trap(regs)) { + + if (!(regs->msr & MSR_PR) && /* not user-mode */ + report_bug(regs->nip) == BUG_TRAP_TYPE_WARN) { regs->nip += 4; return; } -- cgit v0.10.2 From 30e25b71e725b150585e17888b130e3324f8cf7c Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Fri, 8 Dec 2006 02:36:24 -0800 Subject: [PATCH] Fix generic WARN_ON message A warning is a warning, not a BUG. Signed-off-by: Jeremy Fitzhardinge Cc: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/include/asm-generic/bug.h b/include/asm-generic/bug.h index 47e3561..a06eecd 100644 --- a/include/asm-generic/bug.h +++ b/include/asm-generic/bug.h @@ -35,7 +35,7 @@ struct bug_entry { #define WARN_ON(condition) ({ \ typeof(condition) __ret_warn_on = (condition); \ if (unlikely(__ret_warn_on)) { \ - printk("BUG: warning at %s:%d/%s()\n", __FILE__, \ + printk("WARNING at %s:%d %s()\n", __FILE__, \ __LINE__, __FUNCTION__); \ dump_stack(); \ } \ -- cgit v0.10.2 From a5cfc1ec58a07074dacb6aa8c79eff864c966d12 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Fri, 8 Dec 2006 02:36:25 -0800 Subject: [PATCH] bit reverse library This patch provides two bit reverse functions and bit reverse table. - reverse the order of bits in a u32 value u8 bitrev8(u8 x); - reverse the order of bits in a u32 value u32 bitrev32(u32 x); - byte reverse table const u8 byte_rev_table[256]; Signed-off-by: Akinobu Mita Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/include/linux/bitrev.h b/include/linux/bitrev.h new file mode 100644 index 0000000..05e540d --- /dev/null +++ b/include/linux/bitrev.h @@ -0,0 +1,15 @@ +#ifndef _LINUX_BITREV_H +#define _LINUX_BITREV_H + +#include + +extern u8 const byte_rev_table[256]; + +static inline u8 bitrev8(u8 byte) +{ + return byte_rev_table[byte]; +} + +extern u32 bitrev32(u32 in); + +#endif /* _LINUX_BITREV_H */ diff --git a/lib/Kconfig b/lib/Kconfig index 734ce95..0d68355 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -4,6 +4,9 @@ menu "Library routines" +config BITREVERSE + tristate + config CRC_CCITT tristate "CRC-CCITT functions" help diff --git a/lib/Makefile b/lib/Makefile index 7ab486b..6f675c6 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -35,6 +35,7 @@ ifneq ($(CONFIG_HAVE_DEC_LOCK),y) lib-y += dec_and_lock.o endif +obj-$(CONFIG_BITREVERSE) += bitrev.o obj-$(CONFIG_CRC_CCITT) += crc-ccitt.o obj-$(CONFIG_CRC16) += crc16.o obj-$(CONFIG_CRC32) += crc32.o diff --git a/lib/bitrev.c b/lib/bitrev.c new file mode 100644 index 0000000..f4e1c49 --- /dev/null +++ b/lib/bitrev.c @@ -0,0 +1,54 @@ +#include +#include +#include + +const u8 byte_rev_table[256] = { + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, + 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, + 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, + 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, + 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, + 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, + 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, + 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, + 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, + 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, + 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, + 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, + 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, + 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, + 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, + 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, + 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, + 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, + 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, + 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, + 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, + 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, + 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, + 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, + 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, + 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, + 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, + 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, + 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, + 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, + 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff, +}; +EXPORT_SYMBOL_GPL(byte_rev_table); + +static __always_inline u16 bitrev16(u16 x) +{ + return (bitrev8(x & 0xff) << 8) | bitrev8(x >> 8); +} + +/** + * bitrev32 - reverse the order of bits in a u32 value + * @x: value to be bit-reversed + */ +u32 bitrev32(u32 x) +{ + return (bitrev16(x & 0xffff) << 16) | bitrev16(x >> 16); +} +EXPORT_SYMBOL(bitrev32); -- cgit v0.10.2 From 906d66df18faa4aac8d898ae6920d1014694a932 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Fri, 8 Dec 2006 02:36:25 -0800 Subject: [PATCH] crc32: replace bitreverse by bitrev32 This patch replaces bitreverse() by bitrev32. The only users of bitreverse() are crc32 itself and via-velocity. Cc: Jeff Garzik Cc: Matt Domsch Signed-off-by: Akinobu Mita Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/drivers/net/via-velocity.c b/drivers/net/via-velocity.c index 74f8947..4587f23 100644 --- a/drivers/net/via-velocity.c +++ b/drivers/net/via-velocity.c @@ -3132,7 +3132,7 @@ static u16 wol_calc_crc(int size, u8 * pattern, u8 *mask_pattern) } /* Finally, invert the result once to get the correct data */ crc = ~crc; - return bitreverse(crc) >> 16; + return bitrev32(crc) >> 16; } /** diff --git a/include/linux/crc32.h b/include/linux/crc32.h index 56c0645..e20dd1f 100644 --- a/include/linux/crc32.h +++ b/include/linux/crc32.h @@ -6,10 +6,10 @@ #define _LINUX_CRC32_H #include +#include extern u32 crc32_le(u32 crc, unsigned char const *p, size_t len); extern u32 crc32_be(u32 crc, unsigned char const *p, size_t len); -extern u32 bitreverse(u32 in); #define crc32(seed, data, length) crc32_le(seed, (unsigned char const *)data, length) @@ -21,7 +21,7 @@ extern u32 bitreverse(u32 in); * is in bit nr 0], thus it must be reversed before use. Except for * nics that bit swap the result internally... */ -#define ether_crc(length, data) bitreverse(crc32_le(~0, data, length)) +#define ether_crc(length, data) bitrev32(crc32_le(~0, data, length)) #define ether_crc_le(length, data) crc32_le(~0, data, length) #endif /* _LINUX_CRC32_H */ diff --git a/lib/Kconfig b/lib/Kconfig index 0d68355..47b172d 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -26,6 +26,7 @@ config CRC16 config CRC32 tristate "CRC32 functions" default y + select BITREVERSE help This option is provided for the case where no in-kernel-tree modules require CRC32 functions, but a module built outside the diff --git a/lib/crc32.c b/lib/crc32.c index 285fd9b..bfc3331 100644 --- a/lib/crc32.c +++ b/lib/crc32.c @@ -235,23 +235,8 @@ u32 __attribute_pure__ crc32_be(u32 crc, unsigned char const *p, size_t len) } #endif -/** - * bitreverse - reverse the order of bits in a u32 value - * @x: value to be bit-reversed - */ -u32 bitreverse(u32 x) -{ - x = (x >> 16) | (x << 16); - x = (x >> 8 & 0x00ff00ff) | (x << 8 & 0xff00ff00); - x = (x >> 4 & 0x0f0f0f0f) | (x << 4 & 0xf0f0f0f0); - x = (x >> 2 & 0x33333333) | (x << 2 & 0xcccccccc); - x = (x >> 1 & 0x55555555) | (x << 1 & 0xaaaaaaaa); - return x; -} - EXPORT_SYMBOL(crc32_le); EXPORT_SYMBOL(crc32_be); -EXPORT_SYMBOL(bitreverse); /* * A brief CRC tutorial. @@ -400,10 +385,7 @@ buf_dump(char const *prefix, unsigned char const *buf, size_t len) static void bytereverse(unsigned char *buf, size_t len) { while (len--) { - unsigned char x = *buf; - x = (x >> 4) | (x << 4); - x = (x >> 2 & 0x33) | (x << 2 & 0xcc); - x = (x >> 1 & 0x55) | (x << 1 & 0xaa); + unsigned char x = bitrev8(*buf); *buf++ = x; } } @@ -460,11 +442,11 @@ static u32 test_step(u32 init, unsigned char *buf, size_t len) /* Now swap it around for the other test */ bytereverse(buf, len + 4); - init = bitreverse(init); - crc2 = bitreverse(crc1); - if (crc1 != bitreverse(crc2)) + init = bitrev32(init); + crc2 = bitrev32(crc1); + if (crc1 != bitrev32(crc2)) printf("\nBit reversal fail: 0x%08x -> 0x%08x -> 0x%08x\n", - crc1, crc2, bitreverse(crc2)); + crc1, crc2, bitrev32(crc2)); crc1 = crc32_le(init, buf, len); if (crc1 != crc2) printf("\nCRC endianness fail: 0x%08x != 0x%08x\n", crc1, -- cgit v0.10.2 From 1c6676825fe4cc03a71f253fc3e16ec17c6a3195 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Fri, 8 Dec 2006 02:36:26 -0800 Subject: [PATCH] video: use bitrev8 Use bitrev8 for nvidiafb, rivafb, and tgafb drivers Cc: Antonino Daplas Signed-off-by: Akinobu Mita Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 7a43020..c69b6ef 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -541,6 +541,7 @@ config FB_TGA select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT + select BITREVERSE help This is the frame buffer device driver for generic TGA graphic cards. Say Y if you have one of those. @@ -705,6 +706,7 @@ config FB_NVIDIA select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT + select BITREVERSE help This driver supports graphics boards with the nVidia chips, TNT and newer. For very old chipsets, such as the RIVA128, then use @@ -744,6 +746,7 @@ config FB_RIVA select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT + select BITREVERSE help This driver supports graphics boards with the nVidia Riva/Geforce chips. diff --git a/drivers/video/nvidia/nv_accel.c b/drivers/video/nvidia/nv_accel.c index 4aefb8f..9efb8a3 100644 --- a/drivers/video/nvidia/nv_accel.c +++ b/drivers/video/nvidia/nv_accel.c @@ -261,41 +261,6 @@ void NVResetGraphics(struct fb_info *info) NVDmaKickoff(par); } -u8 byte_rev[256] = { - 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, - 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, - 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, - 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, - 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, - 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, - 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, - 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, - 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, - 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, - 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, - 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, - 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, - 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, - 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, - 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, - 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, - 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, - 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, - 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, - 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, - 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, - 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, - 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, - 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, - 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, - 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, - 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, - 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, - 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, - 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, - 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff, -}; - int nvidiafb_sync(struct fb_info *info) { struct nvidia_par *par = info->par; diff --git a/drivers/video/nvidia/nv_local.h b/drivers/video/nvidia/nv_local.h index 4243d7f..e009d24 100644 --- a/drivers/video/nvidia/nv_local.h +++ b/drivers/video/nvidia/nv_local.h @@ -96,13 +96,16 @@ #define READ_GET(par) (NV_RD32(&(par)->FIFO[0x0011], 0) >> 2) #ifdef __LITTLE_ENDIAN + +#include + #define reverse_order(l) \ do { \ u8 *a = (u8 *)(l); \ - *a = byte_rev[*a], a++; \ - *a = byte_rev[*a], a++; \ - *a = byte_rev[*a], a++; \ - *a = byte_rev[*a]; \ + a[0] = bitrev8(a[0]); \ + a[1] = bitrev8(a[1]); \ + a[2] = bitrev8(a[2]); \ + a[3] = bitrev8(a[3]); \ } while(0) #else #define reverse_order(l) do { } while(0) diff --git a/drivers/video/nvidia/nv_proto.h b/drivers/video/nvidia/nv_proto.h index 8612710..43058d0 100644 --- a/drivers/video/nvidia/nv_proto.h +++ b/drivers/video/nvidia/nv_proto.h @@ -62,7 +62,6 @@ extern void nvidiafb_fillrect(struct fb_info *info, extern void nvidiafb_imageblit(struct fb_info *info, const struct fb_image *image); extern int nvidiafb_sync(struct fb_info *info); -extern u8 byte_rev[256]; /* in nv_backlight.h */ #ifdef CONFIG_FB_NVIDIA_BACKLIGHT diff --git a/drivers/video/riva/fbdev.c b/drivers/video/riva/fbdev.c index a433cc7..c6f8685 100644 --- a/drivers/video/riva/fbdev.c +++ b/drivers/video/riva/fbdev.c @@ -40,6 +40,7 @@ #include #include #include +#include #ifdef CONFIG_MTRR #include #endif @@ -521,48 +522,13 @@ static inline unsigned char MISCin(struct riva_par *par) return (VGA_RD08(par->riva.PVIO, 0x3cc)); } -static u8 byte_rev[256] = { - 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, - 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, - 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, - 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, - 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, - 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, - 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, - 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, - 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, - 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, - 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, - 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, - 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, - 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, - 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, - 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, - 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, - 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, - 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, - 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, - 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, - 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, - 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, - 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, - 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, - 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, - 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, - 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, - 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, - 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, - 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, - 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff, -}; - static inline void reverse_order(u32 *l) { u8 *a = (u8 *)l; - *a = byte_rev[*a], a++; - *a = byte_rev[*a], a++; - *a = byte_rev[*a], a++; - *a = byte_rev[*a]; + a[0] = bitrev8(a[0]); + a[1] = bitrev8(a[1]); + a[2] = bitrev8(a[2]); + a[3] = bitrev8(a[3]); } /* ------------------------------------------------------------------------- * diff --git a/drivers/video/tgafb.c b/drivers/video/tgafb.c index 94fde62..08b3153 100644 --- a/drivers/video/tgafb.c +++ b/drivers/video/tgafb.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include