diff options
Diffstat (limited to 'drivers')
214 files changed, 7271 insertions, 3168 deletions
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 27c8d56111c..29e71bddd6f 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -679,24 +679,20 @@ static void ahci_save_initial_config(struct pci_dev *pdev, /* cross check port_map and cap.n_ports */ if (port_map) { - u32 tmp_port_map = port_map; - int n_ports = ahci_nr_ports(cap); + int map_ports = 0; - for (i = 0; i < AHCI_MAX_PORTS && n_ports; i++) { - if (tmp_port_map & (1 << i)) { - n_ports--; - tmp_port_map &= ~(1 << i); - } - } + for (i = 0; i < AHCI_MAX_PORTS; i++) + if (port_map & (1 << i)) + map_ports++; - /* If n_ports and port_map are inconsistent, whine and - * clear port_map and let it be generated from n_ports. + /* If PI has more ports than n_ports, whine, clear + * port_map and let it be generated from n_ports. */ - if (n_ports || tmp_port_map) { + if (map_ports > ahci_nr_ports(cap)) { dev_printk(KERN_WARNING, &pdev->dev, - "nr_ports (%u) and implemented port map " - "(0x%x) don't match, using nr_ports\n", - ahci_nr_ports(cap), port_map); + "implemented port map (0x%x) contains more " + "ports than nr_ports (%u), using nr_ports\n", + port_map, ahci_nr_ports(cap)); port_map = 0; } } @@ -2201,7 +2197,7 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) struct device *dev = &pdev->dev; struct ahci_host_priv *hpriv; struct ata_host *host; - int i, rc; + int n_ports, i, rc; VPRINTK("ENTER\n"); @@ -2255,7 +2251,14 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (hpriv->cap & HOST_CAP_PMP) pi.flags |= ATA_FLAG_PMP; - host = ata_host_alloc_pinfo(&pdev->dev, ppi, fls(hpriv->port_map)); + /* CAP.NP sometimes indicate the index of the last enabled + * port, at other times, that of the last possible port, so + * determining the maximum port number requires looking at + * both CAP.NP and port_map. + */ + n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map)); + + host = ata_host_alloc_pinfo(&pdev->dev, ppi, n_ports); if (!host) return -ENOMEM; host->iomap = pcim_iomap_table(pdev); diff --git a/drivers/ata/ata_piix.c b/drivers/ata/ata_piix.c index 4b99ed0c59b..9c2515f67de 100644 --- a/drivers/ata/ata_piix.c +++ b/drivers/ata/ata_piix.c @@ -1603,7 +1603,8 @@ static void piix_iocfg_bit18_quirk(struct pci_dev *pdev) * Zero on success, or -ERRNO value. */ -static int piix_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) +static int __devinit piix_init_one(struct pci_dev *pdev, + const struct pci_device_id *ent) { static int printed_version; struct device *dev = &pdev->dev; diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 361cf50cbde..3011919f3ec 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -4154,8 +4154,6 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = { /* NCQ is broken */ { "Maxtor *", "BANC*", ATA_HORKAGE_NONCQ }, { "Maxtor 7V300F0", "VA111630", ATA_HORKAGE_NONCQ }, - { "HITACHI HDS7250SASUN500G*", NULL, ATA_HORKAGE_NONCQ }, - { "HITACHI HDS7225SBSUN250G*", NULL, ATA_HORKAGE_NONCQ }, { "ST380817AS", "3.42", ATA_HORKAGE_NONCQ }, { "ST3160023AS", "3.42", ATA_HORKAGE_NONCQ }, diff --git a/drivers/ata/pata_of_platform.c b/drivers/ata/pata_of_platform.c index 938f48a807e..408da30594c 100644 --- a/drivers/ata/pata_of_platform.c +++ b/drivers/ata/pata_of_platform.c @@ -12,7 +12,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/of_platform.h> -#include <linux/pata_platform.h> +#include <linux/ata_platform.h> static int __devinit pata_of_platform_probe(struct of_device *ofdev, const struct of_device_id *match) diff --git a/drivers/ata/pata_platform.c b/drivers/ata/pata_platform.c index 224bb6c2030..aad7adc6ea5 100644 --- a/drivers/ata/pata_platform.c +++ b/drivers/ata/pata_platform.c @@ -19,7 +19,7 @@ #include <linux/ata.h> #include <linux/libata.h> #include <linux/platform_device.h> -#include <linux/pata_platform.h> +#include <linux/ata_platform.h> #define DRV_NAME "pata_platform" #define DRV_VERSION "1.2" diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c index 922d7b2efba..efcb66b6cce 100644 --- a/drivers/ata/sata_fsl.c +++ b/drivers/ata/sata_fsl.c @@ -355,8 +355,8 @@ static unsigned int sata_fsl_fill_sg(struct ata_queued_cmd *qc, void *cmd_desc, ata_port_printk(qc->ap, KERN_ERR, "s/g len unaligned : 0x%x\n", sg_len); - if ((num_prde == (SATA_FSL_MAX_PRD_DIRECT - 1)) && - (qc->n_iter + 1 != qc->n_elem)) { + if (num_prde == (SATA_FSL_MAX_PRD_DIRECT - 1) && + sg_next(sg) != NULL) { VPRINTK("setting indirect prde\n"); prd_ptr_to_indirect_ext = prd; prd->dba = cpu_to_le32(indirect_ext_segment_paddr); diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index 3c1b5c9027d..080b8362f8d 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -69,8 +69,11 @@ #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/interrupt.h> +#include <linux/dmapool.h> #include <linux/dma-mapping.h> #include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/ata_platform.h> #include <scsi/scsi_host.h> #include <scsi/scsi_cmnd.h> #include <scsi/scsi_device.h> @@ -179,6 +182,8 @@ enum { HC_MAIN_IRQ_CAUSE_OFS = 0x1d60, HC_MAIN_IRQ_MASK_OFS = 0x1d64, + HC_SOC_MAIN_IRQ_CAUSE_OFS = 0x20020, + HC_SOC_MAIN_IRQ_MASK_OFS = 0x20024, PORT0_ERR = (1 << 0), /* shift by port # */ PORT0_DONE = (1 << 1), /* shift by port # */ HC0_IRQ_PEND = 0x1ff, /* bits 0-8 = HC0's ports */ @@ -194,11 +199,13 @@ enum { TWSI_INT = (1 << 24), HC_MAIN_RSVD = (0x7f << 25), /* bits 31-25 */ HC_MAIN_RSVD_5 = (0x1fff << 19), /* bits 31-19 */ + HC_MAIN_RSVD_SOC = (0x3fffffb << 6), /* bits 31-9, 7-6 */ HC_MAIN_MASKED_IRQS = (TRAN_LO_DONE | TRAN_HI_DONE | PORTS_0_7_COAL_DONE | GPIO_INT | TWSI_INT | HC_MAIN_RSVD), HC_MAIN_MASKED_IRQS_5 = (PORTS_0_3_COAL_DONE | PORTS_4_7_COAL_DONE | HC_MAIN_RSVD_5), + HC_MAIN_MASKED_IRQS_SOC = (PORTS_0_3_COAL_DONE | HC_MAIN_RSVD_SOC), /* SATAHC registers */ HC_CFG_OFS = 0, @@ -368,6 +375,7 @@ enum chip_type { chip_608x, chip_6042, chip_7042, + chip_soc, }; /* Command ReQuest Block: 32B */ @@ -424,6 +432,10 @@ struct mv_host_priv { u32 hp_flags; struct mv_port_signal signal[8]; const struct mv_hw_ops *ops; + int n_ports; + void __iomem *base; + void __iomem *main_cause_reg_addr; + void __iomem *main_mask_reg_addr; u32 irq_cause_ofs; u32 irq_mask_ofs; u32 unmask_all_irqs; @@ -482,6 +494,15 @@ static void mv6_read_preamp(struct mv_host_priv *hpriv, int idx, static int mv6_reset_hc(struct mv_host_priv *hpriv, void __iomem *mmio, unsigned int n_hc); static void mv6_reset_flash(struct mv_host_priv *hpriv, void __iomem *mmio); +static void mv_soc_enable_leds(struct mv_host_priv *hpriv, + void __iomem *mmio); +static void mv_soc_read_preamp(struct mv_host_priv *hpriv, int idx, + void __iomem *mmio); +static int mv_soc_reset_hc(struct mv_host_priv *hpriv, + void __iomem *mmio, unsigned int n_hc); +static void mv_soc_reset_flash(struct mv_host_priv *hpriv, + void __iomem *mmio); +static void mv_soc_reset_bus(struct ata_host *host, void __iomem *mmio); static void mv_reset_pci_bus(struct ata_host *host, void __iomem *mmio); static void mv_channel_reset(struct mv_host_priv *hpriv, void __iomem *mmio, unsigned int port_no); @@ -661,6 +682,12 @@ static const struct ata_port_info mv_port_info[] = { .udma_mask = ATA_UDMA6, .port_ops = &mv_iie_ops, }, + { /* chip_soc */ + .flags = MV_COMMON_FLAGS | MV_FLAG_SOC, + .pio_mask = 0x1f, /* pio0-4 */ + .udma_mask = ATA_UDMA6, + .port_ops = &mv_iie_ops, + }, }; static const struct pci_device_id mv_pci_tbl[] = { @@ -711,6 +738,15 @@ static const struct mv_hw_ops mv6xxx_ops = { .reset_bus = mv_reset_pci_bus, }; +static const struct mv_hw_ops mv_soc_ops = { + .phy_errata = mv6_phy_errata, + .enable_leds = mv_soc_enable_leds, + .read_preamp = mv_soc_read_preamp, + .reset_hc = mv_soc_reset_hc, + .reset_flash = mv_soc_reset_flash, + .reset_bus = mv_soc_reset_bus, +}; + /* * Functions */ @@ -749,9 +785,15 @@ static inline void __iomem *mv_port_base(void __iomem *base, unsigned int port) (mv_hardport_from_port(port) * MV_PORT_REG_SZ); } +static inline void __iomem *mv_host_base(struct ata_host *host) +{ + struct mv_host_priv *hpriv = host->private_data; + return hpriv->base; +} + static inline void __iomem *mv_ap_base(struct ata_port *ap) { - return mv_port_base(ap->host->iomap[MV_PRIMARY_BAR], ap->port_no); + return mv_port_base(mv_host_base(ap->host), ap->port_no); } static inline int mv_get_hc_count(unsigned long port_flags) @@ -1649,16 +1691,21 @@ static void mv_intr_edma(struct ata_port *ap) */ static void mv_host_intr(struct ata_host *host, u32 relevant, unsigned int hc) { - void __iomem *mmio = host->iomap[MV_PRIMARY_BAR]; + struct mv_host_priv *hpriv = host->private_data; + void __iomem *mmio = hpriv->base; void __iomem *hc_mmio = mv_hc_base(mmio, hc); u32 hc_irq_cause; - int port, port0; + int port, port0, last_port; if (hc == 0) port0 = 0; else port0 = MV_PORTS_PER_HC; + if (HAS_PCI(host)) + last_port = port0 + MV_PORTS_PER_HC; + else + last_port = port0 + hpriv->n_ports; /* we'll need the HC success int register in most cases */ hc_irq_cause = readl(hc_mmio + HC_IRQ_CAUSE_OFS); if (!hc_irq_cause) @@ -1669,7 +1716,7 @@ static void mv_host_intr(struct ata_host *host, u32 relevant, unsigned int hc) VPRINTK("ENTER, hc%u relevant=0x%08x HC IRQ cause=0x%08x\n", hc, relevant, hc_irq_cause); - for (port = port0; port < port0 + MV_PORTS_PER_HC; port++) { + for (port = port0; port < port0 + last_port; port++) { struct ata_port *ap = host->ports[port]; struct mv_port_priv *pp = ap->private_data; int have_err_bits, hard_port, shift; @@ -1764,13 +1811,15 @@ static void mv_pci_error(struct ata_host *host, void __iomem *mmio) static irqreturn_t mv_interrupt(int irq, void *dev_instance) { struct ata_host *host = dev_instance; + struct mv_host_priv *hpriv = host->private_data; unsigned int hc, handled = 0, n_hcs; - void __iomem *mmio = host->iomap[MV_PRIMARY_BAR]; + void __iomem *mmio = hpriv->base; u32 irq_stat, irq_mask; spin_lock(&host->lock); - irq_stat = readl(mmio + HC_MAIN_IRQ_CAUSE_OFS); - irq_mask = readl(mmio + HC_MAIN_IRQ_MASK_OFS); + + irq_stat = readl(hpriv->main_cause_reg_addr); + irq_mask = readl(hpriv->main_mask_reg_addr); /* check the cases where we either have nothing pending or have read * a bogus register value which can indicate HW removal or PCI fault @@ -1827,7 +1876,8 @@ static unsigned int mv5_scr_offset(unsigned int sc_reg_in) static int mv5_scr_read(struct ata_port *ap, unsigned int sc_reg_in, u32 *val) { - void __iomem *mmio = ap->host->iomap[MV_PRIMARY_BAR]; + struct mv_host_priv *hpriv = ap->host->private_data; + void __iomem *mmio = hpriv->base; void __iomem *addr = mv5_phy_base(mmio, ap->port_no); unsigned int ofs = mv5_scr_offset(sc_reg_in); @@ -1840,7 +1890,8 @@ static int mv5_scr_read(struct ata_port *ap, unsigned int sc_reg_in, u32 *val) static int mv5_scr_write(struct ata_port *ap, unsigned int sc_reg_in, u32 val) { - void __iomem *mmio = ap->host->iomap[MV_PRIMARY_BAR]; + struct mv_host_priv *hpriv = ap->host->private_data; + void __iomem *mmio = hpriv->base; void __iomem *addr = mv5_phy_base(mmio, ap->port_no); unsigned int ofs = mv5_scr_offset(sc_reg_in); @@ -2178,6 +2229,93 @@ static void mv6_phy_errata(struct mv_host_priv *hpriv, void __iomem *mmio, writel(m2, port_mmio + PHY_MODE2); } +/* TODO: use the generic LED interface to configure the SATA Presence */ +/* & Acitivy LEDs on the board */ +static void mv_soc_enable_leds(struct mv_host_priv *hpriv, + void __iomem *mmio) +{ + return; +} + +static void mv_soc_read_preamp(struct mv_host_priv *hpriv, int idx, + void __iomem *mmio) +{ + void __iomem *port_mmio; + u32 tmp; + + port_mmio = mv_port_base(mmio, idx); + tmp = readl(port_mmio + PHY_MODE2); + + hpriv->signal[idx].amps = tmp & 0x700; /* bits 10:8 */ + hpriv->signal[idx].pre = tmp & 0xe0; /* bits 7:5 */ +} + +#undef ZERO +#define ZERO(reg) writel(0, port_mmio + (reg)) +static void mv_soc_reset_hc_port(struct mv_host_priv *hpriv, + void __iomem *mmio, unsigned int port) +{ + void __iomem *port_mmio = mv_port_base(mmio, port); + + writelfl(EDMA_DS, port_mmio + EDMA_CMD_OFS); + + mv_channel_reset(hpriv, mmio, port); + + ZERO(0x028); /* command */ + writel(0x101f, port_mmio + EDMA_CFG_OFS); + ZERO(0x004); /* timer */ + ZERO(0x008); /* irq err cause */ + ZERO(0x00c); /* irq err mask */ + ZERO(0x010); /* rq bah */ + ZERO(0x014); /* rq inp */ + ZERO(0x018); /* rq outp */ + ZERO(0x01c); /* respq bah */ + ZERO(0x024); /* respq outp */ + ZERO(0x020); /* respq inp */ + ZERO(0x02c); /* test control */ + writel(0xbc, port_mmio + EDMA_IORDY_TMOUT); +} + +#undef ZERO + +#define ZERO(reg) writel(0, hc_mmio + (reg)) +static void mv_soc_reset_one_hc(struct mv_host_priv *hpriv, + void __iomem *mmio) +{ + void __iomem *hc_mmio = mv_hc_base(mmio, 0); + + ZERO(0x00c); + ZERO(0x010); + ZERO(0x014); + +} + +#undef ZERO + +static int mv_soc_reset_hc(struct mv_host_priv *hpriv, + void __iomem *mmio, unsigned int n_hc) +{ + unsigned int port; + + for (port = 0; port < hpriv->n_ports; port++) + mv_soc_reset_hc_port(hpriv, mmio, port); + + mv_soc_reset_one_hc(hpriv, mmio); + + return 0; +} + +static void mv_soc_reset_flash(struct mv_host_priv *hpriv, + void __iomem *mmio) +{ + return; +} + +static void mv_soc_reset_bus(struct ata_host *host, void __iomem *mmio) +{ + return; +} + static void mv_channel_reset(struct mv_host_priv *hpriv, void __iomem *mmio, unsigned int port_no) { @@ -2342,7 +2480,7 @@ static int mv_hardreset(struct ata_link *link, unsigned int *class, { struct ata_port *ap = link->ap; struct mv_host_priv *hpriv = ap->host->private_data; - void __iomem *mmio = ap->host->iomap[MV_PRIMARY_BAR]; + void __iomem *mmio = hpriv->base; mv_stop_dma(ap); @@ -2383,7 +2521,7 @@ static void mv_error_handler(struct ata_port *ap) static void mv_eh_freeze(struct ata_port *ap) { - void __iomem *mmio = ap->host->iomap[MV_PRIMARY_BAR]; + struct mv_host_priv *hpriv = ap->host->private_data; unsigned int hc = (ap->port_no > 3) ? 1 : 0; u32 tmp, mask; unsigned int shift; @@ -2397,13 +2535,14 @@ static void mv_eh_freeze(struct ata_port *ap) mask = 0x3 << shift; /* disable assertion of portN err, done events */ - tmp = readl(mmio + HC_MAIN_IRQ_MASK_OFS); - writelfl(tmp & ~mask, mmio + HC_MAIN_IRQ_MASK_OFS); + tmp = readl(hpriv->main_mask_reg_addr); + writelfl(tmp & ~mask, hpriv->main_mask_reg_addr); } static void mv_eh_thaw(struct ata_port *ap) { - void __iomem *mmio = ap->host->iomap[MV_PRIMARY_BAR]; + struct mv_host_priv *hpriv = ap->host->private_data; + void __iomem *mmio = hpriv->base; unsigned int hc = (ap->port_no > 3) ? 1 : 0; void __iomem *hc_mmio = mv_hc_base(mmio, hc); void __iomem *port_mmio = mv_ap_base(ap); @@ -2430,8 +2569,8 @@ static void mv_eh_thaw(struct ata_port *ap) writel(hc_irq_cause, hc_mmio + HC_IRQ_CAUSE_OFS); /* enable assertion of portN err, done events */ - tmp = readl(mmio + HC_MAIN_IRQ_MASK_OFS); - writelfl(tmp | mask, mmio + HC_MAIN_IRQ_MASK_OFS); + tmp = readl(hpriv->main_mask_reg_addr); + writelfl(tmp | mask, hpriv->main_mask_reg_addr); } /** @@ -2598,9 +2737,13 @@ static int mv_chip_id(struct ata_host *host, unsigned int board_idx) break; } break; + case chip_soc: + hpriv->ops = &mv_soc_ops; + hp_flags |= MV_HP_ERRATA_60X1C0; + break; default: - dev_printk(KERN_ERR, &pdev->dev, + dev_printk(KERN_ERR, host->dev, "BUG: invalid board index %u\n", board_idx); return 1; } @@ -2633,15 +2776,25 @@ static int mv_chip_id(struct ata_host *host, unsigned int board_idx) static int mv_init_host(struct ata_host *host, unsigned int board_idx) { int rc = 0, n_hc, port, hc; - void __iomem *mmio = host->iomap[MV_PRIMARY_BAR]; struct mv_host_priv *hpriv = host->private_data; - - /* global interrupt mask */ - writel(0, mmio + HC_MAIN_IRQ_MASK_OFS); + void __iomem *mmio = hpriv->base; rc = mv_chip_id(host, board_idx); if (rc) - goto done; + goto done; + + if (HAS_PCI(host)) { + hpriv->main_cause_reg_addr = hpriv->base + + HC_MAIN_IRQ_CAUSE_OFS; + hpriv->main_mask_reg_addr = hpriv->base + HC_MAIN_IRQ_MASK_OFS; + } else { + hpriv->main_cause_reg_addr = hpriv->base + + HC_SOC_MAIN_IRQ_CAUSE_OFS; + hpriv->main_mask_reg_addr = hpriv->base + + HC_SOC_MAIN_IRQ_MASK_OFS; + } + /* global interrupt mask */ + writel(0, hpriv->main_mask_reg_addr); n_hc = mv_get_hc_count(host->ports[0]->flags); @@ -2672,13 +2825,15 @@ static int mv_init_host(struct ata_host *host, unsigned int board_idx) for (port = 0; port < host->n_ports; port++) { struct ata_port *ap = host->ports[port]; void __iomem *port_mmio = mv_port_base(mmio, port); - unsigned int offset = port_mmio - mmio; mv_port_init(&ap->ioaddr, port_mmio); #ifdef CONFIG_PCI - ata_port_pbar_desc(ap, MV_PRIMARY_BAR, -1, "mmio"); - ata_port_pbar_desc(ap, MV_PRIMARY_BAR, offset, "port"); + if (HAS_PCI(host)) { + unsigned int offset = port_mmio - mmio; + ata_port_pbar_desc(ap, MV_PRIMARY_BAR, -1, "mmio"); + ata_port_pbar_desc(ap, MV_PRIMARY_BAR, offset, "port"); + } #endif } @@ -2694,35 +2849,141 @@ static int mv_init_host(struct ata_host *host, unsigned int board_idx) writelfl(0, hc_mmio + HC_IRQ_CAUSE_OFS); } - /* Clear any currently outstanding host interrupt conditions */ - writelfl(0, mmio + hpriv->irq_cause_ofs); + if (HAS_PCI(host)) { + /* Clear any currently outstanding host interrupt conditions */ + writelfl(0, mmio + hpriv->irq_cause_ofs); - /* and unmask interrupt generation for host regs */ - writelfl(hpriv->unmask_all_irqs, mmio + hpriv->irq_mask_ofs); + /* and unmask interrupt generation for host regs */ + writelfl(hpriv->unmask_all_irqs, mmio + hpriv->irq_mask_ofs); + if (IS_GEN_I(hpriv)) + writelfl(~HC_MAIN_MASKED_IRQS_5, + hpriv->main_mask_reg_addr); + else + writelfl(~HC_MAIN_MASKED_IRQS, + hpriv->main_mask_reg_addr); + + VPRINTK("HC MAIN IRQ cause/mask=0x%08x/0x%08x " + "PCI int cause/mask=0x%08x/0x%08x\n", + readl(hpriv->main_cause_reg_addr), + readl(hpriv->main_mask_reg_addr), + readl(mmio + hpriv->irq_cause_ofs), + readl(mmio + hpriv->irq_mask_ofs)); + } else { + writelfl(~HC_MAIN_MASKED_IRQS_SOC, + hpriv->main_mask_reg_addr); + VPRINTK("HC MAIN IRQ cause/mask=0x%08x/0x%08x\n", + readl(hpriv->main_cause_reg_addr), + readl(hpriv->main_mask_reg_addr)); + } +done: + return rc; +} - if (IS_GEN_I(hpriv)) - writelfl(~HC_MAIN_MASKED_IRQS_5, mmio + HC_MAIN_IRQ_MASK_OFS); - else - writelfl(~HC_MAIN_MASKED_IRQS, mmio + HC_MAIN_IRQ_MASK_OFS); +/** + * mv_platform_probe - handle a positive probe of an soc Marvell + * host + * @pdev: platform device found + * + * LOCKING: + * Inherited from caller. + */ +static int mv_platform_probe(struct platform_device *pdev) +{ + static int printed_version; + const struct mv_sata_platform_data *mv_platform_data; + const struct ata_port_info *ppi[] = + { &mv_port_info[chip_soc], NULL }; + struct ata_host *host; + struct mv_host_priv *hpriv; + struct resource *res; + int n_ports, rc; - VPRINTK("HC MAIN IRQ cause/mask=0x%08x/0x%08x " - "PCI int cause/mask=0x%08x/0x%08x\n", - readl(mmio + HC_MAIN_IRQ_CAUSE_OFS), - readl(mmio + HC_MAIN_IRQ_MASK_OFS), - readl(mmio + hpriv->irq_cause_ofs), - readl(mmio + hpriv->irq_mask_ofs)); + if (!printed_version++) + dev_printk(KERN_INFO, &pdev->dev, "version " DRV_VERSION "\n"); -done: - return rc; + /* + * Simple resource validation .. + */ + if (unlikely(pdev->num_resources != 2)) { + dev_err(&pdev->dev, "invalid number of resources\n"); + return -EINVAL; + } + + /* + * Get the register base first + */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) + return -EINVAL; + + /* allocate host */ + mv_platform_data = pdev->dev.platform_data; + n_ports = mv_platform_data->n_ports; + + host = ata_host_alloc_pinfo(&pdev->dev, ppi, n_ports); + hpriv = devm_kzalloc(&pdev->dev, sizeof(*hpriv), GFP_KERNEL); + + if (!host || !hpriv) + return -ENOMEM; + host->private_data = hpriv; + hpriv->n_ports = n_ports; + + host->iomap = NULL; + hpriv->base = ioremap(res->start, res->end - res->start + 1); + hpriv->base -= MV_SATAHC0_REG_BASE; + + /* initialize adapter */ + rc = mv_init_host(host, chip_soc); + if (rc) + return rc; + + dev_printk(KERN_INFO, &pdev->dev, + "slots %u ports %d\n", (unsigned)MV_MAX_Q_DEPTH, + host->n_ports); + + return ata_host_activate(host, platform_get_irq(pdev, 0), mv_interrupt, + IRQF_SHARED, &mv6_sht); +} + +/* + * + * mv_platform_remove - unplug a platform interface + * @pdev: platform device + * + * A platform bus SATA device has been unplugged. Perform the needed + * cleanup. Also called on module unload for any active devices. + */ +static int __devexit mv_platform_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ata_host *host = dev_get_drvdata(dev); + struct mv_host_priv *hpriv = host->private_data; + void __iomem *base = hpriv->base; + + ata_host_detach(host); + iounmap(base); + return 0; } +static struct platform_driver mv_platform_driver = { + .probe = mv_platform_probe, + .remove = __devexit_p(mv_platform_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + + #ifdef CONFIG_PCI -static int mv_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); +static int mv_pci_init_one(struct pci_dev *pdev, + const struct pci_device_id *ent); + static struct pci_driver mv_pci_driver = { .name = DRV_NAME, .id_table = mv_pci_tbl, - .probe = mv_init_one, + .probe = mv_pci_init_one, .remove = ata_pci_remove_one, }; @@ -2828,14 +3089,15 @@ static int mv_create_dma_pools(struct mv_host_priv *hpriv, struct device *dev) } /** - * mv_init_one - handle a positive probe of a Marvell host + * mv_pci_init_one - handle a positive probe of a PCI Marvell host * @pdev: PCI device found * @ent: PCI device ID entry for the matched host * * LOCKING: * Inherited from caller. */ -static int mv_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) +static int mv_pci_init_one(struct pci_dev *pdev, + const struct pci_device_id *ent) { static int printed_version; unsigned int board_idx = (unsigned int)ent->driver_data; @@ -2855,6 +3117,7 @@ static int mv_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (!host || !hpriv) return -ENOMEM; host->private_data = hpriv; + hpriv->n_ports = n_ports; /* acquire resources */ rc = pcim_enable_device(pdev); @@ -2867,6 +3130,7 @@ static int mv_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (rc) return rc; host->iomap = pcim_iomap_table(pdev); + hpriv->base = host->iomap[MV_PRIMARY_BAR]; rc = pci_go_64(pdev); if (rc) @@ -2895,11 +3159,22 @@ static int mv_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) } #endif +static int mv_platform_probe(struct platform_device *pdev); +static int __devexit mv_platform_remove(struct platform_device *pdev); + static int __init mv_init(void) { int rc = -ENODEV; #ifdef CONFIG_PCI rc = pci_register_driver(&mv_pci_driver); + if (rc < 0) + return rc; +#endif + rc = platform_driver_register(&mv_platform_driver); + +#ifdef CONFIG_PCI + if (rc < 0) + pci_unregister_driver(&mv_pci_driver); #endif return rc; } @@ -2909,6 +3184,7 @@ static void __exit mv_exit(void) #ifdef CONFIG_PCI pci_unregister_driver(&mv_pci_driver); #endif + platform_driver_unregister(&mv_platform_driver); } MODULE_AUTHOR("Brett Russ"); diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c index bfe92a43cf8..ed5473bf7a0 100644 --- a/drivers/ata/sata_nv.c +++ b/drivers/ata/sata_nv.c @@ -247,6 +247,7 @@ struct nv_adma_port_priv { void __iomem *ctl_block; void __iomem *gen_block; void __iomem *notifier_clear_block; + u64 adma_dma_mask; u8 flags; int last_issue_ncq; }; @@ -715,9 +716,10 @@ static int nv_adma_slave_config(struct scsi_device *sdev) { struct ata_port *ap = ata_shost_to_port(sdev->host); struct nv_adma_port_priv *pp = ap->private_data; + struct nv_adma_port_priv *port0, *port1; + struct scsi_device *sdev0, *sdev1; struct pci_dev *pdev = to_pci_dev(ap->host->dev); - u64 bounce_limit; - unsigned long segment_boundary; + unsigned long segment_boundary, flags; unsigned short sg_tablesize; int rc; int adma_enable; @@ -729,6 +731,8 @@ static int nv_adma_slave_config(struct scsi_device *sdev) /* Not a proper libata device, ignore */ return rc; + spin_lock_irqsave(ap->lock, flags); + if (ap->link.device[sdev->id].class == ATA_DEV_ATAPI) { /* * NVIDIA reports that ADMA mode does not support ATAPI commands. @@ -737,7 +741,6 @@ static int nv_adma_slave_config(struct scsi_device *sdev) * Restrict DMA parameters as required by the legacy interface * when an ATAPI device is connected. */ - bounce_limit = ATA_DMA_MASK; segment_boundary = ATA_DMA_BOUNDARY; /* Subtract 1 since an extra entry may be needed for padding, see libata-scsi.c */ @@ -748,7 +751,6 @@ static int nv_adma_slave_config(struct scsi_device *sdev) adma_enable = 0; nv_adma_register_mode(ap); } else { - bounce_limit = *ap->dev->dma_mask; segment_boundary = NV_ADMA_DMA_BOUNDARY; sg_tablesize = NV_ADMA_SGTBL_TOTAL_LEN; adma_enable = 1; @@ -774,12 +776,49 @@ static int nv_adma_slave_config(struct scsi_device *sdev) if (current_reg != new_reg) pci_write_config_dword(pdev, NV_MCP_SATA_CFG_20, new_reg); - blk_queue_bounce_limit(sdev->request_queue, bounce_limit); + port0 = ap->host->ports[0]->private_data; + port1 = ap->host->ports[1]->private_data; + sdev0 = ap->host->ports[0]->link.device[0].sdev; + sdev1 = ap->host->ports[1]->link.device[0].sdev; + if ((port0->flags & NV_ADMA_ATAPI_SETUP_COMPLETE) || + (port1->flags & NV_ADMA_ATAPI_SETUP_COMPLETE)) { + /** We have to set the DMA mask to 32-bit if either port is in + ATAPI mode, since they are on the same PCI device which is + used for DMA mapping. If we set the mask we also need to set + the bounce limit on both ports to ensure that the block + layer doesn't feed addresses that cause DMA mapping to + choke. If either SCSI device is not allocated yet, it's OK + since that port will discover its correct setting when it + does get allocated. + Note: Setting 32-bit mask should not fail. */ + if (sdev0) + blk_queue_bounce_limit(sdev0->request_queue, + ATA_DMA_MASK); + if (sdev1) + blk_queue_bounce_limit(sdev1->request_queue, + ATA_DMA_MASK); + + pci_set_dma_mask(pdev, ATA_DMA_MASK); + } else { + /** This shouldn't fail as it was set to this value before */ + pci_set_dma_mask(pdev, pp->adma_dma_mask); + if (sdev0) + blk_queue_bounce_limit(sdev0->request_queue, + pp->adma_dma_mask); + if (sdev1) + blk_queue_bounce_limit(sdev1->request_queue, + pp->adma_dma_mask); + } + blk_queue_segment_boundary(sdev->request_queue, segment_boundary); blk_queue_max_hw_segments(sdev->request_queue, sg_tablesize); ata_port_printk(ap, KERN_INFO, - "bounce limit 0x%llX, segment boundary 0x%lX, hw segs %hu\n", - (unsigned long long)bounce_limit, segment_boundary, sg_tablesize); + "DMA mask 0x%llX, segment boundary 0x%lX, hw segs %hu\n", + (unsigned long long)*ap->host->dev->dma_mask, + segment_boundary, sg_tablesize); + + spin_unlock_irqrestore(ap->lock, flags); + return rc; } @@ -1140,10 +1179,20 @@ static int nv_adma_port_start(struct ata_port *ap) void *mem; dma_addr_t mem_dma; void __iomem *mmio; + struct pci_dev *pdev = to_pci_dev(dev); u16 tmp; VPRINTK("ENTER\n"); + /* Ensure DMA mask is set to 32-bit before allocating legacy PRD and + pad buffers */ + rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (rc) + return rc; + rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); + if (rc) + return rc; + rc = ata_port_start(ap); if (rc) return rc; @@ -1159,6 +1208,15 @@ static int nv_adma_port_start(struct ata_port *ap) pp->notifier_clear_block = pp->gen_block + NV_ADMA_NOTIFIER_CLEAR + (4 * ap->port_no); + /* Now that the legacy PRD and padding buffer are allocated we can + safely raise the DMA mask to allocate the CPB/APRD table. + These are allowed to fail since we store the value that ends up + being used to set as the bounce limit in slave_config later if + needed. */ + pci_set_dma_mask(pdev, DMA_BIT_MASK(64)); + pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)); + pp->adma_dma_mask = *dev->dma_mask; + mem = dmam_alloc_coherent(dev, NV_ADMA_PORT_PRIV_DMA_SZ, &mem_dma, GFP_KERNEL); if (!mem) @@ -2417,12 +2475,6 @@ static int nv_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) hpriv->type = type; host->private_data = hpriv; - /* set 64bit dma masks, may fail */ - if (type == ADMA) { - if (pci_set_dma_mask(pdev, DMA_64BIT_MASK) == 0) - pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK); - } - /* request and iomap NV_MMIO_BAR */ rc = pcim_iomap_regions(pdev, 1 << NV_MMIO_BAR, DRV_NAME); if (rc) diff --git a/drivers/ata/sata_via.c b/drivers/ata/sata_via.c index 3ef072ff319..30caa033719 100644 --- a/drivers/ata/sata_via.c +++ b/drivers/ata/sata_via.c @@ -30,8 +30,6 @@ * Hardware documentation available under NDA. * * - * To-do list: - * - VT6421 PATA support * */ diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index c5885f5ce0a..499b003f927 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -110,7 +110,7 @@ static SYSDEV_ATTR(crash_notes, 0400, show_crash_notes, NULL); * * Initialize and register the CPU device. */ -int __devinit register_cpu(struct cpu *cpu, int num) +int __cpuinit register_cpu(struct cpu *cpu, int num) { int error; cpu->node_id = cpu_to_node(num); diff --git a/drivers/block/ataflop.c b/drivers/block/ataflop.c index 94268c75d04..424995073c6 100644 --- a/drivers/block/ataflop.c +++ b/drivers/block/ataflop.c @@ -90,7 +90,7 @@ static struct atari_disk_type { unsigned blocks; /* total number of blocks */ unsigned fdc_speed; /* fdc_speed setting */ unsigned stretch; /* track doubling ? */ -} disk_type[] = { +} atari_disk_type[] = { { "d360", 9, 720, 0, 0}, /* 0: 360kB diskette */ { "D360", 9, 720, 0, 1}, /* 1: 360kb in 720k or 1.2MB drive */ { "D720", 9,1440, 0, 0}, /* 2: 720kb in 720k or 1.2MB drive */ @@ -658,7 +658,7 @@ static int do_format(int drive, int type, struct atari_format_descr *desc) return -EINVAL; } type = minor2disktype[type].index; - UDT = &disk_type[type]; + UDT = &atari_disk_type[type]; } if (!UDT || desc->track >= UDT->blocks/UDT->spt/2 || desc->head >= 2) { @@ -1064,7 +1064,7 @@ static void fd_rwsec_done1(int status) searched for a non-existent sector! */ !(read_track && FDC_READ(FDCREG_SECTOR) > SUDT->spt)) { if (Probing) { - if (SUDT > disk_type) { + if (SUDT > atari_disk_type) { if (SUDT[-1].blocks > ReqBlock) { /* try another disk type */ SUDT--; @@ -1082,7 +1082,7 @@ static void fd_rwsec_done1(int status) } else { /* record not found, but not probing. Maybe stretch wrong ? Restart probing */ if (SUD.autoprobe) { - SUDT = disk_type + StartDiskType[DriveType]; + SUDT = atari_disk_type + StartDiskType[DriveType]; set_capacity(unit[SelectedDrive].disk, SUDT->blocks); Probing = 1; @@ -1421,7 +1421,7 @@ repeat: if (type == 0) { if (!UDT) { Probing = 1; - UDT = disk_type + StartDiskType[DriveType]; + UDT = atari_disk_type + StartDiskType[DriveType]; set_capacity(floppy->disk, UDT->blocks); UD.autoprobe = 1; } @@ -1439,7 +1439,7 @@ repeat: goto repeat; } type = minor2disktype[type].index; - UDT = &disk_type[type]; + UDT = &atari_disk_type[type]; set_capacity(floppy->disk, UDT->blocks); UD.autoprobe = 0; } @@ -1505,7 +1505,7 @@ static int fd_ioctl(struct inode *inode, struct file *filp, if (minor2disktype[type].drive_types > DriveType) return -ENODEV; type = minor2disktype[type].index; - dtp = &disk_type[type]; + dtp = &atari_disk_type[type]; if (UD.flags & FTD_MSG) printk (KERN_ERR "floppy%d: found dtp %p name %s!\n", drive, dtp, dtp->name); @@ -1576,7 +1576,7 @@ static int fd_ioctl(struct inode *inode, struct file *filp, continue; } setidx = minor2disktype[settype].index; - dtp = &disk_type[setidx]; + dtp = &atari_disk_type[setidx]; /* found matching entry ?? */ if ( dtp->blocks == setprm.size diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 855ce8e5efb..9715be3f248 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -2630,12 +2630,14 @@ static void do_cciss_request(struct request_queue *q) c->Request.CDB[8] = creq->nr_sectors & 0xff; c->Request.CDB[9] = c->Request.CDB[11] = c->Request.CDB[12] = 0; } else { + u32 upper32 = upper_32_bits(start_blk); + c->Request.CDBLen = 16; c->Request.CDB[1]= 0; - c->Request.CDB[2]= (start_blk >> 56) & 0xff; //MSB - c->Request.CDB[3]= (start_blk >> 48) & 0xff; - c->Request.CDB[4]= (start_blk >> 40) & 0xff; - c->Request.CDB[5]= (start_blk >> 32) & 0xff; + c->Request.CDB[2]= (upper32 >> 24) & 0xff; //MSB + c->Request.CDB[3]= (upper32 >> 16) & 0xff; + c->Request.CDB[4]= (upper32 >> 8) & 0xff; + c->Request.CDB[5]= upper32 & 0xff; c->Request.CDB[6]= (start_blk >> 24) & 0xff; c->Request.CDB[7]= (start_blk >> 16) & 0xff; c->Request.CDB[8]= (start_blk >> 8) & 0xff; diff --git a/drivers/block/loop.c b/drivers/block/loop.c index b8af22e610d..91ebb007416 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -973,6 +973,10 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info) lo->transfer = xfer->transfer; lo->ioctl = xfer->ioctl; + if ((lo->lo_flags & LO_FLAGS_AUTOCLEAR) != + (info->lo_flags & LO_FLAGS_AUTOCLEAR)) + lo->lo_flags ^= LO_FLAGS_AUTOCLEAR; + lo->lo_encrypt_key_size = info->lo_encrypt_key_size; lo->lo_init[0] = info->lo_init[0]; lo->lo_init[1] = info->lo_init[1]; @@ -1331,6 +1335,10 @@ static int lo_release(struct inode *inode, struct file *file) mutex_lock(&lo->lo_ctl_mutex); --lo->lo_refcnt; + + if ((lo->lo_flags & LO_FLAGS_AUTOCLEAR) && !lo->lo_refcnt) + loop_clr_fd(lo, inode->i_bdev); + mutex_unlock(&lo->lo_ctl_mutex); return 0; diff --git a/drivers/block/paride/pt.c b/drivers/block/paride/pt.c index 76096cad798..8b9549ab4a4 100644 --- a/drivers/block/paride/pt.c +++ b/drivers/block/paride/pt.c @@ -660,7 +660,7 @@ static int pt_open(struct inode *inode, struct file *file) pt_identify(tape); err = -ENODEV; - if (!tape->flags & PT_MEDIA) + if (!(tape->flags & PT_MEDIA)) goto out; err = -EROFS; diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c index e9de1712e5a..674cd66dcab 100644 --- a/drivers/block/pktcdvd.c +++ b/drivers/block/pktcdvd.c @@ -2212,11 +2212,11 @@ static int pkt_media_speed(struct pktcdvd_device *pd, unsigned *speed) return ret; } - if (!buf[6] & 0x40) { + if (!(buf[6] & 0x40)) { printk(DRIVER_NAME": Disc type is not CD-RW\n"); return 1; } - if (!buf[6] & 0x4) { + if (!(buf[6] & 0x4)) { printk(DRIVER_NAME": A1 values on media are not valid, maybe not CDRW?\n"); return 1; } diff --git a/drivers/block/rd.c b/drivers/block/rd.c index 82f4eecc869..06e23be7090 100644 --- a/drivers/block/rd.c +++ b/drivers/block/rd.c @@ -56,6 +56,7 @@ #include <linux/backing-dev.h> #include <linux/blkpg.h> #include <linux/writeback.h> +#include <linux/log2.h> #include <asm/uaccess.h> @@ -450,7 +451,7 @@ static int __init rd_init(void) err = -ENOMEM; if (rd_blocksize > PAGE_SIZE || rd_blocksize < 512 || - (rd_blocksize & (rd_blocksize-1))) { + !is_power_of_2(rd_blocksize)) { printk("RAMDISK: wrong blocksize %d, reverting to defaults\n", rd_blocksize); rd_blocksize = BLOCK_SIZE; diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c index 47e5b40510c..db259e60289 100644 --- a/drivers/cdrom/cdrom.c +++ b/drivers/cdrom/cdrom.c @@ -1206,25 +1206,26 @@ int check_for_audio_disc(struct cdrom_device_info * cdi, return 0; } -/* Admittedly, the logic below could be performed in a nicer way. */ int cdrom_release(struct cdrom_device_info *cdi, struct file *fp) { struct cdrom_device_ops *cdo = cdi->ops; int opened_for_data; - cdinfo(CD_CLOSE, "entering cdrom_release\n"); + cdinfo(CD_CLOSE, "entering cdrom_release\n"); if (cdi->use_count > 0) cdi->use_count--; - if (cdi->use_count == 0) + + if (cdi->use_count == 0) { cdinfo(CD_CLOSE, "Use count for \"/dev/%s\" now zero\n", cdi->name); - if (cdi->use_count == 0) cdrom_dvd_rw_close_write(cdi); - if (cdi->use_count == 0 && - (cdo->capability & CDC_LOCK) && !keeplocked) { - cdinfo(CD_CLOSE, "Unlocking door!\n"); - cdo->lock_door(cdi, 0); + + if ((cdo->capability & CDC_LOCK) && !keeplocked) { + cdinfo(CD_CLOSE, "Unlocking door!\n"); + cdo->lock_door(cdi, 0); + } } + opened_for_data = !(cdi->options & CDO_USE_FFLAGS) || !(fp && fp->f_flags & O_NONBLOCK); diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 46662959477..85bf9b2aa74 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -276,7 +276,7 @@ config N_HDLC config RISCOM8 tristate "SDL RISCom/8 card support" - depends on SERIAL_NONSTANDARD && BROKEN_ON_SMP + depends on SERIAL_NONSTANDARD help This is a driver for the SDL Communications RISCom/8 multiport card, which gives you many serial ports. You would need something like @@ -765,7 +765,7 @@ config JS_RTC config SGI_DS1286 tristate "SGI DS1286 RTC support" - depends on SGI_IP22 + depends on SGI_HAS_DS1286 help If you say Y here and create a character special file /dev/rtc with major number 10 and minor number 135 using mknod ("man mknod"), you diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c index 480fae29c9b..44160d5ebca 100644 --- a/drivers/char/hvc_console.c +++ b/drivers/char/hvc_console.c @@ -93,7 +93,7 @@ struct hvc_struct { }; /* dynamic list of hvc_struct instances */ -static struct list_head hvc_structs = LIST_HEAD_INIT(hvc_structs); +static LIST_HEAD(hvc_structs); /* * Protect the list of hvc_struct instances from inserts and removals during diff --git a/drivers/char/hvcs.c b/drivers/char/hvcs.c index 3402def2200..786d518e947 100644 --- a/drivers/char/hvcs.c +++ b/drivers/char/hvcs.c @@ -306,7 +306,7 @@ struct hvcs_struct { /* Required to back map a kref to its containing object */ #define from_kref(k) container_of(k, struct hvcs_struct, kref) -static struct list_head hvcs_structs = LIST_HEAD_INIT(hvcs_structs); +static LIST_HEAD(hvcs_structs); static DEFINE_SPINLOCK(hvcs_structs_lock); static void hvcs_unthrottle(struct tty_struct *tty); diff --git a/drivers/char/hw_random/via-rng.c b/drivers/char/hw_random/via-rng.c index 868e39fd42e..f7feae4ebb5 100644 --- a/drivers/char/hw_random/via-rng.c +++ b/drivers/char/hw_random/via-rng.c @@ -42,6 +42,8 @@ enum { VIA_STRFILT_ENABLE = (1 << 14), VIA_RAWBITS_ENABLE = (1 << 13), VIA_RNG_ENABLE = (1 << 6), + VIA_NOISESRC1 = (1 << 8), + VIA_NOISESRC2 = (1 << 9), VIA_XSTORE_CNT_MASK = 0x0F, VIA_RNG_CHUNK_8 = 0x00, /* 64 rand bits, 64 stored bits */ @@ -119,6 +121,7 @@ static int via_rng_data_read(struct hwrng *rng, u32 *data) static int via_rng_init(struct hwrng *rng) { + struct cpuinfo_x86 *c = &cpu_data(0); u32 lo, hi, old_lo; /* Control the RNG via MSR. Tread lightly and pay very close @@ -134,6 +137,17 @@ static int via_rng_init(struct hwrng *rng) lo &= ~VIA_XSTORE_CNT_MASK; lo &= ~(VIA_STRFILT_ENABLE | VIA_STRFILT_FAIL | VIA_RAWBITS_ENABLE); lo |= VIA_RNG_ENABLE; + lo |= VIA_NOISESRC1; + + /* Enable secondary noise source on CPUs where it is present. */ + + /* Nehemiah stepping 8 and higher */ + if ((c->x86_model == 9) && (c->x86_mask > 7)) + lo |= VIA_NOISESRC2; + + /* Esther */ + if (c->x86_model >= 10) + lo |= VIA_NOISESRC2; if (lo != old_lo) wrmsr(MSR_VIA_RNG, lo, hi); diff --git a/drivers/char/i8k.c b/drivers/char/i8k.c index 30e56451642..179223a1741 100644 --- a/drivers/char/i8k.c +++ b/drivers/char/i8k.c @@ -439,6 +439,13 @@ static struct dmi_system_id __initdata i8k_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Latitude"), }, }, + { /* UK Inspiron 6400 */ + .ident = "Dell Inspiron 3", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MM061"), + }, + }, { } }; diff --git a/drivers/char/ip27-rtc.c b/drivers/char/ip27-rtc.c index 932264a657d..86e6538a77b 100644 --- a/drivers/char/ip27-rtc.c +++ b/drivers/char/ip27-rtc.c @@ -46,8 +46,8 @@ #include <asm/sn/sn0/hub.h> #include <asm/sn/sn_private.h> -static int rtc_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg); +static long rtc_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg); static int rtc_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data); @@ -75,8 +75,7 @@ static unsigned long epoch = 1970; /* year corresponding to 0x00 */ static const unsigned char days_in_mo[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; -static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) +static long rtc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct rtc_time wtime; @@ -197,7 +196,7 @@ static int rtc_release(struct inode *inode, struct file *file) static const struct file_operations rtc_fops = { .owner = THIS_MODULE, - .ioctl = rtc_ioctl, + .unlocked_ioctl = rtc_ioctl, .open = rtc_open, .release = rtc_release, }; diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 5dc1265ce1d..32b2b22996d 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -365,12 +365,12 @@ static struct device_driver ipmidriver = { }; static DEFINE_MUTEX(ipmidriver_mutex); -static struct list_head ipmi_interfaces = LIST_HEAD_INIT(ipmi_interfaces); +static LIST_HEAD(ipmi_interfaces); static DEFINE_MUTEX(ipmi_interfaces_mutex); /* List of watchers that want to know when smi's are added and deleted. */ -static struct list_head smi_watchers = LIST_HEAD_INIT(smi_watchers); +static LIST_HEAD(smi_watchers); static DEFINE_MUTEX(smi_watchers_mutex); @@ -441,7 +441,7 @@ struct watcher_entry { int ipmi_smi_watcher_register(struct ipmi_smi_watcher *watcher) { ipmi_smi_t intf; - struct list_head to_deliver = LIST_HEAD_INIT(to_deliver); + LIST_HEAD(to_deliver); struct watcher_entry *e, *e2; mutex_lock(&smi_watchers_mutex); diff --git a/drivers/char/lp.c b/drivers/char/lp.c index 81674d7c56c..60ac642752b 100644 --- a/drivers/char/lp.c +++ b/drivers/char/lp.c @@ -312,7 +312,7 @@ static ssize_t lp_write(struct file * file, const char __user * buf, if (copy_size > LP_BUFFER_SIZE) copy_size = LP_BUFFER_SIZE; - if (down_interruptible (&lp_table[minor].port_mutex)) + if (mutex_lock_interruptible(&lp_table[minor].port_mutex)) return -EINTR; if (copy_from_user (kbuf, buf, copy_size)) { @@ -399,7 +399,7 @@ static ssize_t lp_write(struct file * file, const char __user * buf, lp_release_parport (&lp_table[minor]); } out_unlock: - up (&lp_table[minor].port_mutex); + mutex_unlock(&lp_table[minor].port_mutex); return retv; } @@ -421,7 +421,7 @@ static ssize_t lp_read(struct file * file, char __user * buf, if (count > LP_BUFFER_SIZE) count = LP_BUFFER_SIZE; - if (down_interruptible (&lp_table[minor].port_mutex)) + if (mutex_lock_interruptible(&lp_table[minor].port_mutex)) return -EINTR; lp_claim_parport_or_block (&lp_table[minor]); @@ -479,7 +479,7 @@ static ssize_t lp_read(struct file * file, char __user * buf, if (retval > 0 && copy_to_user (buf, kbuf, retval)) retval = -EFAULT; - up (&lp_table[minor].port_mutex); + mutex_unlock(&lp_table[minor].port_mutex); return retval; } @@ -888,7 +888,7 @@ static int __init lp_init (void) lp_table[i].last_error = 0; init_waitqueue_head (&lp_table[i].waitq); init_waitqueue_head (&lp_table[i].dataq); - init_MUTEX (&lp_table[i].port_mutex); + mutex_init(&lp_table[i].port_mutex); lp_table[i].timeout = 10 * HZ; } diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c index fd0abef7ee0..47420787a01 100644 --- a/drivers/char/mxser.c +++ b/drivers/char/mxser.c @@ -37,7 +37,6 @@ #include <linux/module.h> -#include <linux/autoconf.h> #include <linux/errno.h> #include <linux/signal.h> #include <linux/sched.h> diff --git a/drivers/char/mxser_new.c b/drivers/char/mxser_new.c index 081c84c7b54..bf1bee4e1f5 100644 --- a/drivers/char/mxser_new.c +++ b/drivers/char/mxser_new.c @@ -20,7 +20,6 @@ */ #include <linux/module.h> -#include <linux/autoconf.h> #include <linux/errno.h> #include <linux/signal.h> #include <linux/sched.h> diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c index 596c7173997..90c3969012a 100644 --- a/drivers/char/n_tty.c +++ b/drivers/char/n_tty.c @@ -695,17 +695,16 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c) return; } - if (tty->stopped && !tty->flow_stopped && - I_IXON(tty) && I_IXANY(tty)) { - start_tty(tty); - return; - } - if (I_ISTRIP(tty)) c &= 0x7f; if (I_IUCLC(tty) && L_IEXTEN(tty)) c=tolower(c); + if (tty->stopped && !tty->flow_stopped && I_IXON(tty) && + ((I_IXANY(tty) && c != START_CHAR(tty) && c != STOP_CHAR(tty)) || + c == INTR_CHAR(tty) || c == QUIT_CHAR(tty))) + start_tty(tty); + if (tty->closing) { if (I_IXON(tty)) { if (c == START_CHAR(tty)) @@ -769,7 +768,21 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c) signal = SIGTSTP; if (c == SUSP_CHAR(tty)) { send_signal: - isig(signal, tty, 0); + /* + * Echo character, and then send the signal. + * Note that we do not use isig() here because we want + * the order to be: + * 1) flush, 2) echo, 3) signal + */ + if (!L_NOFLSH(tty)) { + n_tty_flush_buffer(tty); + if (tty->driver->flush_buffer) + tty->driver->flush_buffer(tty); + } + if (L_ECHO(tty)) + echo_char(c, tty); + if (tty->pgrp) + kill_pgrp(tty->pgrp, signal, 1); return; } } diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index 8caff0ca80f..279ff5005ce 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -57,6 +57,7 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/ioctl.h> +#include <linux/synclink.h> #include <asm/system.h> #include <asm/io.h> @@ -87,8 +88,6 @@ #include <asm/uaccess.h> -#include "linux/synclink.h" - static MGSL_PARAMS default_params = { MGSL_MODE_HDLC, /* unsigned long mode */ 0, /* unsigned char loopback; */ diff --git a/drivers/char/random.c b/drivers/char/random.c index c511a831f0c..f43c89f7c44 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -1039,6 +1039,7 @@ write_pool(struct entropy_store *r, const char __user *buffer, size_t count) p += bytes; add_entropy_words(r, buf, (bytes + 3) / 4); + cond_resched(); } return 0; diff --git a/drivers/char/riscom8.c b/drivers/char/riscom8.c index 102ece4c4e0..d130b87d8ed 100644 --- a/drivers/char/riscom8.c +++ b/drivers/char/riscom8.c @@ -47,6 +47,7 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/tty_flip.h> +#include <linux/spinlock.h> #include <asm/uaccess.h> @@ -81,6 +82,8 @@ static struct tty_driver *riscom_driver; +static DEFINE_SPINLOCK(riscom_lock); + static struct riscom_board rc_board[RC_NBOARD] = { { .base = RC_IOBASE1, @@ -217,13 +220,14 @@ static void __init rc_init_CD180(struct riscom_board const * bp) { unsigned long flags; - save_flags(flags); cli(); + spin_lock_irqsave(&riscom_lock, flags); + rc_out(bp, RC_CTOUT, 0); /* Clear timeout */ rc_wait_CCR(bp); /* Wait for CCR ready */ rc_out(bp, CD180_CCR, CCR_HARDRESET); /* Reset CD180 chip */ - sti(); + spin_unlock_irqrestore(&riscom_lock, flags); msleep(50); /* Delay 0.05 sec */ - cli(); + spin_lock_irqsave(&riscom_lock, flags); rc_out(bp, CD180_GIVR, RC_ID); /* Set ID for this chip */ rc_out(bp, CD180_GICR, 0); /* Clear all bits */ rc_out(bp, CD180_PILR1, RC_ACK_MINT); /* Prio for modem intr */ @@ -234,7 +238,7 @@ static void __init rc_init_CD180(struct riscom_board const * bp) rc_out(bp, CD180_PPRH, (RC_OSCFREQ/(1000000/RISCOM_TPS)) >> 8); rc_out(bp, CD180_PPRL, (RC_OSCFREQ/(1000000/RISCOM_TPS)) & 0xff); - restore_flags(flags); + spin_unlock_irqrestore(&riscom_lock, flags); } /* Main probing routine, also sets irq. */ @@ -812,9 +816,9 @@ static int rc_setup_port(struct riscom_board *bp, struct riscom_port *port) } port->xmit_buf = (unsigned char *) tmp; } - - save_flags(flags); cli(); - + + spin_lock_irqsave(&riscom_lock, flags); + if (port->tty) clear_bit(TTY_IO_ERROR, &port->tty->flags); @@ -825,7 +829,7 @@ static int rc_setup_port(struct riscom_board *bp, struct riscom_port *port) rc_change_speed(bp, port); port->flags |= ASYNC_INITIALIZED; - restore_flags(flags); + spin_unlock_irqrestore(&riscom_lock, flags); return 0; } @@ -901,6 +905,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, int retval; int do_clocal = 0; int CD; + unsigned long flags; /* * If the device is in the middle of being closed, then block @@ -936,19 +941,26 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, */ retval = 0; add_wait_queue(&port->open_wait, &wait); - cli(); + + spin_lock_irqsave(&riscom_lock, flags); + if (!tty_hung_up_p(filp)) port->count--; - sti(); + + spin_unlock_irqrestore(&riscom_lock, flags); + port->blocked_open++; while (1) { - cli(); + spin_lock_irqsave(&riscom_lock, flags); + rc_out(bp, CD180_CAR, port_No(port)); CD = rc_in(bp, CD180_MSVR) & MSVR_CD; rc_out(bp, CD180_MSVR, MSVR_RTS); bp->DTR &= ~(1u << port_No(port)); rc_out(bp, RC_DTR, bp->DTR); - sti(); + + spin_unlock_irqrestore(&riscom_lock, flags); + set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)) { @@ -1020,8 +1032,9 @@ static void rc_close(struct tty_struct * tty, struct file * filp) if (!port || rc_paranoia_check(port, tty->name, "close")) return; - - save_flags(flags); cli(); + + spin_lock_irqsave(&riscom_lock, flags); + if (tty_hung_up_p(filp)) goto out; @@ -1088,7 +1101,9 @@ static void rc_close(struct tty_struct * tty, struct file * filp) } port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); wake_up_interruptible(&port->close_wait); -out: restore_flags(flags); + +out: + spin_unlock_irqrestore(&riscom_lock, flags); } static int rc_write(struct tty_struct * tty, @@ -1107,34 +1122,33 @@ static int rc_write(struct tty_struct * tty, if (!tty || !port->xmit_buf) return 0; - save_flags(flags); while (1) { - cli(); + spin_lock_irqsave(&riscom_lock, flags); + c = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt - 1, SERIAL_XMIT_SIZE - port->xmit_head)); - if (c <= 0) { - restore_flags(flags); - break; - } + if (c <= 0) + break; /* lock continues to be held */ memcpy(port->xmit_buf + port->xmit_head, buf, c); port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1); port->xmit_cnt += c; - restore_flags(flags); + + spin_unlock_irqrestore(&riscom_lock, flags); buf += c; count -= c; total += c; } - cli(); if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped && !(port->IER & IER_TXRDY)) { port->IER |= IER_TXRDY; rc_out(bp, CD180_CAR, port_No(port)); rc_out(bp, CD180_IER, port->IER); } - restore_flags(flags); + + spin_unlock_irqrestore(&riscom_lock, flags); return total; } @@ -1150,7 +1164,7 @@ static void rc_put_char(struct tty_struct * tty, unsigned char ch) if (!tty || !port->xmit_buf) return; - save_flags(flags); cli(); + spin_lock_irqsave(&riscom_lock, flags); if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) goto out; @@ -1158,7 +1172,9 @@ static void rc_put_char(struct tty_struct * tty, unsigned char ch) port->xmit_buf[port->xmit_head++] = ch; port->xmit_head &= SERIAL_XMIT_SIZE - 1; port->xmit_cnt++; -out: restore_flags(flags); + +out: + spin_unlock_irqrestore(&riscom_lock, flags); } static void rc_flush_chars(struct tty_struct * tty) @@ -1173,11 +1189,13 @@ static void rc_flush_chars(struct tty_struct * tty) !port->xmit_buf) return; - save_flags(flags); cli(); + spin_lock_irqsave(&riscom_lock, flags); + port->IER |= IER_TXRDY; rc_out(port_Board(port), CD180_CAR, port_No(port)); rc_out(port_Board(port), CD180_IER, port->IER); - restore_flags(flags); + + spin_unlock_irqrestore(&riscom_lock, flags); } static int rc_write_room(struct tty_struct * tty) @@ -1212,9 +1230,11 @@ static void rc_flush_buffer(struct tty_struct *tty) if (rc_paranoia_check(port, tty->name, "rc_flush_buffer")) return; - save_flags(flags); cli(); + spin_lock_irqsave(&riscom_lock, flags); + port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; - restore_flags(flags); + + spin_unlock_irqrestore(&riscom_lock, flags); tty_wakeup(tty); } @@ -1231,11 +1251,15 @@ static int rc_tiocmget(struct tty_struct *tty, struct file *file) return -ENODEV; bp = port_Board(port); - save_flags(flags); cli(); + + spin_lock_irqsave(&riscom_lock, flags); + rc_out(bp, CD180_CAR, port_No(port)); status = rc_in(bp, CD180_MSVR); result = rc_in(bp, RC_RI) & (1u << port_No(port)) ? 0 : TIOCM_RNG; - restore_flags(flags); + + spin_unlock_irqrestore(&riscom_lock, flags); + result |= ((status & MSVR_RTS) ? TIOCM_RTS : 0) | ((status & MSVR_DTR) ? TIOCM_DTR : 0) | ((status & MSVR_CD) ? TIOCM_CAR : 0) @@ -1256,7 +1280,8 @@ static int rc_tiocmset(struct tty_struct *tty, struct file *file, bp = port_Board(port); - save_flags(flags); cli(); + spin_lock_irqsave(&riscom_lock, flags); + if (set & TIOCM_RTS) port->MSVR |= MSVR_RTS; if (set & TIOCM_DTR) @@ -1270,7 +1295,9 @@ static int rc_tiocmset(struct tty_struct *tty, struct file *file, rc_out(bp, CD180_CAR, port_No(port)); rc_out(bp, CD180_MSVR, port->MSVR); rc_out(bp, RC_DTR, bp->DTR); - restore_flags(flags); + + spin_unlock_irqrestore(&riscom_lock, flags); + return 0; } @@ -1279,7 +1306,8 @@ static inline void rc_send_break(struct riscom_port * port, unsigned long length struct riscom_board *bp = port_Board(port); unsigned long flags; - save_flags(flags); cli(); + spin_lock_irqsave(&riscom_lock, flags); + port->break_length = RISCOM_TPS / HZ * length; port->COR2 |= COR2_ETC; port->IER |= IER_TXRDY; @@ -1289,7 +1317,8 @@ static inline void rc_send_break(struct riscom_port * port, unsigned long length rc_wait_CCR(bp); rc_out(bp, CD180_CCR, CCR_CORCHG2); rc_wait_CCR(bp); - restore_flags(flags); + + spin_unlock_irqrestore(&riscom_lock, flags); } static inline int rc_set_serial_info(struct riscom_port * port, @@ -1298,7 +1327,6 @@ static inline int rc_set_serial_info(struct riscom_port * port, struct serial_struct tmp; struct riscom_board *bp = port_Board(port); int change_speed; - unsigned long flags; if (copy_from_user(&tmp, newinfo, sizeof(tmp))) return -EFAULT; @@ -1332,9 +1360,11 @@ static inline int rc_set_serial_info(struct riscom_port * port, port->closing_wait = tmp.closing_wait; } if (change_speed) { - save_flags(flags); cli(); + unsigned long flags; + + spin_lock_irqsave(&riscom_lock, flags); rc_change_speed(bp, port); - restore_flags(flags); + spin_unlock_irqrestore(&riscom_lock, flags); } return 0; } @@ -1414,17 +1444,19 @@ static void rc_throttle(struct tty_struct * tty) return; bp = port_Board(port); - - save_flags(flags); cli(); + + spin_lock_irqsave(&riscom_lock, flags); + port->MSVR &= ~MSVR_RTS; rc_out(bp, CD180_CAR, port_No(port)); - if (I_IXOFF(tty)) { + if (I_IXOFF(tty)) { rc_wait_CCR(bp); rc_out(bp, CD180_CCR, CCR_SSCH2); rc_wait_CCR(bp); } rc_out(bp, CD180_MSVR, port->MSVR); - restore_flags(flags); + + spin_unlock_irqrestore(&riscom_lock, flags); } static void rc_unthrottle(struct tty_struct * tty) @@ -1438,7 +1470,8 @@ static void rc_unthrottle(struct tty_struct * tty) bp = port_Board(port); - save_flags(flags); cli(); + spin_lock_irqsave(&riscom_lock, flags); + port->MSVR |= MSVR_RTS; rc_out(bp, CD180_CAR, port_No(port)); if (I_IXOFF(tty)) { @@ -1447,7 +1480,8 @@ static void rc_unthrottle(struct tty_struct * tty) rc_wait_CCR(bp); } rc_out(bp, CD180_MSVR, port->MSVR); - restore_flags(flags); + + spin_unlock_irqrestore(&riscom_lock, flags); } static void rc_stop(struct tty_struct * tty) @@ -1461,11 +1495,13 @@ static void rc_stop(struct tty_struct * tty) bp = port_Board(port); - save_flags(flags); cli(); + spin_lock_irqsave(&riscom_lock, flags); + port->IER &= ~IER_TXRDY; rc_out(bp, CD180_CAR, port_No(port)); rc_out(bp, CD180_IER, port->IER); - restore_flags(flags); + + spin_unlock_irqrestore(&riscom_lock, flags); } static void rc_start(struct tty_struct * tty) @@ -1479,13 +1515,15 @@ static void rc_start(struct tty_struct * tty) bp = port_Board(port); - save_flags(flags); cli(); + spin_lock_irqsave(&riscom_lock, flags); + if (port->xmit_cnt && port->xmit_buf && !(port->IER & IER_TXRDY)) { port->IER |= IER_TXRDY; rc_out(bp, CD180_CAR, port_No(port)); rc_out(bp, CD180_IER, port->IER); } - restore_flags(flags); + + spin_unlock_irqrestore(&riscom_lock, flags); } /* @@ -1537,9 +1575,9 @@ static void rc_set_termios(struct tty_struct * tty, struct ktermios * old_termio tty->termios->c_iflag == old_termios->c_iflag) return; - save_flags(flags); cli(); + spin_lock_irqsave(&riscom_lock, flags); rc_change_speed(port_Board(port), port); - restore_flags(flags); + spin_unlock_irqrestore(&riscom_lock, flags); if ((old_termios->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) { @@ -1627,11 +1665,12 @@ static void rc_release_drivers(void) { unsigned long flags; - save_flags(flags); - cli(); + spin_lock_irqsave(&riscom_lock, flags); + tty_unregister_driver(riscom_driver); put_tty_driver(riscom_driver); - restore_flags(flags); + + spin_unlock_irqrestore(&riscom_lock, flags); } #ifndef MODULE diff --git a/drivers/char/ser_a2232.c b/drivers/char/ser_a2232.c index 3c869145bfd..4ba3aec9e1c 100644 --- a/drivers/char/ser_a2232.c +++ b/drivers/char/ser_a2232.c @@ -653,7 +653,7 @@ static void a2232_init_portstructs(void) port->gs.closing_wait = 30 * HZ; port->gs.rd = &a2232_real_driver; #ifdef NEW_WRITE_LOCKING - init_MUTEX(&(port->gs.port_write_mutex)); + mutex_init(&(port->gs.port_write_mutex)); #endif init_waitqueue_head(&port->gs.open_wait); init_waitqueue_head(&port->gs.close_wait); diff --git a/drivers/char/synclink.c b/drivers/char/synclink.c index d010ed95ed3..ddc74d1f4f1 100644 --- a/drivers/char/synclink.c +++ b/drivers/char/synclink.c @@ -85,6 +85,7 @@ #include <linux/vmalloc.h> #include <linux/init.h> #include <linux/ioctl.h> +#include <linux/synclink.h> #include <asm/system.h> #include <asm/io.h> @@ -110,8 +111,6 @@ #include <asm/uaccess.h> -#include "linux/synclink.h" - #define RCLRVALUE 0xffff static MGSL_PARAMS default_params = { diff --git a/drivers/char/synclink_gt.c b/drivers/char/synclink_gt.c index 64e835f6243..1f954acf2ba 100644 --- a/drivers/char/synclink_gt.c +++ b/drivers/char/synclink_gt.c @@ -73,6 +73,7 @@ #include <linux/bitops.h> #include <linux/workqueue.h> #include <linux/hdlc.h> +#include <linux/synclink.h> #include <asm/system.h> #include <asm/io.h> @@ -81,8 +82,6 @@ #include <asm/types.h> #include <asm/uaccess.h> -#include "linux/synclink.h" - #if defined(CONFIG_HDLC) || (defined(CONFIG_HDLC_MODULE) && defined(CONFIG_SYNCLINK_GT_MODULE)) #define SYNCLINK_GENERIC_HDLC 1 #else @@ -2040,37 +2039,41 @@ static void bh_transmit(struct slgt_info *info) tty_wakeup(tty); } -static void dsr_change(struct slgt_info *info) +static void dsr_change(struct slgt_info *info, unsigned short status) { - get_signals(info); + if (status & BIT3) { + info->signals |= SerialSignal_DSR; + info->input_signal_events.dsr_up++; + } else { + info->signals &= ~SerialSignal_DSR; + info->input_signal_events.dsr_down++; + } DBGISR(("dsr_change %s signals=%04X\n", info->device_name, info->signals)); if ((info->dsr_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) { slgt_irq_off(info, IRQ_DSR); return; } info->icount.dsr++; - if (info->signals & SerialSignal_DSR) - info->input_signal_events.dsr_up++; - else - info->input_signal_events.dsr_down++; wake_up_interruptible(&info->status_event_wait_q); wake_up_interruptible(&info->event_wait_q); info->pending_bh |= BH_STATUS; } -static void cts_change(struct slgt_info *info) +static void cts_change(struct slgt_info *info, unsigned short status) { - get_signals(info); + if (status & BIT2) { + info->signals |= SerialSignal_CTS; + info->input_signal_events.cts_up++; + } else { + info->signals &= ~SerialSignal_CTS; + info->input_signal_events.cts_down++; + } DBGISR(("cts_change %s signals=%04X\n", info->device_name, info->signals)); if ((info->cts_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) { slgt_irq_off(info, IRQ_CTS); return; } info->icount.cts++; - if (info->signals & SerialSignal_CTS) - info->input_signal_events.cts_up++; - else - info->input_signal_events.cts_down++; wake_up_interruptible(&info->status_event_wait_q); wake_up_interruptible(&info->event_wait_q); info->pending_bh |= BH_STATUS; @@ -2091,20 +2094,21 @@ static void cts_change(struct slgt_info *info) } } -static void dcd_change(struct slgt_info *info) +static void dcd_change(struct slgt_info *info, unsigned short status) { - get_signals(info); + if (status & BIT1) { + info->signals |= SerialSignal_DCD; + info->input_signal_events.dcd_up++; + } else { + info->signals &= ~SerialSignal_DCD; + info->input_signal_events.dcd_down++; + } DBGISR(("dcd_change %s signals=%04X\n", info->device_name, info->signals)); if ((info->dcd_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) { slgt_irq_off(info, IRQ_DCD); return; } info->icount.dcd++; - if (info->signals & SerialSignal_DCD) { - info->input_signal_events.dcd_up++; - } else { - info->input_signal_events.dcd_down++; - } #if SYNCLINK_GENERIC_HDLC if (info->netcount) { if (info->signals & SerialSignal_DCD) @@ -2127,20 +2131,21 @@ static void dcd_change(struct slgt_info *info) } } -static void ri_change(struct slgt_info *info) +static void ri_change(struct slgt_info *info, unsigned short status) { - get_signals(info); + if (status & BIT0) { + info->signals |= SerialSignal_RI; + info->input_signal_events.ri_up++; + } else { + info->signals &= ~SerialSignal_RI; + info->input_signal_events.ri_down++; + } DBGISR(("ri_change %s signals=%04X\n", info->device_name, info->signals)); if ((info->ri_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) { slgt_irq_off(info, IRQ_RI); return; } - info->icount.dcd++; - if (info->signals & SerialSignal_RI) { - info->input_signal_events.ri_up++; - } else { - info->input_signal_events.ri_down++; - } + info->icount.rng++; wake_up_interruptible(&info->status_event_wait_q); wake_up_interruptible(&info->event_wait_q); info->pending_bh |= BH_STATUS; @@ -2191,13 +2196,13 @@ static void isr_serial(struct slgt_info *info) } if (status & IRQ_DSR) - dsr_change(info); + dsr_change(info, status); if (status & IRQ_CTS) - cts_change(info); + cts_change(info, status); if (status & IRQ_DCD) - dcd_change(info); + dcd_change(info, status); if (status & IRQ_RI) - ri_change(info); + ri_change(info, status); } static void isr_rdma(struct slgt_info *info) diff --git a/drivers/char/synclinkmp.c b/drivers/char/synclinkmp.c index c63013b2fc3..f3e7807f78d 100644 --- a/drivers/char/synclinkmp.c +++ b/drivers/char/synclinkmp.c @@ -66,6 +66,7 @@ #include <linux/termios.h> #include <linux/workqueue.h> #include <linux/hdlc.h> +#include <linux/synclink.h> #if defined(CONFIG_HDLC) || (defined(CONFIG_HDLC_MODULE) && defined(CONFIG_SYNCLINKMP_MODULE)) #define SYNCLINK_GENERIC_HDLC 1 @@ -80,8 +81,6 @@ #include <asm/uaccess.h> -#include "linux/synclink.h" - static MGSL_PARAMS default_params = { MGSL_MODE_HDLC, /* unsigned long mode */ 0, /* unsigned char loopback; */ diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index c88424a0c89..a5d8bcb4000 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -1031,18 +1031,13 @@ void tpm_remove_hardware(struct device *dev) spin_unlock(&driver_lock); - dev_set_drvdata(dev, NULL); misc_deregister(&chip->vendor.miscdev); - kfree(chip->vendor.miscdev.name); sysfs_remove_group(&dev->kobj, chip->vendor.attr_group); tpm_bios_log_teardown(chip->bios_dir); - clear_bit(chip->dev_num, dev_mask); - - kfree(chip); - - put_device(dev); + /* write it this way to be explicit (chip->dev == dev) */ + put_device(chip->dev); } EXPORT_SYMBOL_GPL(tpm_remove_hardware); @@ -1083,6 +1078,26 @@ int tpm_pm_resume(struct device *dev) EXPORT_SYMBOL_GPL(tpm_pm_resume); /* + * Once all references to platform device are down to 0, + * release all allocated structures. + * In case vendor provided release function, + * call it too. + */ +static void tpm_dev_release(struct device *dev) +{ + struct tpm_chip *chip = dev_get_drvdata(dev); + + if (chip->vendor.release) + chip->vendor.release(dev); + + chip->release(dev); + + clear_bit(chip->dev_num, dev_mask); + kfree(chip->vendor.miscdev.name); + kfree(chip); +} + +/* * Called from tpm_<specific>.c probe function only for devices * the driver has determined it should claim. Prior to calling * this function the specific probe function has called pci_enable_device @@ -1136,23 +1151,21 @@ struct tpm_chip *tpm_register_hardware(struct device *dev, const struct tpm_vend chip->vendor.miscdev.parent = dev; chip->dev = get_device(dev); + chip->release = dev->release; + dev->release = tpm_dev_release; + dev_set_drvdata(dev, chip); if (misc_register(&chip->vendor.miscdev)) { dev_err(chip->dev, "unable to misc_register %s, minor %d\n", chip->vendor.miscdev.name, chip->vendor.miscdev.minor); - put_device(dev); - clear_bit(chip->dev_num, dev_mask); - kfree(chip); - kfree(devname); + put_device(chip->dev); return NULL; } spin_lock(&driver_lock); - dev_set_drvdata(dev, chip); - list_add(&chip->list, &tpm_chip_list); spin_unlock(&driver_lock); @@ -1160,10 +1173,7 @@ struct tpm_chip *tpm_register_hardware(struct device *dev, const struct tpm_vend if (sysfs_create_group(&dev->kobj, chip->vendor.attr_group)) { list_del(&chip->list); misc_deregister(&chip->vendor.miscdev); - put_device(dev); - clear_bit(chip->dev_num, dev_mask); - kfree(chip); - kfree(devname); + put_device(chip->dev); return NULL; } diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index d15ccddc92e..e885148b4cf 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -74,6 +74,7 @@ struct tpm_vendor_specific { int (*send) (struct tpm_chip *, u8 *, size_t); void (*cancel) (struct tpm_chip *); u8 (*status) (struct tpm_chip *); + void (*release) (struct device *); struct miscdevice miscdev; struct attribute_group *attr_group; struct list_head list; @@ -106,6 +107,7 @@ struct tpm_chip { struct dentry **bios_dir; struct list_head list; + void (*release) (struct device *); }; #define to_tpm_chip(n) container_of(n, struct tpm_chip, vendor) diff --git a/drivers/char/tpm/tpm_infineon.c b/drivers/char/tpm/tpm_infineon.c index 967002a5a1e..726ee8a0277 100644 --- a/drivers/char/tpm/tpm_infineon.c +++ b/drivers/char/tpm/tpm_infineon.c @@ -611,7 +611,7 @@ static __devexit void tpm_inf_pnp_remove(struct pnp_dev *dev) } } -static struct pnp_driver tpm_inf_pnp = { +static struct pnp_driver tpm_inf_pnp_driver = { .name = "tpm_inf_pnp", .driver = { .owner = THIS_MODULE, @@ -625,12 +625,12 @@ static struct pnp_driver tpm_inf_pnp = { static int __init init_inf(void) { - return pnp_register_driver(&tpm_inf_pnp); + return pnp_register_driver(&tpm_inf_pnp_driver); } static void __exit cleanup_inf(void) { - pnp_unregister_driver(&tpm_inf_pnp); + pnp_unregister_driver(&tpm_inf_pnp_driver); } module_init(init_inf); diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index f36fecd3fd2..79c86c47947 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -138,7 +138,7 @@ EXPORT_SYMBOL(tty_mutex); extern struct tty_driver *ptm_driver; /* Unix98 pty masters; for /dev/ptmx */ extern int pty_limit; /* Config limit on Unix98 ptys */ static DEFINE_IDR(allocated_ptys); -static DECLARE_MUTEX(allocated_ptys_lock); +static DEFINE_MUTEX(allocated_ptys_lock); static int ptmx_open(struct inode *, struct file *); #endif @@ -2571,9 +2571,9 @@ static void release_dev(struct file * filp) #ifdef CONFIG_UNIX98_PTYS /* Make this pty number available for reallocation */ if (devpts) { - down(&allocated_ptys_lock); + mutex_lock(&allocated_ptys_lock); idr_remove(&allocated_ptys, idx); - up(&allocated_ptys_lock); + mutex_unlock(&allocated_ptys_lock); } #endif @@ -2737,24 +2737,24 @@ static int ptmx_open(struct inode * inode, struct file * filp) nonseekable_open(inode, filp); /* find a device that is not in use. */ - down(&allocated_ptys_lock); + mutex_lock(&allocated_ptys_lock); if (!idr_pre_get(&allocated_ptys, GFP_KERNEL)) { - up(&allocated_ptys_lock); + mutex_unlock(&allocated_ptys_lock); return -ENOMEM; } idr_ret = idr_get_new(&allocated_ptys, NULL, &index); if (idr_ret < 0) { - up(&allocated_ptys_lock); + mutex_unlock(&allocated_ptys_lock); if (idr_ret == -EAGAIN) return -ENOMEM; return -EIO; } if (index >= pty_limit) { idr_remove(&allocated_ptys, index); - up(&allocated_ptys_lock); + mutex_unlock(&allocated_ptys_lock); return -EIO; } - up(&allocated_ptys_lock); + mutex_unlock(&allocated_ptys_lock); mutex_lock(&tty_mutex); retval = init_dev(ptm_driver, index, &tty); @@ -2781,9 +2781,9 @@ out1: release_dev(filp); return retval; out: - down(&allocated_ptys_lock); + mutex_lock(&allocated_ptys_lock); idr_remove(&allocated_ptys, index); - up(&allocated_ptys_lock); + mutex_unlock(&allocated_ptys_lock); return retval; } #endif @@ -3721,7 +3721,6 @@ static void initialize_tty_struct(struct tty_struct *tty) tty->buf.head = tty->buf.tail = NULL; tty_buffer_init(tty); INIT_DELAYED_WORK(&tty->buf.work, flush_to_ldisc); - init_MUTEX(&tty->buf.pty_sem); mutex_init(&tty->termios_mutex); init_waitqueue_head(&tty->write_wait); init_waitqueue_head(&tty->read_wait); @@ -4048,10 +4047,6 @@ void __init console_init(void) } } -#ifdef CONFIG_VT -extern int vty_init(void); -#endif - static int __init tty_class_init(void) { tty_class = class_create(THIS_MODULE, "tty"); diff --git a/drivers/char/vt.c b/drivers/char/vt.c index 7a5badfb7d8..367be917506 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -2400,13 +2400,15 @@ static void vt_console_print(struct console *co, const char *b, unsigned count) { struct vc_data *vc = vc_cons[fg_console].d; unsigned char c; - static unsigned long printing; + static DEFINE_SPINLOCK(printing_lock); const ushort *start; ushort cnt = 0; ushort myx; /* console busy or not yet initialized */ - if (!printable || test_and_set_bit(0, &printing)) + if (!printable) + return; + if (!spin_trylock(&printing_lock)) return; if (kmsg_redirect && vc_cons_allocated(kmsg_redirect - 1)) @@ -2481,7 +2483,7 @@ static void vt_console_print(struct console *co, const char *b, unsigned count) notify_update(vc); quit: - clear_bit(0, &printing); + spin_unlock(&printing_lock); } static struct tty_driver *vt_console_device(struct console *c, int *index) diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index c46b7c219ee..a703deffb79 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -5,6 +5,7 @@ menuconfig DMADEVICES bool "DMA Engine support" depends on (PCI && X86) || ARCH_IOP32X || ARCH_IOP33X || ARCH_IOP13XX + depends on !HIGHMEM64G help DMA engines can do asynchronous data transfers without involving the host CPU. Currently, this framework can be diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index bcf52df3033..29965231b91 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -473,20 +473,22 @@ dma_async_memcpy_buf_to_buf(struct dma_chan *chan, void *dest, { struct dma_device *dev = chan->device; struct dma_async_tx_descriptor *tx; - dma_addr_t addr; + dma_addr_t dma_dest, dma_src; dma_cookie_t cookie; int cpu; - tx = dev->device_prep_dma_memcpy(chan, len, 0); - if (!tx) + dma_src = dma_map_single(dev->dev, src, len, DMA_TO_DEVICE); + dma_dest = dma_map_single(dev->dev, dest, len, DMA_FROM_DEVICE); + tx = dev->device_prep_dma_memcpy(chan, dma_dest, dma_src, len, 0); + + if (!tx) { + dma_unmap_single(dev->dev, dma_src, len, DMA_TO_DEVICE); + dma_unmap_single(dev->dev, dma_dest, len, DMA_FROM_DEVICE); return -ENOMEM; + } tx->ack = 1; tx->callback = NULL; - addr = dma_map_single(dev->dev, src, len, DMA_TO_DEVICE); - tx->tx_set_src(addr, tx, 0); - addr = dma_map_single(dev->dev, dest, len, DMA_FROM_DEVICE); - tx->tx_set_dest(addr, tx, 0); cookie = tx->tx_submit(tx); cpu = get_cpu(); @@ -517,20 +519,22 @@ dma_async_memcpy_buf_to_pg(struct dma_chan *chan, struct page *page, { struct dma_device *dev = chan->device; struct dma_async_tx_descriptor *tx; - dma_addr_t addr; + dma_addr_t dma_dest, dma_src; dma_cookie_t cookie; int cpu; - tx = dev->device_prep_dma_memcpy(chan, len, 0); - if (!tx) + dma_src = dma_map_single(dev->dev, kdata, len, DMA_TO_DEVICE); + dma_dest = dma_map_page(dev->dev, page, offset, len, DMA_FROM_DEVICE); + tx = dev->device_prep_dma_memcpy(chan, dma_dest, dma_src, len, 0); + + if (!tx) { + dma_unmap_single(dev->dev, dma_src, len, DMA_TO_DEVICE); + dma_unmap_page(dev->dev, dma_dest, len, DMA_FROM_DEVICE); return -ENOMEM; + } tx->ack = 1; tx->callback = NULL; - addr = dma_map_single(dev->dev, kdata, len, DMA_TO_DEVICE); - tx->tx_set_src(addr, tx, 0); - addr = dma_map_page(dev->dev, page, offset, len, DMA_FROM_DEVICE); - tx->tx_set_dest(addr, tx, 0); cookie = tx->tx_submit(tx); cpu = get_cpu(); @@ -563,20 +567,23 @@ dma_async_memcpy_pg_to_pg(struct dma_chan *chan, struct page *dest_pg, { struct dma_device *dev = chan->device; struct dma_async_tx_descriptor *tx; - dma_addr_t addr; + dma_addr_t dma_dest, dma_src; dma_cookie_t cookie; int cpu; - tx = dev->device_prep_dma_memcpy(chan, len, 0); - if (!tx) + dma_src = dma_map_page(dev->dev, src_pg, src_off, len, DMA_TO_DEVICE); + dma_dest = dma_map_page(dev->dev, dest_pg, dest_off, len, + DMA_FROM_DEVICE); + tx = dev->device_prep_dma_memcpy(chan, dma_dest, dma_src, len, 0); + + if (!tx) { + dma_unmap_page(dev->dev, dma_src, len, DMA_TO_DEVICE); + dma_unmap_page(dev->dev, dma_dest, len, DMA_FROM_DEVICE); return -ENOMEM; + } tx->ack = 1; tx->callback = NULL; - addr = dma_map_page(dev->dev, src_pg, src_off, len, DMA_TO_DEVICE); - tx->tx_set_src(addr, tx, 0); - addr = dma_map_page(dev->dev, dest_pg, dest_off, len, DMA_FROM_DEVICE); - tx->tx_set_dest(addr, tx, 0); cookie = tx->tx_submit(tx); cpu = get_cpu(); diff --git a/drivers/dma/ioat_dma.c b/drivers/dma/ioat_dma.c index 45e7b4666c7..dff38accc5c 100644 --- a/drivers/dma/ioat_dma.c +++ b/drivers/dma/ioat_dma.c @@ -159,20 +159,6 @@ static int ioat_dma_enumerate_channels(struct ioatdma_device *device) return device->common.chancnt; } -static void ioat_set_src(dma_addr_t addr, - struct dma_async_tx_descriptor *tx, - int index) -{ - tx_to_ioat_desc(tx)->src = addr; -} - -static void ioat_set_dest(dma_addr_t addr, - struct dma_async_tx_descriptor *tx, - int index) -{ - tx_to_ioat_desc(tx)->dst = addr; -} - /** * ioat_dma_memcpy_issue_pending - push potentially unrecognized appended * descriptors to hw @@ -415,8 +401,6 @@ static struct ioat_desc_sw *ioat_dma_alloc_descriptor( memset(desc, 0, sizeof(*desc)); dma_async_tx_descriptor_init(&desc_sw->async_tx, &ioat_chan->common); - desc_sw->async_tx.tx_set_src = ioat_set_src; - desc_sw->async_tx.tx_set_dest = ioat_set_dest; switch (ioat_chan->device->version) { case IOAT_VER_1_2: desc_sw->async_tx.tx_submit = ioat1_tx_submit; @@ -714,8 +698,10 @@ static struct ioat_desc_sw *ioat_dma_get_next_descriptor( static struct dma_async_tx_descriptor *ioat1_dma_prep_memcpy( struct dma_chan *chan, + dma_addr_t dma_dest, + dma_addr_t dma_src, size_t len, - int int_en) + unsigned long flags) { struct ioat_dma_chan *ioat_chan = to_ioat_chan(chan); struct ioat_desc_sw *new; @@ -726,6 +712,8 @@ static struct dma_async_tx_descriptor *ioat1_dma_prep_memcpy( if (new) { new->len = len; + new->dst = dma_dest; + new->src = dma_src; return &new->async_tx; } else return NULL; @@ -733,8 +721,10 @@ static struct dma_async_tx_descriptor *ioat1_dma_prep_memcpy( static struct dma_async_tx_descriptor *ioat2_dma_prep_memcpy( struct dma_chan *chan, + dma_addr_t dma_dest, + dma_addr_t dma_src, size_t len, - int int_en) + unsigned long flags) { struct ioat_dma_chan *ioat_chan = to_ioat_chan(chan); struct ioat_desc_sw *new; @@ -749,6 +739,8 @@ static struct dma_async_tx_descriptor *ioat2_dma_prep_memcpy( if (new) { new->len = len; + new->dst = dma_dest; + new->src = dma_src; return &new->async_tx; } else return NULL; @@ -1045,7 +1037,7 @@ static int ioat_dma_self_test(struct ioatdma_device *device) u8 *dest; struct dma_chan *dma_chan; struct dma_async_tx_descriptor *tx; - dma_addr_t addr; + dma_addr_t dma_dest, dma_src; dma_cookie_t cookie; int err = 0; @@ -1073,7 +1065,12 @@ static int ioat_dma_self_test(struct ioatdma_device *device) goto out; } - tx = device->common.device_prep_dma_memcpy(dma_chan, IOAT_TEST_SIZE, 0); + dma_src = dma_map_single(dma_chan->device->dev, src, IOAT_TEST_SIZE, + DMA_TO_DEVICE); + dma_dest = dma_map_single(dma_chan->device->dev, dest, IOAT_TEST_SIZE, + DMA_FROM_DEVICE); + tx = device->common.device_prep_dma_memcpy(dma_chan, dma_dest, dma_src, + IOAT_TEST_SIZE, 0); if (!tx) { dev_err(&device->pdev->dev, "Self-test prep failed, disabling\n"); @@ -1082,12 +1079,6 @@ static int ioat_dma_self_test(struct ioatdma_device *device) } async_tx_ack(tx); - addr = dma_map_single(dma_chan->device->dev, src, IOAT_TEST_SIZE, - DMA_TO_DEVICE); - tx->tx_set_src(addr, tx, 0); - addr = dma_map_single(dma_chan->device->dev, dest, IOAT_TEST_SIZE, - DMA_FROM_DEVICE); - tx->tx_set_dest(addr, tx, 0); tx->callback = ioat_dma_test_callback; tx->callback_param = (void *)0x8086; cookie = tx->tx_submit(tx); diff --git a/drivers/dma/iop-adma.c b/drivers/dma/iop-adma.c index e5c62b75f36..3986d54492b 100644 --- a/drivers/dma/iop-adma.c +++ b/drivers/dma/iop-adma.c @@ -284,7 +284,7 @@ iop_adma_alloc_slots(struct iop_adma_chan *iop_chan, int num_slots, int slots_per_op) { struct iop_adma_desc_slot *iter, *_iter, *alloc_start = NULL; - struct list_head chain = LIST_HEAD_INIT(chain); + LIST_HEAD(chain); int slots_found, retry = 0; /* start search from the last allocated descrtiptor @@ -443,17 +443,6 @@ iop_adma_tx_submit(struct dma_async_tx_descriptor *tx) return cookie; } -static void -iop_adma_set_dest(dma_addr_t addr, struct dma_async_tx_descriptor *tx, - int index) -{ - struct iop_adma_desc_slot *sw_desc = tx_to_iop_adma_slot(tx); - struct iop_adma_chan *iop_chan = to_iop_adma_chan(tx->chan); - - /* to do: support transfers lengths > IOP_ADMA_MAX_BYTE_COUNT */ - iop_desc_set_dest_addr(sw_desc->group_head, iop_chan, addr); -} - static void iop_chan_start_null_memcpy(struct iop_adma_chan *iop_chan); static void iop_chan_start_null_xor(struct iop_adma_chan *iop_chan); @@ -486,7 +475,6 @@ static int iop_adma_alloc_chan_resources(struct dma_chan *chan) dma_async_tx_descriptor_init(&slot->async_tx, chan); slot->async_tx.tx_submit = iop_adma_tx_submit; - slot->async_tx.tx_set_dest = iop_adma_set_dest; INIT_LIST_HEAD(&slot->chain_node); INIT_LIST_HEAD(&slot->slot_node); INIT_LIST_HEAD(&slot->async_tx.tx_list); @@ -547,18 +535,9 @@ iop_adma_prep_dma_interrupt(struct dma_chan *chan) return sw_desc ? &sw_desc->async_tx : NULL; } -static void -iop_adma_memcpy_set_src(dma_addr_t addr, struct dma_async_tx_descriptor *tx, - int index) -{ - struct iop_adma_desc_slot *sw_desc = tx_to_iop_adma_slot(tx); - struct iop_adma_desc_slot *grp_start = sw_desc->group_head; - - iop_desc_set_memcpy_src_addr(grp_start, addr); -} - static struct dma_async_tx_descriptor * -iop_adma_prep_dma_memcpy(struct dma_chan *chan, size_t len, int int_en) +iop_adma_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dma_dest, + dma_addr_t dma_src, size_t len, unsigned long flags) { struct iop_adma_chan *iop_chan = to_iop_adma_chan(chan); struct iop_adma_desc_slot *sw_desc, *grp_start; @@ -576,11 +555,12 @@ iop_adma_prep_dma_memcpy(struct dma_chan *chan, size_t len, int int_en) sw_desc = iop_adma_alloc_slots(iop_chan, slot_cnt, slots_per_op); if (sw_desc) { grp_start = sw_desc->group_head; - iop_desc_init_memcpy(grp_start, int_en); + iop_desc_init_memcpy(grp_start, flags); iop_desc_set_byte_count(grp_start, iop_chan, len); + iop_desc_set_dest_addr(grp_start, iop_chan, dma_dest); + iop_desc_set_memcpy_src_addr(grp_start, dma_src); sw_desc->unmap_src_cnt = 1; sw_desc->unmap_len = len; - sw_desc->async_tx.tx_set_src = iop_adma_memcpy_set_src; } spin_unlock_bh(&iop_chan->lock); @@ -588,8 +568,8 @@ iop_adma_prep_dma_memcpy(struct dma_chan *chan, size_t len, int int_en) } static struct dma_async_tx_descriptor * -iop_adma_prep_dma_memset(struct dma_chan *chan, int value, size_t len, - int int_en) +iop_adma_prep_dma_memset(struct dma_chan *chan, dma_addr_t dma_dest, + int value, size_t len, unsigned long flags) { struct iop_adma_chan *iop_chan = to_iop_adma_chan(chan); struct iop_adma_desc_slot *sw_desc, *grp_start; @@ -607,9 +587,10 @@ iop_adma_prep_dma_memset(struct dma_chan *chan, int value, size_t len, sw_desc = iop_adma_alloc_slots(iop_chan, slot_cnt, slots_per_op); if (sw_desc) { grp_start = sw_desc->group_head; - iop_desc_init_memset(grp_start, int_en); + iop_desc_init_memset(grp_start, flags); iop_desc_set_byte_count(grp_start, iop_chan, len); iop_desc_set_block_fill_val(grp_start, value); + iop_desc_set_dest_addr(grp_start, iop_chan, dma_dest); sw_desc->unmap_src_cnt = 1; sw_desc->unmap_len = len; } @@ -618,19 +599,10 @@ iop_adma_prep_dma_memset(struct dma_chan *chan, int value, size_t len, return sw_desc ? &sw_desc->async_tx : NULL; } -static void -iop_adma_xor_set_src(dma_addr_t addr, struct dma_async_tx_descriptor *tx, - int index) -{ - struct iop_adma_desc_slot *sw_desc = tx_to_iop_adma_slot(tx); - struct iop_adma_desc_slot *grp_start = sw_desc->group_head; - - iop_desc_set_xor_src_addr(grp_start, index, addr); -} - static struct dma_async_tx_descriptor * -iop_adma_prep_dma_xor(struct dma_chan *chan, unsigned int src_cnt, size_t len, - int int_en) +iop_adma_prep_dma_xor(struct dma_chan *chan, dma_addr_t dma_dest, + dma_addr_t *dma_src, unsigned int src_cnt, size_t len, + unsigned long flags) { struct iop_adma_chan *iop_chan = to_iop_adma_chan(chan); struct iop_adma_desc_slot *sw_desc, *grp_start; @@ -641,39 +613,32 @@ iop_adma_prep_dma_xor(struct dma_chan *chan, unsigned int src_cnt, size_t len, BUG_ON(unlikely(len > IOP_ADMA_XOR_MAX_BYTE_COUNT)); dev_dbg(iop_chan->device->common.dev, - "%s src_cnt: %d len: %u int_en: %d\n", - __FUNCTION__, src_cnt, len, int_en); + "%s src_cnt: %d len: %u flags: %lx\n", + __FUNCTION__, src_cnt, len, flags); spin_lock_bh(&iop_chan->lock); slot_cnt = iop_chan_xor_slot_count(len, src_cnt, &slots_per_op); sw_desc = iop_adma_alloc_slots(iop_chan, slot_cnt, slots_per_op); if (sw_desc) { grp_start = sw_desc->group_head; - iop_desc_init_xor(grp_start, src_cnt, int_en); + iop_desc_init_xor(grp_start, src_cnt, flags); iop_desc_set_byte_count(grp_start, iop_chan, len); + iop_desc_set_dest_addr(grp_start, iop_chan, dma_dest); sw_desc->unmap_src_cnt = src_cnt; sw_desc->unmap_len = len; - sw_desc->async_tx.tx_set_src = iop_adma_xor_set_src; + while (src_cnt--) + iop_desc_set_xor_src_addr(grp_start, src_cnt, + dma_src[src_cnt]); } spin_unlock_bh(&iop_chan->lock); return sw_desc ? &sw_desc->async_tx : NULL; } -static void -iop_adma_xor_zero_sum_set_src(dma_addr_t addr, - struct dma_async_tx_descriptor *tx, - int index) -{ - struct iop_adma_desc_slot *sw_desc = tx_to_iop_adma_slot(tx); - struct iop_adma_desc_slot *grp_start = sw_desc->group_head; - - iop_desc_set_zero_sum_src_addr(grp_start, index, addr); -} - static struct dma_async_tx_descriptor * -iop_adma_prep_dma_zero_sum(struct dma_chan *chan, unsigned int src_cnt, - size_t len, u32 *result, int int_en) +iop_adma_prep_dma_zero_sum(struct dma_chan *chan, dma_addr_t *dma_src, + unsigned int src_cnt, size_t len, u32 *result, + unsigned long flags) { struct iop_adma_chan *iop_chan = to_iop_adma_chan(chan); struct iop_adma_desc_slot *sw_desc, *grp_start; @@ -690,14 +655,16 @@ iop_adma_prep_dma_zero_sum(struct dma_chan *chan, unsigned int src_cnt, sw_desc = iop_adma_alloc_slots(iop_chan, slot_cnt, slots_per_op); if (sw_desc) { grp_start = sw_desc->group_head; - iop_desc_init_zero_sum(grp_start, src_cnt, int_en); + iop_desc_init_zero_sum(grp_start, src_cnt, flags); iop_desc_set_zero_sum_byte_count(grp_start, len); grp_start->xor_check_result = result; pr_debug("\t%s: grp_start->xor_check_result: %p\n", __FUNCTION__, grp_start->xor_check_result); sw_desc->unmap_src_cnt = src_cnt; sw_desc->unmap_len = len; - sw_desc->async_tx.tx_set_src = iop_adma_xor_zero_sum_set_src; + while (src_cnt--) + iop_desc_set_zero_sum_src_addr(grp_start, src_cnt, + dma_src[src_cnt]); } spin_unlock_bh(&iop_chan->lock); @@ -882,13 +849,12 @@ static int __devinit iop_adma_memcpy_self_test(struct iop_adma_device *device) goto out; } - tx = iop_adma_prep_dma_memcpy(dma_chan, IOP_ADMA_TEST_SIZE, 1); dest_dma = dma_map_single(dma_chan->device->dev, dest, IOP_ADMA_TEST_SIZE, DMA_FROM_DEVICE); - iop_adma_set_dest(dest_dma, tx, 0); src_dma = dma_map_single(dma_chan->device->dev, src, IOP_ADMA_TEST_SIZE, DMA_TO_DEVICE); - iop_adma_memcpy_set_src(src_dma, tx, 0); + tx = iop_adma_prep_dma_memcpy(dma_chan, dest_dma, src_dma, + IOP_ADMA_TEST_SIZE, 1); cookie = iop_adma_tx_submit(tx); iop_adma_issue_pending(dma_chan); @@ -929,6 +895,7 @@ iop_adma_xor_zero_sum_self_test(struct iop_adma_device *device) struct page *dest; struct page *xor_srcs[IOP_ADMA_NUM_SRC_TEST]; struct page *zero_sum_srcs[IOP_ADMA_NUM_SRC_TEST + 1]; + dma_addr_t dma_srcs[IOP_ADMA_NUM_SRC_TEST + 1]; dma_addr_t dma_addr, dest_dma; struct dma_async_tx_descriptor *tx; struct dma_chan *dma_chan; @@ -981,17 +948,13 @@ iop_adma_xor_zero_sum_self_test(struct iop_adma_device *device) } /* test xor */ - tx = iop_adma_prep_dma_xor(dma_chan, IOP_ADMA_NUM_SRC_TEST, - PAGE_SIZE, 1); dest_dma = dma_map_page(dma_chan->device->dev, dest, 0, PAGE_SIZE, DMA_FROM_DEVICE); - iop_adma_set_dest(dest_dma, tx, 0); - - for (i = 0; i < IOP_ADMA_NUM_SRC_TEST; i++) { - dma_addr = dma_map_page(dma_chan->device->dev, xor_srcs[i], 0, - PAGE_SIZE, DMA_TO_DEVICE); - iop_adma_xor_set_src(dma_addr, tx, i); - } + for (i = 0; i < IOP_ADMA_NUM_SRC_TEST; i++) + dma_srcs[i] = dma_map_page(dma_chan->device->dev, xor_srcs[i], + 0, PAGE_SIZE, DMA_TO_DEVICE); + tx = iop_adma_prep_dma_xor(dma_chan, dest_dma, dma_srcs, + IOP_ADMA_NUM_SRC_TEST, PAGE_SIZE, 1); cookie = iop_adma_tx_submit(tx); iop_adma_issue_pending(dma_chan); @@ -1032,13 +995,13 @@ iop_adma_xor_zero_sum_self_test(struct iop_adma_device *device) zero_sum_result = 1; - tx = iop_adma_prep_dma_zero_sum(dma_chan, IOP_ADMA_NUM_SRC_TEST + 1, - PAGE_SIZE, &zero_sum_result, 1); - for (i = 0; i < IOP_ADMA_NUM_SRC_TEST + 1; i++) { - dma_addr = dma_map_page(dma_chan->device->dev, zero_sum_srcs[i], - 0, PAGE_SIZE, DMA_TO_DEVICE); - iop_adma_xor_zero_sum_set_src(dma_addr, tx, i); - } + for (i = 0; i < IOP_ADMA_NUM_SRC_TEST + 1; i++) + dma_srcs[i] = dma_map_page(dma_chan->device->dev, + zero_sum_srcs[i], 0, PAGE_SIZE, + DMA_TO_DEVICE); + tx = iop_adma_prep_dma_zero_sum(dma_chan, dma_srcs, + IOP_ADMA_NUM_SRC_TEST + 1, PAGE_SIZE, + &zero_sum_result, 1); cookie = iop_adma_tx_submit(tx); iop_adma_issue_pending(dma_chan); @@ -1060,10 +1023,9 @@ iop_adma_xor_zero_sum_self_test(struct iop_adma_device *device) } /* test memset */ - tx = iop_adma_prep_dma_memset(dma_chan, 0, PAGE_SIZE, 1); dma_addr = dma_map_page(dma_chan->device->dev, dest, 0, PAGE_SIZE, DMA_FROM_DEVICE); - iop_adma_set_dest(dma_addr, tx, 0); + tx = iop_adma_prep_dma_memset(dma_chan, dma_addr, 0, PAGE_SIZE, 1); cookie = iop_adma_tx_submit(tx); iop_adma_issue_pending(dma_chan); @@ -1089,13 +1051,13 @@ iop_adma_xor_zero_sum_self_test(struct iop_adma_device *device) /* test for non-zero parity sum */ zero_sum_result = 0; - tx = iop_adma_prep_dma_zero_sum(dma_chan, IOP_ADMA_NUM_SRC_TEST + 1, - PAGE_SIZE, &zero_sum_result, 1); - for (i = 0; i < IOP_ADMA_NUM_SRC_TEST + 1; i++) { - dma_addr = dma_map_page(dma_chan->device->dev, zero_sum_srcs[i], - 0, PAGE_SIZE, DMA_TO_DEVICE); - iop_adma_xor_zero_sum_set_src(dma_addr, tx, i); - } + for (i = 0; i < IOP_ADMA_NUM_SRC_TEST + 1; i++) + dma_srcs[i] = dma_map_page(dma_chan->device->dev, + zero_sum_srcs[i], 0, PAGE_SIZE, + DMA_TO_DEVICE); + tx = iop_adma_prep_dma_zero_sum(dma_chan, dma_srcs, + IOP_ADMA_NUM_SRC_TEST + 1, PAGE_SIZE, + &zero_sum_result, 1); cookie = iop_adma_tx_submit(tx); iop_adma_issue_pending(dma_chan); diff --git a/drivers/firmware/dcdbas.c b/drivers/firmware/dcdbas.c index 18cdcb3ae1c..1636806ec55 100644 --- a/drivers/firmware/dcdbas.c +++ b/drivers/firmware/dcdbas.c @@ -658,4 +658,5 @@ MODULE_DESCRIPTION(DRIVER_DESCRIPTION " (version " DRIVER_VERSION ")"); MODULE_VERSION(DRIVER_VERSION); MODULE_AUTHOR("Dell Inc."); MODULE_LICENSE("GPL"); - +/* Any System or BIOS claiming to be by Dell */ +MODULE_ALIAS("dmi:*:[bs]vnD[Ee][Ll][Ll]*:*"); diff --git a/drivers/firmware/dmi-id.c b/drivers/firmware/dmi-id.c index 313c99cbdc6..e880d6c8d89 100644 --- a/drivers/firmware/dmi-id.c +++ b/drivers/firmware/dmi-id.c @@ -11,7 +11,6 @@ #include <linux/init.h> #include <linux/dmi.h> #include <linux/device.h> -#include <linux/autoconf.h> struct dmi_device_attribute{ struct device_attribute dev_attr; diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 74fac0f5c34..bbd28342e77 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -27,15 +27,16 @@ config DEBUG_GPIO comment "I2C GPIO expanders:" -config GPIO_PCA9539 - tristate "PCA9539 16-bit I/O port" +config GPIO_PCA953X + tristate "PCA953x I/O ports" depends on I2C help - Say yes here to support the PCA9539 16-bit I/O port. These - parts are made by NXP and TI. + Say yes here to support the PCA9534 (8-bit), PCA9535 (16-bit), + PCA9536 (4-bit), PCA9537 (4-bit), PCA9538 (8-bit), and PCA9539 + (16-bit) I/O ports. These parts are made by NXP and TI. This driver can also be built as a module. If so, the module - will be called pca9539. + will be called pca953x. config GPIO_PCF857X tristate "PCF857x, PCA857x, and PCA967x I2C GPIO expanders" diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 470ecd6aa77..fdde9923cf3 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -5,5 +5,5 @@ ccflags-$(CONFIG_DEBUG_GPIO) += -DDEBUG obj-$(CONFIG_HAVE_GPIO_LIB) += gpiolib.o obj-$(CONFIG_GPIO_MCP23S08) += mcp23s08.o -obj-$(CONFIG_GPIO_PCA9539) += pca9539.o +obj-$(CONFIG_GPIO_PCA953X) += pca953x.o obj-$(CONFIG_GPIO_PCF857X) += pcf857x.o diff --git a/drivers/gpio/pca9539.c b/drivers/gpio/pca9539.c deleted file mode 100644 index 3e85c92a7d5..00000000000 --- a/drivers/gpio/pca9539.c +++ /dev/null @@ -1,271 +0,0 @@ -/* - * pca9539.c - 16-bit I/O port with interrupt and reset - * - * Copyright (C) 2005 Ben Gardner <bgardner@wabtec.com> - * Copyright (C) 2007 Marvell International Ltd. - * - * Derived from drivers/i2c/chips/pca9539.c - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/i2c.h> -#include <linux/i2c/pca9539.h> - -#include <asm/gpio.h> - - -#define NR_PCA9539_GPIOS 16 - -#define PCA9539_INPUT 0 -#define PCA9539_OUTPUT 2 -#define PCA9539_INVERT 4 -#define PCA9539_DIRECTION 6 - -struct pca9539_chip { - unsigned gpio_start; - uint16_t reg_output; - uint16_t reg_direction; - - struct i2c_client *client; - struct gpio_chip gpio_chip; -}; - -/* NOTE: we can't currently rely on fault codes to come from SMBus - * calls, so we map all errors to EIO here and return zero otherwise. - */ -static int pca9539_write_reg(struct pca9539_chip *chip, int reg, uint16_t val) -{ - if (i2c_smbus_write_word_data(chip->client, reg, val) < 0) - return -EIO; - else - return 0; -} - -static int pca9539_read_reg(struct pca9539_chip *chip, int reg, uint16_t *val) -{ - int ret; - - ret = i2c_smbus_read_word_data(chip->client, reg); - if (ret < 0) { - dev_err(&chip->client->dev, "failed reading register\n"); - return -EIO; - } - - *val = (uint16_t)ret; - return 0; -} - -static int pca9539_gpio_direction_input(struct gpio_chip *gc, unsigned off) -{ - struct pca9539_chip *chip; - uint16_t reg_val; - int ret; - - chip = container_of(gc, struct pca9539_chip, gpio_chip); - - reg_val = chip->reg_direction | (1u << off); - ret = pca9539_write_reg(chip, PCA9539_DIRECTION, reg_val); - if (ret) - return ret; - - chip->reg_direction = reg_val; - return 0; -} - -static int pca9539_gpio_direction_output(struct gpio_chip *gc, - unsigned off, int val) -{ - struct pca9539_chip *chip; - uint16_t reg_val; - int ret; - - chip = container_of(gc, struct pca9539_chip, gpio_chip); - - /* set output level */ - if (val) - reg_val = chip->reg_output | (1u << off); - else - reg_val = chip->reg_output & ~(1u << off); - - ret = pca9539_write_reg(chip, PCA9539_OUTPUT, reg_val); - if (ret) - return ret; - - chip->reg_output = reg_val; - - /* then direction */ - reg_val = chip->reg_direction & ~(1u << off); - ret = pca9539_write_reg(chip, PCA9539_DIRECTION, reg_val); - if (ret) - return ret; - - chip->reg_direction = reg_val; - return 0; -} - -static int pca9539_gpio_get_value(struct gpio_chip *gc, unsigned off) -{ - struct pca9539_chip *chip; - uint16_t reg_val; - int ret; - - chip = container_of(gc, struct pca9539_chip, gpio_chip); - - ret = pca9539_read_reg(chip, PCA9539_INPUT, ®_val); - if (ret < 0) { - /* NOTE: diagnostic already emitted; that's all we should - * do unless gpio_*_value_cansleep() calls become different - * from their nonsleeping siblings (and report faults). - */ - return 0; - } - - return (reg_val & (1u << off)) ? 1 : 0; -} - -static void pca9539_gpio_set_value(struct gpio_chip *gc, unsigned off, int val) -{ - struct pca9539_chip *chip; - uint16_t reg_val; - int ret; - - chip = container_of(gc, struct pca9539_chip, gpio_chip); - - if (val) - reg_val = chip->reg_output | (1u << off); - else - reg_val = chip->reg_output & ~(1u << off); - - ret = pca9539_write_reg(chip, PCA9539_OUTPUT, reg_val); - if (ret) - return; - - chip->reg_output = reg_val; -} - -static int pca9539_init_gpio(struct pca9539_chip *chip) -{ - struct gpio_chip *gc; - - gc = &chip->gpio_chip; - - gc->direction_input = pca9539_gpio_direction_input; - gc->direction_output = pca9539_gpio_direction_output; - gc->get = pca9539_gpio_get_value; - gc->set = pca9539_gpio_set_value; - - gc->base = chip->gpio_start; - gc->ngpio = NR_PCA9539_GPIOS; - gc->label = "pca9539"; - - return gpiochip_add(gc); -} - -static int __devinit pca9539_probe(struct i2c_client *client) -{ - struct pca9539_platform_data *pdata; - struct pca9539_chip *chip; - int ret; - - pdata = client->dev.platform_data; - if (pdata == NULL) - return -ENODEV; - - chip = kzalloc(sizeof(struct pca9539_chip), GFP_KERNEL); - if (chip == NULL) - return -ENOMEM; - - chip->client = client; - - chip->gpio_start = pdata->gpio_base; - - /* initialize cached registers from their original values. - * we can't share this chip with another i2c master. - */ - ret = pca9539_read_reg(chip, PCA9539_OUTPUT, &chip->reg_output); - if (ret) - goto out_failed; - - ret = pca9539_read_reg(chip, PCA9539_DIRECTION, &chip->reg_direction); - if (ret) - goto out_failed; - - /* set platform specific polarity inversion */ - ret = pca9539_write_reg(chip, PCA9539_INVERT, pdata->invert); - if (ret) - goto out_failed; - - ret = pca9539_init_gpio(chip); - if (ret) - goto out_failed; - - if (pdata->setup) { - ret = pdata->setup(client, chip->gpio_chip.base, - chip->gpio_chip.ngpio, pdata->context); - if (ret < 0) - dev_warn(&client->dev, "setup failed, %d\n", ret); - } - - i2c_set_clientdata(client, chip); - return 0; - -out_failed: - kfree(chip); - return ret; -} - -static int pca9539_remove(struct i2c_client *client) -{ - struct pca9539_platform_data *pdata = client->dev.platform_data; - struct pca9539_chip *chip = i2c_get_clientdata(client); - int ret = 0; - - if (pdata->teardown) { - ret = pdata->teardown(client, chip->gpio_chip.base, - chip->gpio_chip.ngpio, pdata->context); - if (ret < 0) { - dev_err(&client->dev, "%s failed, %d\n", - "teardown", ret); - return ret; - } - } - - ret = gpiochip_remove(&chip->gpio_chip); - if (ret) { - dev_err(&client->dev, "%s failed, %d\n", - "gpiochip_remove()", ret); - return ret; - } - - kfree(chip); - return 0; -} - -static struct i2c_driver pca9539_driver = { - .driver = { - .name = "pca9539", - }, - .probe = pca9539_probe, - .remove = pca9539_remove, -}; - -static int __init pca9539_init(void) -{ - return i2c_add_driver(&pca9539_driver); -} -module_init(pca9539_init); - -static void __exit pca9539_exit(void) -{ - i2c_del_driver(&pca9539_driver); -} -module_exit(pca9539_exit); - -MODULE_AUTHOR("eric miao <eric.miao@marvell.com>"); -MODULE_DESCRIPTION("GPIO expander driver for PCA9539"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/pca953x.c b/drivers/gpio/pca953x.c new file mode 100644 index 00000000000..92583cd4bff --- /dev/null +++ b/drivers/gpio/pca953x.c @@ -0,0 +1,308 @@ +/* + * pca953x.c - 4/8/16 bit I/O ports + * + * Copyright (C) 2005 Ben Gardner <bgardner@wabtec.com> + * Copyright (C) 2007 Marvell International Ltd. + * + * Derived from drivers/i2c/chips/pca9539.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/i2c/pca953x.h> + +#include <asm/gpio.h> + +#define PCA953X_INPUT 0 +#define PCA953X_OUTPUT 1 +#define PCA953X_INVERT 2 +#define PCA953X_DIRECTION 3 + +/* This is temporary - in 2.6.26 i2c_driver_data should replace it. */ +struct pca953x_desc { + char name[I2C_NAME_SIZE]; + unsigned long driver_data; +}; + +static const struct pca953x_desc pca953x_descs[] = { + { "pca9534", 8, }, + { "pca9535", 16, }, + { "pca9536", 4, }, + { "pca9537", 4, }, + { "pca9538", 8, }, + { "pca9539", 16, }, + /* REVISIT several pca955x parts should work here too */ +}; + +struct pca953x_chip { + unsigned gpio_start; + uint16_t reg_output; + uint16_t reg_direction; + + struct i2c_client *client; + struct gpio_chip gpio_chip; +}; + +/* NOTE: we can't currently rely on fault codes to come from SMBus + * calls, so we map all errors to EIO here and return zero otherwise. + */ +static int pca953x_write_reg(struct pca953x_chip *chip, int reg, uint16_t val) +{ + int ret; + + if (chip->gpio_chip.ngpio <= 8) + ret = i2c_smbus_write_byte_data(chip->client, reg, val); + else + ret = i2c_smbus_write_word_data(chip->client, reg << 1, val); + + if (ret < 0) { + dev_err(&chip->client->dev, "failed writing register\n"); + return -EIO; + } + + return 0; +} + +static int pca953x_read_reg(struct pca953x_chip *chip, int reg, uint16_t *val) +{ + int ret; + + if (chip->gpio_chip.ngpio <= 8) + ret = i2c_smbus_read_byte_data(chip->client, reg); + else + ret = i2c_smbus_read_word_data(chip->client, reg << 1); + + if (ret < 0) { + dev_err(&chip->client->dev, "failed reading register\n"); + return -EIO; + } + + *val = (uint16_t)ret; + return 0; +} + +static int pca953x_gpio_direction_input(struct gpio_chip *gc, unsigned off) +{ + struct pca953x_chip *chip; + uint16_t reg_val; + int ret; + + chip = container_of(gc, struct pca953x_chip, gpio_chip); + + reg_val = chip->reg_direction | (1u << off); + ret = pca953x_write_reg(chip, PCA953X_DIRECTION, reg_val); + if (ret) + return ret; + + chip->reg_direction = reg_val; + return 0; +} + +static int pca953x_gpio_direction_output(struct gpio_chip *gc, + unsigned off, int val) +{ + struct pca953x_chip *chip; + uint16_t reg_val; + int ret; + + chip = container_of(gc, struct pca953x_chip, gpio_chip); + + /* set output level */ + if (val) + reg_val = chip->reg_output | (1u << off); + else + reg_val = chip->reg_output & ~(1u << off); + + ret = pca953x_write_reg(chip, PCA953X_OUTPUT, reg_val); + if (ret) + return ret; + + chip->reg_output = reg_val; + + /* then direction */ + reg_val = chip->reg_direction & ~(1u << off); + ret = pca953x_write_reg(chip, PCA953X_DIRECTION, reg_val); + if (ret) + return ret; + + chip->reg_direction = reg_val; + return 0; +} + +static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off) +{ + struct pca953x_chip *chip; + uint16_t reg_val; + int ret; + + chip = container_of(gc, struct pca953x_chip, gpio_chip); + + ret = pca953x_read_reg(chip, PCA953X_INPUT, ®_val); + if (ret < 0) { + /* NOTE: diagnostic already emitted; that's all we should + * do unless gpio_*_value_cansleep() calls become different + * from their nonsleeping siblings (and report faults). + */ + return 0; + } + + return (reg_val & (1u << off)) ? 1 : 0; +} + +static void pca953x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val) +{ + struct pca953x_chip *chip; + uint16_t reg_val; + int ret; + + chip = container_of(gc, struct pca953x_chip, gpio_chip); + + if (val) + reg_val = chip->reg_output | (1u << off); + else + reg_val = chip->reg_output & ~(1u << off); + + ret = pca953x_write_reg(chip, PCA953X_OUTPUT, reg_val); + if (ret) + return; + + chip->reg_output = reg_val; +} + +static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios) +{ + struct gpio_chip *gc; + + gc = &chip->gpio_chip; + + gc->direction_input = pca953x_gpio_direction_input; + gc->direction_output = pca953x_gpio_direction_output; + gc->get = pca953x_gpio_get_value; + gc->set = pca953x_gpio_set_value; + + gc->base = chip->gpio_start; + gc->ngpio = gpios; + gc->label = chip->client->name; +} + +static int __devinit pca953x_probe(struct i2c_client *client) +{ + struct pca953x_platform_data *pdata; + struct pca953x_chip *chip; + int ret, i; + const struct pca953x_desc *id = NULL; + + pdata = client->dev.platform_data; + if (pdata == NULL) + return -ENODEV; + + /* this loop vanishes when we get i2c_device_id */ + for (i = 0; i < ARRAY_SIZE(pca953x_descs); i++) + if (!strcmp(pca953x_descs[i].name, client->name)) { + id = pca953x_descs + i; + break; + } + if (!id) + return -ENODEV; + + chip = kzalloc(sizeof(struct pca953x_chip), GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + + chip->client = client; + + chip->gpio_start = pdata->gpio_base; + + /* initialize cached registers from their original values. + * we can't share this chip with another i2c master. + */ + pca953x_setup_gpio(chip, id->driver_data); + + ret = pca953x_read_reg(chip, PCA953X_OUTPUT, &chip->reg_output); + if (ret) + goto out_failed; + + ret = pca953x_read_reg(chip, PCA953X_DIRECTION, &chip->reg_direction); + if (ret) + goto out_failed; + + /* set platform specific polarity inversion */ + ret = pca953x_write_reg(chip, PCA953X_INVERT, pdata->invert); + if (ret) + goto out_failed; + + + ret = gpiochip_add(&chip->gpio_chip); + if (ret) + goto out_failed; + + if (pdata->setup) { + ret = pdata->setup(client, chip->gpio_chip.base, + chip->gpio_chip.ngpio, pdata->context); + if (ret < 0) + dev_warn(&client->dev, "setup failed, %d\n", ret); + } + + i2c_set_clientdata(client, chip); + return 0; + +out_failed: + kfree(chip); + return ret; +} + +static int pca953x_remove(struct i2c_client *client) +{ + struct pca953x_platform_data *pdata = client->dev.platform_data; + struct pca953x_chip *chip = i2c_get_clientdata(client); + int ret = 0; + + if (pdata->teardown) { + ret = pdata->teardown(client, chip->gpio_chip.base, + chip->gpio_chip.ngpio, pdata->context); + if (ret < 0) { + dev_err(&client->dev, "%s failed, %d\n", + "teardown", ret); + return ret; + } + } + + ret = gpiochip_remove(&chip->gpio_chip); + if (ret) { + dev_err(&client->dev, "%s failed, %d\n", + "gpiochip_remove()", ret); + return ret; + } + + kfree(chip); + return 0; +} + +static struct i2c_driver pca953x_driver = { + .driver = { + .name = "pca953x", + }, + .probe = pca953x_probe, + .remove = pca953x_remove, +}; + +static int __init pca953x_init(void) +{ + return i2c_add_driver(&pca953x_driver); +} +module_init(pca953x_init); + +static void __exit pca953x_exit(void) +{ + i2c_del_driver(&pca953x_driver); +} +module_exit(pca953x_exit); + +MODULE_AUTHOR("eric miao <eric.miao@marvell.com>"); +MODULE_DESCRIPTION("GPIO expander driver for PCA953x"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index fd0ef826895..6daea896c5d 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -1049,7 +1049,7 @@ static int init_irq (ide_hwif_t *hwif) */ if (!match || match->irq != hwif->irq) { int sa = 0; -#if defined(__mc68000__) || defined(CONFIG_APUS) +#if defined(__mc68000__) sa = IRQF_SHARED; #endif /* __mc68000__ || CONFIG_APUS */ @@ -1072,7 +1072,7 @@ static int init_irq (ide_hwif_t *hwif) hwif->rqsize = 65536; } -#if !defined(__mc68000__) && !defined(CONFIG_APUS) +#if !defined(__mc68000__) printk("%s at 0x%03lx-0x%03lx,0x%03lx on irq %d", hwif->name, hwif->io_ports[IDE_DATA_OFFSET], hwif->io_ports[IDE_DATA_OFFSET]+7, @@ -1080,7 +1080,7 @@ static int init_irq (ide_hwif_t *hwif) #else printk("%s at 0x%08lx on irq %d", hwif->name, hwif->io_ports[IDE_DATA_OFFSET], hwif->irq); -#endif /* __mc68000__ && CONFIG_APUS */ +#endif /* __mc68000__ */ if (match) printk(" (%sed with %s)", hwif->sharing_irq ? "shar" : "serializ", match->name); diff --git a/drivers/ide/legacy/ide_platform.c b/drivers/ide/legacy/ide_platform.c index 26c82ce602d..688fcae1748 100644 --- a/drivers/ide/legacy/ide_platform.c +++ b/drivers/ide/legacy/ide_platform.c @@ -17,7 +17,7 @@ #include <linux/ide.h> #include <linux/ioport.h> #include <linux/module.h> -#include <linux/pata_platform.h> +#include <linux/ata_platform.h> #include <linux/platform_device.h> #include <linux/io.h> diff --git a/drivers/input/touchscreen/h3600_ts_input.c b/drivers/input/touchscreen/h3600_ts_input.c index 2ae6c6016a8..28ae15ed12c 100644 --- a/drivers/input/touchscreen/h3600_ts_input.c +++ b/drivers/input/touchscreen/h3600_ts_input.c @@ -109,7 +109,7 @@ struct h3600_dev { static irqreturn_t action_button_handler(int irq, void *dev_id) { int down = (GPLR & GPIO_BITSY_ACTION_BUTTON) ? 0 : 1; - struct input_dev *dev = (struct input_dev *) dev_id; + struct input_dev *dev = dev_id; input_report_key(dev, KEY_ENTER, down); input_sync(dev); @@ -120,7 +120,7 @@ static irqreturn_t action_button_handler(int irq, void *dev_id) static irqreturn_t npower_button_handler(int irq, void *dev_id) { int down = (GPLR & GPIO_BITSY_NPOWER_BUTTON) ? 0 : 1; - struct input_dev *dev = (struct input_dev *) dev_id; + struct input_dev *dev = dev_id; /* * This interrupt is only called when we release the key. So we have diff --git a/drivers/isdn/act2000/module.c b/drivers/isdn/act2000/module.c index ee2b0b9f8f4..8325022e2be 100644 --- a/drivers/isdn/act2000/module.c +++ b/drivers/isdn/act2000/module.c @@ -310,7 +310,7 @@ act2000_command(act2000_card * card, isdn_ctrl * c) } break; case ISDN_CMD_DIAL: - if (!card->flags & ACT2000_FLAGS_RUNNING) + if (!(card->flags & ACT2000_FLAGS_RUNNING)) return -ENODEV; if (!(chan = find_channel(card, c->arg & 0x0f))) break; @@ -339,7 +339,7 @@ act2000_command(act2000_card * card, isdn_ctrl * c) } return ret; case ISDN_CMD_ACCEPTD: - if (!card->flags & ACT2000_FLAGS_RUNNING) + if (!(card->flags & ACT2000_FLAGS_RUNNING)) return -ENODEV; if (!(chan = find_channel(card, c->arg & 0x0f))) break; @@ -347,11 +347,11 @@ act2000_command(act2000_card * card, isdn_ctrl * c) actcapi_select_b2_protocol_req(card, chan); return 0; case ISDN_CMD_ACCEPTB: - if (!card->flags & ACT2000_FLAGS_RUNNING) + if (!(card->flags & ACT2000_FLAGS_RUNNING)) return -ENODEV; return 0; case ISDN_CMD_HANGUP: - if (!card->flags & ACT2000_FLAGS_RUNNING) + if (!(card->flags & ACT2000_FLAGS_RUNNING)) return -ENODEV; if (!(chan = find_channel(card, c->arg & 0x0f))) break; @@ -366,7 +366,7 @@ act2000_command(act2000_card * card, isdn_ctrl * c) } return 0; case ISDN_CMD_SETEAZ: - if (!card->flags & ACT2000_FLAGS_RUNNING) + if (!(card->flags & ACT2000_FLAGS_RUNNING)) return -ENODEV; if (!(chan = find_channel(card, c->arg & 0x0f))) break; @@ -386,7 +386,7 @@ act2000_command(act2000_card * card, isdn_ctrl * c) actcapi_listen_req(card); return 0; case ISDN_CMD_CLREAZ: - if (!card->flags & ACT2000_FLAGS_RUNNING) + if (!(card->flags & ACT2000_FLAGS_RUNNING)) return -ENODEV; if (!(chan = find_channel(card, c->arg & 0x0f))) break; @@ -394,14 +394,14 @@ act2000_command(act2000_card * card, isdn_ctrl * c) actcapi_listen_req(card); return 0; case ISDN_CMD_SETL2: - if (!card->flags & ACT2000_FLAGS_RUNNING) + if (!(card->flags & ACT2000_FLAGS_RUNNING)) return -ENODEV; if (!(chan = find_channel(card, c->arg & 0x0f))) break; chan->l2prot = (c->arg >> 8); return 0; case ISDN_CMD_SETL3: - if (!card->flags & ACT2000_FLAGS_RUNNING) + if (!(card->flags & ACT2000_FLAGS_RUNNING)) return -ENODEV; if ((c->arg >> 8) != ISDN_PROTO_L3_TRANS) { printk(KERN_WARNING "L3 protocol unknown\n"); @@ -524,7 +524,7 @@ if_writecmd(const u_char __user *buf, int len, int id, int channel) act2000_card *card = act2000_findcard(id); if (card) { - if (!card->flags & ACT2000_FLAGS_RUNNING) + if (!(card->flags & ACT2000_FLAGS_RUNNING)) return -ENODEV; return (len); } @@ -539,7 +539,7 @@ if_readstatus(u_char __user * buf, int len, int id, int channel) act2000_card *card = act2000_findcard(id); if (card) { - if (!card->flags & ACT2000_FLAGS_RUNNING) + if (!(card->flags & ACT2000_FLAGS_RUNNING)) return -ENODEV; return (act2000_readstatus(buf, len, card)); } @@ -554,7 +554,7 @@ if_sendbuf(int id, int channel, int ack, struct sk_buff *skb) act2000_card *card = act2000_findcard(id); if (card) { - if (!card->flags & ACT2000_FLAGS_RUNNING) + if (!(card->flags & ACT2000_FLAGS_RUNNING)) return -ENODEV; return (act2000_sendbuf(card, channel, ack, skb)); } diff --git a/drivers/isdn/gigaset/asyncdata.c b/drivers/isdn/gigaset/asyncdata.c index 00a3be5b862..091deb9d1c4 100644 --- a/drivers/isdn/gigaset/asyncdata.c +++ b/drivers/isdn/gigaset/asyncdata.c @@ -350,8 +350,8 @@ void gigaset_m10x_input(struct inbuf_t *inbuf) unsigned char *src, c; int procbytes; - head = atomic_read(&inbuf->head); - tail = atomic_read(&inbuf->tail); + head = inbuf->head; + tail = inbuf->tail; gig_dbg(DEBUG_INTR, "buffer state: %u -> %u", head, tail); if (head != tail) { @@ -361,7 +361,7 @@ void gigaset_m10x_input(struct inbuf_t *inbuf) gig_dbg(DEBUG_INTR, "processing %u bytes", numbytes); while (numbytes) { - if (atomic_read(&cs->mstate) == MS_LOCKED) { + if (cs->mstate == MS_LOCKED) { procbytes = lock_loop(src, numbytes, inbuf); src += procbytes; numbytes -= procbytes; @@ -436,7 +436,7 @@ nextbyte: } gig_dbg(DEBUG_INTR, "setting head to %u", head); - atomic_set(&inbuf->head, head); + inbuf->head = head; } } EXPORT_SYMBOL_GPL(gigaset_m10x_input); diff --git a/drivers/isdn/gigaset/bas-gigaset.c b/drivers/isdn/gigaset/bas-gigaset.c index af7648274b3..5255b5e20e1 100644 --- a/drivers/isdn/gigaset/bas-gigaset.c +++ b/drivers/isdn/gigaset/bas-gigaset.c @@ -73,6 +73,14 @@ static int gigaset_probe(struct usb_interface *interface, /* Function will be called if the device is unplugged */ static void gigaset_disconnect(struct usb_interface *interface); +/* functions called before/after suspend */ +static int gigaset_suspend(struct usb_interface *intf, pm_message_t message); +static int gigaset_resume(struct usb_interface *intf); + +/* functions called before/after device reset */ +static int gigaset_pre_reset(struct usb_interface *intf); +static int gigaset_post_reset(struct usb_interface *intf); + static int atread_submit(struct cardstate *, int); static void stopurbs(struct bas_bc_state *); static int req_submit(struct bc_state *, int, int, int); @@ -105,8 +113,9 @@ struct bas_cardstate { unsigned char int_in_buf[3]; spinlock_t lock; /* locks all following */ - atomic_t basstate; /* bitmap (BS_*) */ + int basstate; /* bitmap (BS_*) */ int pending; /* uncompleted base request */ + wait_queue_head_t waitqueue; int rcvbuf_size; /* size of AT receive buffer */ /* 0: no receive in progress */ int retry_cmd_in; /* receive req retry count */ @@ -121,10 +130,10 @@ struct bas_cardstate { #define BS_ATTIMER 0x020 /* waiting for HD_READY_SEND_ATDATA */ #define BS_ATRDPEND 0x040 /* urb_cmd_in in use */ #define BS_ATWRPEND 0x080 /* urb_cmd_out in use */ +#define BS_SUSPEND 0x100 /* USB port suspended */ static struct gigaset_driver *driver = NULL; -static struct cardstate *cardstate = NULL; /* usb specific object needed to register this driver with the usb subsystem */ static struct usb_driver gigaset_usb_driver = { @@ -132,6 +141,11 @@ static struct usb_driver gigaset_usb_driver = { .probe = gigaset_probe, .disconnect = gigaset_disconnect, .id_table = gigaset_table, + .suspend = gigaset_suspend, + .resume = gigaset_resume, + .reset_resume = gigaset_post_reset, + .pre_reset = gigaset_pre_reset, + .post_reset = gigaset_post_reset, }; /* get message text for usb_submit_urb return code @@ -248,12 +262,12 @@ static inline void dump_urb(enum debuglevel level, const char *tag, if (urb) { gig_dbg(level, " dev=0x%08lx, pipe=%s:EP%d/DV%d:%s, " - "status=%d, hcpriv=0x%08lx, transfer_flags=0x%x,", + "hcpriv=0x%08lx, transfer_flags=0x%x,", (unsigned long) urb->dev, usb_pipetype_str(urb->pipe), usb_pipeendpoint(urb->pipe), usb_pipedevice(urb->pipe), usb_pipein(urb->pipe) ? "in" : "out", - urb->status, (unsigned long) urb->hcpriv, + (unsigned long) urb->hcpriv, urb->transfer_flags); gig_dbg(level, " transfer_buffer=0x%08lx[%d], actual_length=%d, " @@ -355,27 +369,27 @@ static void check_pending(struct bas_cardstate *ucs) case 0: break; case HD_OPEN_ATCHANNEL: - if (atomic_read(&ucs->basstate) & BS_ATOPEN) + if (ucs->basstate & BS_ATOPEN) ucs->pending = 0; break; case HD_OPEN_B1CHANNEL: - if (atomic_read(&ucs->basstate) & BS_B1OPEN) + if (ucs->basstate & BS_B1OPEN) ucs->pending = 0; break; case HD_OPEN_B2CHANNEL: - if (atomic_read(&ucs->basstate) & BS_B2OPEN) + if (ucs->basstate & BS_B2OPEN) ucs->pending = 0; break; case HD_CLOSE_ATCHANNEL: - if (!(atomic_read(&ucs->basstate) & BS_ATOPEN)) + if (!(ucs->basstate & BS_ATOPEN)) ucs->pending = 0; break; case HD_CLOSE_B1CHANNEL: - if (!(atomic_read(&ucs->basstate) & BS_B1OPEN)) + if (!(ucs->basstate & BS_B1OPEN)) ucs->pending = 0; break; case HD_CLOSE_B2CHANNEL: - if (!(atomic_read(&ucs->basstate) & BS_B2OPEN)) + if (!(ucs->basstate & BS_B2OPEN)) ucs->pending = 0; break; case HD_DEVICE_INIT_ACK: /* no reply expected */ @@ -441,8 +455,8 @@ inline static int update_basstate(struct bas_cardstate *ucs, int state; spin_lock_irqsave(&ucs->lock, flags); - state = atomic_read(&ucs->basstate); - atomic_set(&ucs->basstate, (state & ~clear) | set); + state = ucs->basstate; + ucs->basstate = (state & ~clear) | set; spin_unlock_irqrestore(&ucs->lock, flags); return state; } @@ -459,11 +473,13 @@ static void read_ctrl_callback(struct urb *urb) struct inbuf_t *inbuf = urb->context; struct cardstate *cs = inbuf->cs; struct bas_cardstate *ucs = cs->hw.bas; + int status = urb->status; int have_data = 0; unsigned numbytes; int rc; update_basstate(ucs, 0, BS_ATRDPEND); + wake_up(&ucs->waitqueue); if (!ucs->rcvbuf_size) { dev_warn(cs->dev, "%s: no receive in progress\n", __func__); @@ -472,7 +488,7 @@ static void read_ctrl_callback(struct urb *urb) del_timer(&ucs->timer_cmd_in); - switch (urb->status) { + switch (status) { case 0: /* normal completion */ numbytes = urb->actual_length; if (unlikely(numbytes != ucs->rcvbuf_size)) { @@ -506,12 +522,12 @@ static void read_ctrl_callback(struct urb *urb) case -ESHUTDOWN: /* device shut down */ /* no action necessary */ gig_dbg(DEBUG_USBREQ, "%s: %s", - __func__, get_usb_statmsg(urb->status)); + __func__, get_usb_statmsg(status)); break; default: /* severe trouble */ dev_warn(cs->dev, "control read: %s\n", - get_usb_statmsg(urb->status)); + get_usb_statmsg(status)); if (ucs->retry_cmd_in++ < BAS_RETRY) { dev_notice(cs->dev, "control read: retry %d\n", ucs->retry_cmd_in); @@ -550,17 +566,28 @@ static void read_ctrl_callback(struct urb *urb) static int atread_submit(struct cardstate *cs, int timeout) { struct bas_cardstate *ucs = cs->hw.bas; + int basstate; int ret; gig_dbg(DEBUG_USBREQ, "-------> HD_READ_ATMESSAGE (%d)", ucs->rcvbuf_size); - if (update_basstate(ucs, BS_ATRDPEND, 0) & BS_ATRDPEND) { + basstate = update_basstate(ucs, BS_ATRDPEND, 0); + if (basstate & BS_ATRDPEND) { dev_err(cs->dev, "could not submit HD_READ_ATMESSAGE: URB busy\n"); return -EBUSY; } + if (basstate & BS_SUSPEND) { + dev_notice(cs->dev, + "HD_READ_ATMESSAGE not submitted, " + "suspend in progress\n"); + update_basstate(ucs, 0, BS_ATRDPEND); + /* treat like disconnect */ + return -ENODEV; + } + ucs->dr_cmd_in.bRequestType = IN_VENDOR_REQ; ucs->dr_cmd_in.bRequest = HD_READ_ATMESSAGE; ucs->dr_cmd_in.wValue = 0; @@ -601,12 +628,13 @@ static void read_int_callback(struct urb *urb) struct cardstate *cs = urb->context; struct bas_cardstate *ucs = cs->hw.bas; struct bc_state *bcs; + int status = urb->status; unsigned long flags; int rc; unsigned l; int channel; - switch (urb->status) { + switch (status) { case 0: /* success */ break; case -ENOENT: /* cancelled */ @@ -614,7 +642,7 @@ static void read_int_callback(struct urb *urb) case -EINPROGRESS: /* pending */ /* ignore silently */ gig_dbg(DEBUG_USBREQ, "%s: %s", - __func__, get_usb_statmsg(urb->status)); + __func__, get_usb_statmsg(status)); return; case -ENODEV: /* device removed */ case -ESHUTDOWN: /* device shut down */ @@ -623,7 +651,7 @@ static void read_int_callback(struct urb *urb) return; default: /* severe trouble */ dev_warn(cs->dev, "interrupt read: %s\n", - get_usb_statmsg(urb->status)); + get_usb_statmsg(status)); //FIXME corrective action? resubmission always ok? goto resubmit; } @@ -745,6 +773,7 @@ static void read_int_callback(struct urb *urb) } check_pending(ucs); + wake_up(&ucs->waitqueue); resubmit: rc = usb_submit_urb(urb, GFP_ATOMIC); @@ -766,17 +795,18 @@ static void read_iso_callback(struct urb *urb) { struct bc_state *bcs; struct bas_bc_state *ubc; + int status = urb->status; unsigned long flags; int i, rc; /* status codes not worth bothering the tasklet with */ - if (unlikely(urb->status == -ENOENT || - urb->status == -ECONNRESET || - urb->status == -EINPROGRESS || - urb->status == -ENODEV || - urb->status == -ESHUTDOWN)) { + if (unlikely(status == -ENOENT || + status == -ECONNRESET || + status == -EINPROGRESS || + status == -ENODEV || + status == -ESHUTDOWN)) { gig_dbg(DEBUG_ISO, "%s: %s", - __func__, get_usb_statmsg(urb->status)); + __func__, get_usb_statmsg(status)); return; } @@ -787,10 +817,11 @@ static void read_iso_callback(struct urb *urb) if (likely(ubc->isoindone == NULL)) { /* pass URB to tasklet */ ubc->isoindone = urb; + ubc->isoinstatus = status; tasklet_schedule(&ubc->rcvd_tasklet); } else { /* tasklet still busy, drop data and resubmit URB */ - ubc->loststatus = urb->status; + ubc->loststatus = status; for (i = 0; i < BAS_NUMFRAMES; i++) { ubc->isoinlost += urb->iso_frame_desc[i].actual_length; if (unlikely(urb->iso_frame_desc[i].status != 0 && @@ -800,7 +831,7 @@ static void read_iso_callback(struct urb *urb) urb->iso_frame_desc[i].status = 0; urb->iso_frame_desc[i].actual_length = 0; } - if (likely(atomic_read(&ubc->running))) { + if (likely(ubc->running)) { /* urb->dev is clobbered by USB subsystem */ urb->dev = bcs->cs->hw.bas->udev; urb->transfer_flags = URB_ISO_ASAP; @@ -831,22 +862,24 @@ static void write_iso_callback(struct urb *urb) { struct isow_urbctx_t *ucx; struct bas_bc_state *ubc; + int status = urb->status; unsigned long flags; /* status codes not worth bothering the tasklet with */ - if (unlikely(urb->status == -ENOENT || - urb->status == -ECONNRESET || - urb->status == -EINPROGRESS || - urb->status == -ENODEV || - urb->status == -ESHUTDOWN)) { + if (unlikely(status == -ENOENT || + status == -ECONNRESET || + status == -EINPROGRESS || + status == -ENODEV || + status == -ESHUTDOWN)) { gig_dbg(DEBUG_ISO, "%s: %s", - __func__, get_usb_statmsg(urb->status)); + __func__, get_usb_statmsg(status)); return; } /* pass URB context to tasklet */ ucx = urb->context; ubc = ucx->bcs->hw.bas; + ucx->status = status; spin_lock_irqsave(&ubc->isooutlock, flags); ubc->isooutovfl = ubc->isooutdone; @@ -875,7 +908,7 @@ static int starturbs(struct bc_state *bcs) bcs->inputstate |= INS_flag_hunt; /* submit all isochronous input URBs */ - atomic_set(&ubc->running, 1); + ubc->running = 1; for (k = 0; k < BAS_INURBS; k++) { urb = ubc->isoinurbs[k]; if (!urb) { @@ -932,15 +965,15 @@ static int starturbs(struct bc_state *bcs) ubc->isoouturbs[k].limit = -1; } - /* submit two URBs, keep third one */ - for (k = 0; k < 2; ++k) { + /* keep one URB free, submit the others */ + for (k = 0; k < BAS_OUTURBS-1; ++k) { dump_urb(DEBUG_ISO, "Initial isoc write", urb); rc = usb_submit_urb(ubc->isoouturbs[k].urb, GFP_ATOMIC); if (rc != 0) goto error; } dump_urb(DEBUG_ISO, "Initial isoc write (free)", urb); - ubc->isooutfree = &ubc->isoouturbs[2]; + ubc->isooutfree = &ubc->isoouturbs[BAS_OUTURBS-1]; ubc->isooutdone = ubc->isooutovfl = NULL; return 0; error: @@ -958,7 +991,7 @@ static void stopurbs(struct bas_bc_state *ubc) { int k, rc; - atomic_set(&ubc->running, 0); + ubc->running = 0; for (k = 0; k < BAS_INURBS; ++k) { rc = usb_unlink_urb(ubc->isoinurbs[k]); @@ -1034,7 +1067,7 @@ static int submit_iso_write_urb(struct isow_urbctx_t *ucx) } break; } - ucx->limit = atomic_read(&ubc->isooutbuf->nextread); + ucx->limit = ubc->isooutbuf->nextread; ifd->status = 0; ifd->actual_length = 0; } @@ -1070,6 +1103,7 @@ static void write_iso_tasklet(unsigned long data) struct cardstate *cs = bcs->cs; struct isow_urbctx_t *done, *next, *ovfl; struct urb *urb; + int status; struct usb_iso_packet_descriptor *ifd; int offset; unsigned long flags; @@ -1080,7 +1114,7 @@ static void write_iso_tasklet(unsigned long data) /* loop while completed URBs arrive in time */ for (;;) { - if (unlikely(!(atomic_read(&ubc->running)))) { + if (unlikely(!(ubc->running))) { gig_dbg(DEBUG_ISO, "%s: not running", __func__); return; } @@ -1126,7 +1160,8 @@ static void write_iso_tasklet(unsigned long data) /* process completed URB */ urb = done->urb; - switch (urb->status) { + status = done->status; + switch (status) { case -EXDEV: /* partial completion */ gig_dbg(DEBUG_ISO, "%s: URB partially completed", __func__); @@ -1179,12 +1214,12 @@ static void write_iso_tasklet(unsigned long data) break; default: /* severe trouble */ dev_warn(cs->dev, "isochronous write: %s\n", - get_usb_statmsg(urb->status)); + get_usb_statmsg(status)); } /* mark the write buffer area covered by this URB as free */ if (done->limit >= 0) - atomic_set(&ubc->isooutbuf->read, done->limit); + ubc->isooutbuf->read = done->limit; /* mark URB as free */ spin_lock_irqsave(&ubc->isooutlock, flags); @@ -1233,6 +1268,7 @@ static void read_iso_tasklet(unsigned long data) struct bas_bc_state *ubc = bcs->hw.bas; struct cardstate *cs = bcs->cs; struct urb *urb; + int status; char *rcvbuf; unsigned long flags; int totleft, numbytes, offset, frame, rc; @@ -1245,6 +1281,7 @@ static void read_iso_tasklet(unsigned long data) spin_unlock_irqrestore(&ubc->isoinlock, flags); return; } + status = ubc->isoinstatus; ubc->isoindone = NULL; if (unlikely(ubc->loststatus != -EINPROGRESS)) { dev_warn(cs->dev, @@ -1256,15 +1293,15 @@ static void read_iso_tasklet(unsigned long data) } spin_unlock_irqrestore(&ubc->isoinlock, flags); - if (unlikely(!(atomic_read(&ubc->running)))) { + if (unlikely(!(ubc->running))) { gig_dbg(DEBUG_ISO, "%s: channel not running, " "dropped URB with status: %s", - __func__, get_usb_statmsg(urb->status)); + __func__, get_usb_statmsg(status)); return; } - switch (urb->status) { + switch (status) { case 0: /* normal completion */ break; case -EXDEV: /* inspect individual frames @@ -1276,7 +1313,7 @@ static void read_iso_tasklet(unsigned long data) case -ECONNRESET: case -EINPROGRESS: gig_dbg(DEBUG_ISO, "%s: %s", - __func__, get_usb_statmsg(urb->status)); + __func__, get_usb_statmsg(status)); continue; /* -> skip */ case -EPIPE: dev_err(cs->dev, "isochronous read stalled\n"); @@ -1284,7 +1321,7 @@ static void read_iso_tasklet(unsigned long data) continue; /* -> skip */ default: /* severe trouble */ dev_warn(cs->dev, "isochronous read: %s\n", - get_usb_statmsg(urb->status)); + get_usb_statmsg(status)); goto error; } @@ -1406,6 +1443,8 @@ static void req_timeout(unsigned long data) dev_warn(bcs->cs->dev, "request 0x%02x timed out, clearing\n", pending); } + + wake_up(&ucs->waitqueue); } /* write_ctrl_callback @@ -1418,11 +1457,12 @@ static void req_timeout(unsigned long data) static void write_ctrl_callback(struct urb *urb) { struct bas_cardstate *ucs = urb->context; + int status = urb->status; int rc; unsigned long flags; /* check status */ - switch (urb->status) { + switch (status) { case 0: /* normal completion */ spin_lock_irqsave(&ucs->lock, flags); switch (ucs->pending) { @@ -1441,20 +1481,22 @@ static void write_ctrl_callback(struct urb *urb) case -ESHUTDOWN: /* device shut down */ /* ignore silently */ gig_dbg(DEBUG_USBREQ, "%s: %s", - __func__, get_usb_statmsg(urb->status)); + __func__, get_usb_statmsg(status)); break; default: /* any failure */ - if (++ucs->retry_ctrl > BAS_RETRY) { + /* don't retry if suspend requested */ + if (++ucs->retry_ctrl > BAS_RETRY || + (ucs->basstate & BS_SUSPEND)) { dev_err(&ucs->interface->dev, "control request 0x%02x failed: %s\n", ucs->dr_ctrl.bRequest, - get_usb_statmsg(urb->status)); + get_usb_statmsg(status)); break; /* give up */ } dev_notice(&ucs->interface->dev, "control request 0x%02x: %s, retry %d\n", - ucs->dr_ctrl.bRequest, get_usb_statmsg(urb->status), + ucs->dr_ctrl.bRequest, get_usb_statmsg(status), ucs->retry_ctrl); /* urb->dev is clobbered by USB subsystem */ urb->dev = ucs->udev; @@ -1474,6 +1516,7 @@ static void write_ctrl_callback(struct urb *urb) del_timer(&ucs->timer_ctrl); ucs->pending = 0; spin_unlock_irqrestore(&ucs->lock, flags); + wake_up(&ucs->waitqueue); } /* req_submit @@ -1548,37 +1591,46 @@ static int req_submit(struct bc_state *bcs, int req, int val, int timeout) */ static int gigaset_init_bchannel(struct bc_state *bcs) { + struct cardstate *cs = bcs->cs; int req, ret; unsigned long flags; - spin_lock_irqsave(&bcs->cs->lock, flags); - if (unlikely(!bcs->cs->connected)) { + spin_lock_irqsave(&cs->lock, flags); + if (unlikely(!cs->connected)) { gig_dbg(DEBUG_USBREQ, "%s: not connected", __func__); - spin_unlock_irqrestore(&bcs->cs->lock, flags); + spin_unlock_irqrestore(&cs->lock, flags); return -ENODEV; } + if (cs->hw.bas->basstate & BS_SUSPEND) { + dev_notice(cs->dev, + "not starting isochronous I/O, " + "suspend in progress\n"); + spin_unlock_irqrestore(&cs->lock, flags); + return -EHOSTUNREACH; + } + if ((ret = starturbs(bcs)) < 0) { - dev_err(bcs->cs->dev, + dev_err(cs->dev, "could not start isochronous I/O for channel B%d: %s\n", bcs->channel + 1, ret == -EFAULT ? "null URB" : get_usb_rcmsg(ret)); if (ret != -ENODEV) error_hangup(bcs); - spin_unlock_irqrestore(&bcs->cs->lock, flags); + spin_unlock_irqrestore(&cs->lock, flags); return ret; } req = bcs->channel ? HD_OPEN_B2CHANNEL : HD_OPEN_B1CHANNEL; if ((ret = req_submit(bcs, req, 0, BAS_TIMEOUT)) < 0) { - dev_err(bcs->cs->dev, "could not open channel B%d\n", + dev_err(cs->dev, "could not open channel B%d\n", bcs->channel + 1); stopurbs(bcs->hw.bas); if (ret != -ENODEV) error_hangup(bcs); } - spin_unlock_irqrestore(&bcs->cs->lock, flags); + spin_unlock_irqrestore(&cs->lock, flags); return ret; } @@ -1594,20 +1646,20 @@ static int gigaset_init_bchannel(struct bc_state *bcs) */ static int gigaset_close_bchannel(struct bc_state *bcs) { + struct cardstate *cs = bcs->cs; int req, ret; unsigned long flags; - spin_lock_irqsave(&bcs->cs->lock, flags); - if (unlikely(!bcs->cs->connected)) { - spin_unlock_irqrestore(&bcs->cs->lock, flags); + spin_lock_irqsave(&cs->lock, flags); + if (unlikely(!cs->connected)) { + spin_unlock_irqrestore(&cs->lock, flags); gig_dbg(DEBUG_USBREQ, "%s: not connected", __func__); return -ENODEV; } - if (!(atomic_read(&bcs->cs->hw.bas->basstate) & - (bcs->channel ? BS_B2OPEN : BS_B1OPEN))) { + if (!(cs->hw.bas->basstate & (bcs->channel ? BS_B2OPEN : BS_B1OPEN))) { /* channel not running: just signal common.c */ - spin_unlock_irqrestore(&bcs->cs->lock, flags); + spin_unlock_irqrestore(&cs->lock, flags); gigaset_bchannel_down(bcs); return 0; } @@ -1615,10 +1667,10 @@ static int gigaset_close_bchannel(struct bc_state *bcs) /* channel running: tell device to close it */ req = bcs->channel ? HD_CLOSE_B2CHANNEL : HD_CLOSE_B1CHANNEL; if ((ret = req_submit(bcs, req, 0, BAS_TIMEOUT)) < 0) - dev_err(bcs->cs->dev, "closing channel B%d failed\n", + dev_err(cs->dev, "closing channel B%d failed\n", bcs->channel + 1); - spin_unlock_irqrestore(&bcs->cs->lock, flags); + spin_unlock_irqrestore(&cs->lock, flags); return ret; } @@ -1665,12 +1717,14 @@ static void write_command_callback(struct urb *urb) { struct cardstate *cs = urb->context; struct bas_cardstate *ucs = cs->hw.bas; + int status = urb->status; unsigned long flags; update_basstate(ucs, 0, BS_ATWRPEND); + wake_up(&ucs->waitqueue); /* check status */ - switch (urb->status) { + switch (status) { case 0: /* normal completion */ break; case -ENOENT: /* cancelled */ @@ -1680,26 +1734,33 @@ static void write_command_callback(struct urb *urb) case -ESHUTDOWN: /* device shut down */ /* ignore silently */ gig_dbg(DEBUG_USBREQ, "%s: %s", - __func__, get_usb_statmsg(urb->status)); + __func__, get_usb_statmsg(status)); return; default: /* any failure */ if (++ucs->retry_cmd_out > BAS_RETRY) { dev_warn(cs->dev, "command write: %s, " "giving up after %d retries\n", - get_usb_statmsg(urb->status), + get_usb_statmsg(status), ucs->retry_cmd_out); break; } + if (ucs->basstate & BS_SUSPEND) { + dev_warn(cs->dev, + "command write: %s, " + "won't retry - suspend requested\n", + get_usb_statmsg(status)); + break; + } if (cs->cmdbuf == NULL) { dev_warn(cs->dev, "command write: %s, " "cannot retry - cmdbuf gone\n", - get_usb_statmsg(urb->status)); + get_usb_statmsg(status)); break; } dev_notice(cs->dev, "command write: %s, retry %d\n", - get_usb_statmsg(urb->status), ucs->retry_cmd_out); + get_usb_statmsg(status), ucs->retry_cmd_out); if (atwrite_submit(cs, cs->cmdbuf->buf, cs->cmdbuf->len) >= 0) /* resubmitted - bypass regular exit block */ return; @@ -1799,8 +1860,14 @@ static int start_cbsend(struct cardstate *cs) int rc; int retval = 0; + /* check if suspend requested */ + if (ucs->basstate & BS_SUSPEND) { + gig_dbg(DEBUG_TRANSCMD|DEBUG_LOCKCMD, "suspending"); + return -EHOSTUNREACH; + } + /* check if AT channel is open */ - if (!(atomic_read(&ucs->basstate) & BS_ATOPEN)) { + if (!(ucs->basstate & BS_ATOPEN)) { gig_dbg(DEBUG_TRANSCMD|DEBUG_LOCKCMD, "AT channel not open"); rc = req_submit(cs->bcs, HD_OPEN_ATCHANNEL, 0, BAS_TIMEOUT); if (rc < 0) { @@ -1816,8 +1883,7 @@ static int start_cbsend(struct cardstate *cs) /* try to send first command in queue */ spin_lock_irqsave(&cs->cmdlock, flags); - while ((cb = cs->cmdbuf) != NULL && - atomic_read(&ucs->basstate) & BS_ATREADY) { + while ((cb = cs->cmdbuf) != NULL && (ucs->basstate & BS_ATREADY)) { ucs->retry_cmd_out = 0; rc = atwrite_submit(cs, cb->buf, cb->len); if (unlikely(rc)) { @@ -1855,7 +1921,7 @@ static int gigaset_write_cmd(struct cardstate *cs, unsigned long flags; int rc; - gigaset_dbg_buffer(atomic_read(&cs->mstate) != MS_LOCKED ? + gigaset_dbg_buffer(cs->mstate != MS_LOCKED ? DEBUG_TRANSCMD : DEBUG_LOCKCMD, "CMD Transmit", len, buf); @@ -1970,7 +2036,7 @@ static int gigaset_freebcshw(struct bc_state *bcs) return 0; /* kill URBs and tasklets before freeing - better safe than sorry */ - atomic_set(&ubc->running, 0); + ubc->running = 0; gig_dbg(DEBUG_INIT, "%s: killing iso URBs", __func__); for (i = 0; i < BAS_OUTURBS; ++i) { usb_kill_urb(ubc->isoouturbs[i].urb); @@ -2005,7 +2071,7 @@ static int gigaset_initbcshw(struct bc_state *bcs) return 0; } - atomic_set(&ubc->running, 0); + ubc->running = 0; atomic_set(&ubc->corrbytes, 0); spin_lock_init(&ubc->isooutlock); for (i = 0; i < BAS_OUTURBS; ++i) { @@ -2050,7 +2116,7 @@ static void gigaset_reinitbcshw(struct bc_state *bcs) { struct bas_bc_state *ubc = bcs->hw.bas; - atomic_set(&bcs->hw.bas->running, 0); + bcs->hw.bas->running = 0; atomic_set(&bcs->hw.bas->corrbytes, 0); bcs->hw.bas->numsub = 0; spin_lock_init(&ubc->isooutlock); @@ -2081,10 +2147,11 @@ static int gigaset_initcshw(struct cardstate *cs) spin_lock_init(&ucs->lock); ucs->pending = 0; - atomic_set(&ucs->basstate, 0); + ucs->basstate = 0; init_timer(&ucs->timer_ctrl); init_timer(&ucs->timer_atrdy); init_timer(&ucs->timer_cmd_in); + init_waitqueue_head(&ucs->waitqueue); return 1; } @@ -2102,7 +2169,7 @@ static void freeurbs(struct cardstate *cs) int i, j; gig_dbg(DEBUG_INIT, "%s: killing URBs", __func__); - for (j = 0; j < 2; ++j) { + for (j = 0; j < BAS_CHANNELS; ++j) { ubc = cs->bcs[j].hw.bas; for (i = 0; i < BAS_OUTURBS; ++i) { usb_kill_urb(ubc->isoouturbs[i].urb); @@ -2179,11 +2246,11 @@ static int gigaset_probe(struct usb_interface *interface, __func__, le16_to_cpu(udev->descriptor.idVendor), le16_to_cpu(udev->descriptor.idProduct)); - cs = gigaset_getunassignedcs(driver); - if (!cs) { - dev_err(&udev->dev, "no free cardstate\n"); + /* allocate memory for our device state and intialize it */ + cs = gigaset_initcs(driver, BAS_CHANNELS, 0, 0, cidmode, + GIGASET_MODULENAME); + if (!cs) return -ENODEV; - } ucs = cs->hw.bas; /* save off device structure ptrs for later use */ @@ -2203,7 +2270,7 @@ static int gigaset_probe(struct usb_interface *interface, !(ucs->urb_ctrl = usb_alloc_urb(0, GFP_KERNEL))) goto allocerr; - for (j = 0; j < 2; ++j) { + for (j = 0; j < BAS_CHANNELS; ++j) { ubc = cs->bcs[j].hw.bas; for (i = 0; i < BAS_OUTURBS; ++i) if (!(ubc->isoouturbs[i].urb = @@ -2237,7 +2304,7 @@ static int gigaset_probe(struct usb_interface *interface, /* tell common part that the device is ready */ if (startmode == SM_LOCKED) - atomic_set(&cs->mstate, MS_LOCKED); + cs->mstate = MS_LOCKED; /* save address of controller structure */ usb_set_intfdata(interface, cs); @@ -2252,7 +2319,7 @@ allocerr: error: freeurbs(cs); usb_set_intfdata(interface, NULL); - gigaset_unassign(cs); + gigaset_freecs(cs); return -ENODEV; } @@ -2272,11 +2339,10 @@ static void gigaset_disconnect(struct usb_interface *interface) dev_info(cs->dev, "disconnecting Gigaset base\n"); /* mark base as not ready, all channels disconnected */ - atomic_set(&ucs->basstate, 0); + ucs->basstate = 0; /* tell LL all channels are down */ - //FIXME shouldn't gigaset_stop() do this? - for (j = 0; j < 2; ++j) + for (j = 0; j < BAS_CHANNELS; ++j) gigaset_bchannel_down(cs->bcs + j); /* stop driver (common part) */ @@ -2295,9 +2361,113 @@ static void gigaset_disconnect(struct usb_interface *interface) ucs->interface = NULL; ucs->udev = NULL; cs->dev = NULL; - gigaset_unassign(cs); + gigaset_freecs(cs); } +/* gigaset_suspend + * This function is called before the USB connection is suspended. + */ +static int gigaset_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct cardstate *cs = usb_get_intfdata(intf); + struct bas_cardstate *ucs = cs->hw.bas; + int rc; + + /* set suspend flag; this stops AT command/response traffic */ + if (update_basstate(ucs, BS_SUSPEND, 0) & BS_SUSPEND) { + gig_dbg(DEBUG_SUSPEND, "already suspended"); + return 0; + } + + /* wait a bit for blocking conditions to go away */ + rc = wait_event_timeout(ucs->waitqueue, + !(ucs->basstate & + (BS_B1OPEN|BS_B2OPEN|BS_ATRDPEND|BS_ATWRPEND)), + BAS_TIMEOUT*HZ/10); + gig_dbg(DEBUG_SUSPEND, "wait_event_timeout() -> %d", rc); + + /* check for conditions preventing suspend */ + if (ucs->basstate & (BS_B1OPEN|BS_B2OPEN|BS_ATRDPEND|BS_ATWRPEND)) { + dev_warn(cs->dev, "cannot suspend:\n"); + if (ucs->basstate & BS_B1OPEN) + dev_warn(cs->dev, " B channel 1 open\n"); + if (ucs->basstate & BS_B2OPEN) + dev_warn(cs->dev, " B channel 2 open\n"); + if (ucs->basstate & BS_ATRDPEND) + dev_warn(cs->dev, " receiving AT reply\n"); + if (ucs->basstate & BS_ATWRPEND) + dev_warn(cs->dev, " sending AT command\n"); + update_basstate(ucs, 0, BS_SUSPEND); + return -EBUSY; + } + + /* close AT channel if open */ + if (ucs->basstate & BS_ATOPEN) { + gig_dbg(DEBUG_SUSPEND, "closing AT channel"); + rc = req_submit(cs->bcs, HD_CLOSE_ATCHANNEL, 0, 0); + if (rc) { + update_basstate(ucs, 0, BS_SUSPEND); + return rc; + } + wait_event_timeout(ucs->waitqueue, !ucs->pending, + BAS_TIMEOUT*HZ/10); + /* in case of timeout, proceed anyway */ + } + + /* kill all URBs and timers that might still be pending */ + usb_kill_urb(ucs->urb_ctrl); + usb_kill_urb(ucs->urb_int_in); + del_timer_sync(&ucs->timer_ctrl); + + gig_dbg(DEBUG_SUSPEND, "suspend complete"); + return 0; +} + +/* gigaset_resume + * This function is called after the USB connection has been resumed. + */ +static int gigaset_resume(struct usb_interface *intf) +{ + struct cardstate *cs = usb_get_intfdata(intf); + struct bas_cardstate *ucs = cs->hw.bas; + int rc; + + /* resubmit interrupt URB for spontaneous messages from base */ + rc = usb_submit_urb(ucs->urb_int_in, GFP_KERNEL); + if (rc) { + dev_err(cs->dev, "could not resubmit interrupt URB: %s\n", + get_usb_rcmsg(rc)); + return rc; + } + + /* clear suspend flag to reallow activity */ + update_basstate(ucs, 0, BS_SUSPEND); + + gig_dbg(DEBUG_SUSPEND, "resume complete"); + return 0; +} + +/* gigaset_pre_reset + * This function is called before the USB connection is reset. + */ +static int gigaset_pre_reset(struct usb_interface *intf) +{ + /* handle just like suspend */ + return gigaset_suspend(intf, PMSG_ON); +} + +/* gigaset_post_reset + * This function is called after the USB connection has been reset. + */ +static int gigaset_post_reset(struct usb_interface *intf) +{ + /* FIXME: send HD_DEVICE_INIT_ACK? */ + + /* resume operations */ + return gigaset_resume(intf); +} + + static const struct gigaset_ops gigops = { gigaset_write_cmd, gigaset_write_room, @@ -2330,12 +2500,6 @@ static int __init bas_gigaset_init(void) &gigops, THIS_MODULE)) == NULL) goto error; - /* allocate memory for our device state and intialize it */ - cardstate = gigaset_initcs(driver, 2, 0, 0, cidmode, - GIGASET_MODULENAME); - if (!cardstate) - goto error; - /* register this driver with the USB subsystem */ result = usb_register(&gigaset_usb_driver); if (result < 0) { @@ -2347,9 +2511,7 @@ static int __init bas_gigaset_init(void) info(DRIVER_DESC); return 0; -error: if (cardstate) - gigaset_freecs(cardstate); - cardstate = NULL; +error: if (driver) gigaset_freedriver(driver); driver = NULL; @@ -2361,43 +2523,50 @@ error: if (cardstate) */ static void __exit bas_gigaset_exit(void) { - struct bas_cardstate *ucs = cardstate->hw.bas; + struct bas_cardstate *ucs; + int i; gigaset_blockdriver(driver); /* => probe will fail * => no gigaset_start any more */ - gigaset_shutdown(cardstate); - /* from now on, no isdn callback should be possible */ - - /* close all still open channels */ - if (atomic_read(&ucs->basstate) & BS_B1OPEN) { - gig_dbg(DEBUG_INIT, "closing B1 channel"); - usb_control_msg(ucs->udev, usb_sndctrlpipe(ucs->udev, 0), - HD_CLOSE_B1CHANNEL, OUT_VENDOR_REQ, 0, 0, - NULL, 0, BAS_TIMEOUT); - } - if (atomic_read(&ucs->basstate) & BS_B2OPEN) { - gig_dbg(DEBUG_INIT, "closing B2 channel"); - usb_control_msg(ucs->udev, usb_sndctrlpipe(ucs->udev, 0), - HD_CLOSE_B2CHANNEL, OUT_VENDOR_REQ, 0, 0, - NULL, 0, BAS_TIMEOUT); - } - if (atomic_read(&ucs->basstate) & BS_ATOPEN) { - gig_dbg(DEBUG_INIT, "closing AT channel"); - usb_control_msg(ucs->udev, usb_sndctrlpipe(ucs->udev, 0), - HD_CLOSE_ATCHANNEL, OUT_VENDOR_REQ, 0, 0, - NULL, 0, BAS_TIMEOUT); + /* stop all connected devices */ + for (i = 0; i < driver->minors; i++) { + if (gigaset_shutdown(driver->cs + i) < 0) + continue; /* no device */ + /* from now on, no isdn callback should be possible */ + + /* close all still open channels */ + ucs = driver->cs[i].hw.bas; + if (ucs->basstate & BS_B1OPEN) { + gig_dbg(DEBUG_INIT, "closing B1 channel"); + usb_control_msg(ucs->udev, + usb_sndctrlpipe(ucs->udev, 0), + HD_CLOSE_B1CHANNEL, OUT_VENDOR_REQ, + 0, 0, NULL, 0, BAS_TIMEOUT); + } + if (ucs->basstate & BS_B2OPEN) { + gig_dbg(DEBUG_INIT, "closing B2 channel"); + usb_control_msg(ucs->udev, + usb_sndctrlpipe(ucs->udev, 0), + HD_CLOSE_B2CHANNEL, OUT_VENDOR_REQ, + 0, 0, NULL, 0, BAS_TIMEOUT); + } + if (ucs->basstate & BS_ATOPEN) { + gig_dbg(DEBUG_INIT, "closing AT channel"); + usb_control_msg(ucs->udev, + usb_sndctrlpipe(ucs->udev, 0), + HD_CLOSE_ATCHANNEL, OUT_VENDOR_REQ, + 0, 0, NULL, 0, BAS_TIMEOUT); + } + ucs->basstate = 0; } - atomic_set(&ucs->basstate, 0); /* deregister this driver with the USB subsystem */ usb_deregister(&gigaset_usb_driver); /* this will call the disconnect-callback */ /* from now on, no disconnect/probe callback should be running */ - gigaset_freecs(cardstate); - cardstate = NULL; gigaset_freedriver(driver); driver = NULL; } diff --git a/drivers/isdn/gigaset/common.c b/drivers/isdn/gigaset/common.c index acd417197d0..aacedec4986 100644 --- a/drivers/isdn/gigaset/common.c +++ b/drivers/isdn/gigaset/common.c @@ -31,7 +31,6 @@ MODULE_PARM_DESC(debug, "debug level"); /* driver state flags */ #define VALID_MINOR 0x01 #define VALID_ID 0x02 -#define ASSIGNED 0x04 void gigaset_dbg_buffer(enum debuglevel level, const unsigned char *msg, size_t len, const unsigned char *buf) @@ -178,7 +177,7 @@ int gigaset_get_channel(struct bc_state *bcs) unsigned long flags; spin_lock_irqsave(&bcs->cs->lock, flags); - if (bcs->use_count) { + if (bcs->use_count || !try_module_get(bcs->cs->driver->owner)) { gig_dbg(DEBUG_ANY, "could not allocate channel %d", bcs->channel); spin_unlock_irqrestore(&bcs->cs->lock, flags); @@ -203,6 +202,7 @@ void gigaset_free_channel(struct bc_state *bcs) } --bcs->use_count; bcs->busy = 0; + module_put(bcs->cs->driver->owner); gig_dbg(DEBUG_ANY, "freed channel %d", bcs->channel); spin_unlock_irqrestore(&bcs->cs->lock, flags); } @@ -356,31 +356,28 @@ static struct cardstate *alloc_cs(struct gigaset_driver *drv) { unsigned long flags; unsigned i; + struct cardstate *cs; struct cardstate *ret = NULL; spin_lock_irqsave(&drv->lock, flags); + if (drv->blocked) + goto exit; for (i = 0; i < drv->minors; ++i) { - if (!(drv->flags[i] & VALID_MINOR)) { - if (try_module_get(drv->owner)) { - drv->flags[i] = VALID_MINOR; - ret = drv->cs + i; - } + cs = drv->cs + i; + if (!(cs->flags & VALID_MINOR)) { + cs->flags = VALID_MINOR; + ret = cs; break; } } +exit: spin_unlock_irqrestore(&drv->lock, flags); return ret; } static void free_cs(struct cardstate *cs) { - unsigned long flags; - struct gigaset_driver *drv = cs->driver; - spin_lock_irqsave(&drv->lock, flags); - if (drv->flags[cs->minor_index] & VALID_MINOR) - module_put(drv->owner); - drv->flags[cs->minor_index] = 0; - spin_unlock_irqrestore(&drv->lock, flags); + cs->flags = 0; } static void make_valid(struct cardstate *cs, unsigned mask) @@ -388,7 +385,7 @@ static void make_valid(struct cardstate *cs, unsigned mask) unsigned long flags; struct gigaset_driver *drv = cs->driver; spin_lock_irqsave(&drv->lock, flags); - drv->flags[cs->minor_index] |= mask; + cs->flags |= mask; spin_unlock_irqrestore(&drv->lock, flags); } @@ -397,7 +394,7 @@ static void make_invalid(struct cardstate *cs, unsigned mask) unsigned long flags; struct gigaset_driver *drv = cs->driver; spin_lock_irqsave(&drv->lock, flags); - drv->flags[cs->minor_index] &= ~mask; + cs->flags &= ~mask; spin_unlock_irqrestore(&drv->lock, flags); } @@ -501,11 +498,11 @@ static void gigaset_inbuf_init(struct inbuf_t *inbuf, struct bc_state *bcs, struct cardstate *cs, int inputstate) /* inbuf->read must be allocated before! */ { - atomic_set(&inbuf->head, 0); - atomic_set(&inbuf->tail, 0); + inbuf->head = 0; + inbuf->tail = 0; inbuf->cs = cs; inbuf->bcs = bcs; /*base driver: NULL*/ - inbuf->rcvbuf = NULL; //FIXME + inbuf->rcvbuf = NULL; inbuf->inputstate = inputstate; } @@ -521,8 +518,8 @@ int gigaset_fill_inbuf(struct inbuf_t *inbuf, const unsigned char *src, return 0; bytesleft = numbytes; - tail = atomic_read(&inbuf->tail); - head = atomic_read(&inbuf->head); + tail = inbuf->tail; + head = inbuf->head; gig_dbg(DEBUG_INTR, "buffer state: %u -> %u", head, tail); while (bytesleft) { @@ -546,7 +543,7 @@ int gigaset_fill_inbuf(struct inbuf_t *inbuf, const unsigned char *src, src += n; } gig_dbg(DEBUG_INTR, "setting tail to %u", tail); - atomic_set(&inbuf->tail, tail); + inbuf->tail = tail; return numbytes != bytesleft; } EXPORT_SYMBOL_GPL(gigaset_fill_inbuf); @@ -668,7 +665,7 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels, tasklet_init(&cs->event_tasklet, &gigaset_handle_event, (unsigned long) cs); - atomic_set(&cs->commands_pending, 0); + cs->commands_pending = 0; cs->cur_at_seq = 0; cs->gotfwver = -1; cs->open_count = 0; @@ -688,8 +685,8 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels, init_waitqueue_head(&cs->waitqueue); cs->waiting = 0; - atomic_set(&cs->mode, M_UNKNOWN); - atomic_set(&cs->mstate, MS_UNINITIALIZED); + cs->mode = M_UNKNOWN; + cs->mstate = MS_UNINITIALIZED; for (i = 0; i < channels; ++i) { gig_dbg(DEBUG_INIT, "setting up bcs[%d].read", i); @@ -806,8 +803,8 @@ static void cleanup_cs(struct cardstate *cs) spin_lock_irqsave(&cs->lock, flags); - atomic_set(&cs->mode, M_UNKNOWN); - atomic_set(&cs->mstate, MS_UNINITIALIZED); + cs->mode = M_UNKNOWN; + cs->mstate = MS_UNINITIALIZED; clear_at_state(&cs->at_state); dealloc_at_states(cs); @@ -817,8 +814,8 @@ static void cleanup_cs(struct cardstate *cs) kfree(cs->inbuf->rcvbuf); cs->inbuf->rcvbuf = NULL; cs->inbuf->inputstate = INS_command; - atomic_set(&cs->inbuf->head, 0); - atomic_set(&cs->inbuf->tail, 0); + cs->inbuf->head = 0; + cs->inbuf->tail = 0; cb = cs->cmdbuf; while (cb) { @@ -832,7 +829,7 @@ static void cleanup_cs(struct cardstate *cs) cs->gotfwver = -1; cs->dle = 0; cs->cur_at_seq = 0; - atomic_set(&cs->commands_pending, 0); + cs->commands_pending = 0; cs->cbytes = 0; spin_unlock_irqrestore(&cs->lock, flags); @@ -862,7 +859,7 @@ int gigaset_start(struct cardstate *cs) cs->connected = 1; spin_unlock_irqrestore(&cs->lock, flags); - if (atomic_read(&cs->mstate) != MS_LOCKED) { + if (cs->mstate != MS_LOCKED) { cs->ops->set_modem_ctrl(cs, 0, TIOCM_DTR|TIOCM_RTS); cs->ops->baud_rate(cs, B115200); cs->ops->set_line_ctrl(cs, CS8); @@ -893,10 +890,17 @@ error: } EXPORT_SYMBOL_GPL(gigaset_start); -void gigaset_shutdown(struct cardstate *cs) +/* gigaset_shutdown + * check if a device is associated to the cardstate structure and stop it + * return value: 0 if ok, -1 if no device was associated + */ +int gigaset_shutdown(struct cardstate *cs) { mutex_lock(&cs->mutex); + if (!(cs->flags & VALID_MINOR)) + return -1; + cs->waiting = 1; if (!gigaset_add_event(cs, &cs->at_state, EV_SHUTDOWN, NULL, 0, NULL)) { @@ -913,6 +917,7 @@ void gigaset_shutdown(struct cardstate *cs) exit: mutex_unlock(&cs->mutex); + return 0; } EXPORT_SYMBOL_GPL(gigaset_shutdown); @@ -954,13 +959,11 @@ struct cardstate *gigaset_get_cs_by_id(int id) list_for_each_entry(drv, &drivers, list) { spin_lock(&drv->lock); for (i = 0; i < drv->minors; ++i) { - if (drv->flags[i] & VALID_ID) { - cs = drv->cs + i; - if (cs->myid == id) - ret = cs; - } - if (ret) + cs = drv->cs + i; + if ((cs->flags & VALID_ID) && cs->myid == id) { + ret = cs; break; + } } spin_unlock(&drv->lock); if (ret) @@ -983,10 +986,9 @@ void gigaset_debugdrivers(void) spin_lock(&drv->lock); for (i = 0; i < drv->minors; ++i) { gig_dbg(DEBUG_DRIVER, " index %u", i); - gig_dbg(DEBUG_DRIVER, " flags 0x%02x", - drv->flags[i]); cs = drv->cs + i; gig_dbg(DEBUG_DRIVER, " cardstate %p", cs); + gig_dbg(DEBUG_DRIVER, " flags 0x%02x", cs->flags); gig_dbg(DEBUG_DRIVER, " minor_index %u", cs->minor_index); gig_dbg(DEBUG_DRIVER, " driver %p", cs->driver); @@ -1010,7 +1012,7 @@ static struct cardstate *gigaset_get_cs_by_minor(unsigned minor) continue; index = minor - drv->minor; spin_lock(&drv->lock); - if (drv->flags[index] & VALID_MINOR) + if (drv->cs[index].flags & VALID_MINOR) ret = drv->cs + index; spin_unlock(&drv->lock); if (ret) @@ -1038,7 +1040,6 @@ void gigaset_freedriver(struct gigaset_driver *drv) gigaset_if_freedriver(drv); kfree(drv->cs); - kfree(drv->flags); kfree(drv); } EXPORT_SYMBOL_GPL(gigaset_freedriver); @@ -1080,12 +1081,8 @@ struct gigaset_driver *gigaset_initdriver(unsigned minor, unsigned minors, if (!drv->cs) goto error; - drv->flags = kmalloc(minors * sizeof *drv->flags, GFP_KERNEL); - if (!drv->flags) - goto error; - for (i = 0; i < minors; ++i) { - drv->flags[i] = 0; + drv->cs[i].flags = 0; drv->cs[i].driver = drv; drv->cs[i].ops = drv->ops; drv->cs[i].minor_index = i; @@ -1106,53 +1103,9 @@ error: } EXPORT_SYMBOL_GPL(gigaset_initdriver); -/* For drivers without fixed assignment device<->cardstate (usb) */ -struct cardstate *gigaset_getunassignedcs(struct gigaset_driver *drv) -{ - unsigned long flags; - struct cardstate *cs = NULL; - unsigned i; - - spin_lock_irqsave(&drv->lock, flags); - if (drv->blocked) - goto exit; - for (i = 0; i < drv->minors; ++i) { - if ((drv->flags[i] & VALID_MINOR) && - !(drv->flags[i] & ASSIGNED)) { - drv->flags[i] |= ASSIGNED; - cs = drv->cs + i; - break; - } - } -exit: - spin_unlock_irqrestore(&drv->lock, flags); - return cs; -} -EXPORT_SYMBOL_GPL(gigaset_getunassignedcs); - -void gigaset_unassign(struct cardstate *cs) -{ - unsigned long flags; - unsigned *minor_flags; - struct gigaset_driver *drv; - - if (!cs) - return; - drv = cs->driver; - spin_lock_irqsave(&drv->lock, flags); - minor_flags = drv->flags + cs->minor_index; - if (*minor_flags & VALID_MINOR) - *minor_flags &= ~ASSIGNED; - spin_unlock_irqrestore(&drv->lock, flags); -} -EXPORT_SYMBOL_GPL(gigaset_unassign); - void gigaset_blockdriver(struct gigaset_driver *drv) { - unsigned long flags; - spin_lock_irqsave(&drv->lock, flags); drv->blocked = 1; - spin_unlock_irqrestore(&drv->lock, flags); } EXPORT_SYMBOL_GPL(gigaset_blockdriver); diff --git a/drivers/isdn/gigaset/ev-layer.c b/drivers/isdn/gigaset/ev-layer.c index cec1ef342fc..5cbf64d850e 100644 --- a/drivers/isdn/gigaset/ev-layer.c +++ b/drivers/isdn/gigaset/ev-layer.c @@ -735,7 +735,7 @@ static void disconnect(struct at_state_t **at_state_p) /* revert to selected idle mode */ if (!cs->cidmode) { cs->at_state.pending_commands |= PC_UMMODE; - atomic_set(&cs->commands_pending, 1); //FIXME + cs->commands_pending = 1; gig_dbg(DEBUG_CMD, "Scheduling PC_UMMODE"); } spin_unlock_irqrestore(&cs->lock, flags); @@ -793,15 +793,15 @@ static void init_failed(struct cardstate *cs, int mode) struct at_state_t *at_state; cs->at_state.pending_commands &= ~PC_INIT; - atomic_set(&cs->mode, mode); - atomic_set(&cs->mstate, MS_UNINITIALIZED); + cs->mode = mode; + cs->mstate = MS_UNINITIALIZED; gigaset_free_channels(cs); for (i = 0; i < cs->channels; ++i) { at_state = &cs->bcs[i].at_state; if (at_state->pending_commands & PC_CID) { at_state->pending_commands &= ~PC_CID; at_state->pending_commands |= PC_NOCID; - atomic_set(&cs->commands_pending, 1); + cs->commands_pending = 1; } } } @@ -812,11 +812,11 @@ static void schedule_init(struct cardstate *cs, int state) gig_dbg(DEBUG_CMD, "not scheduling PC_INIT again"); return; } - atomic_set(&cs->mstate, state); - atomic_set(&cs->mode, M_UNKNOWN); + cs->mstate = state; + cs->mode = M_UNKNOWN; gigaset_block_channels(cs); cs->at_state.pending_commands |= PC_INIT; - atomic_set(&cs->commands_pending, 1); + cs->commands_pending = 1; gig_dbg(DEBUG_CMD, "Scheduling PC_INIT"); } @@ -953,13 +953,13 @@ static void start_dial(struct at_state_t *at_state, void *data, unsigned seq_ind at_state->pending_commands |= PC_CID; gig_dbg(DEBUG_CMD, "Scheduling PC_CID"); - atomic_set(&cs->commands_pending, 1); + cs->commands_pending = 1; return; error: at_state->pending_commands |= PC_NOCID; gig_dbg(DEBUG_CMD, "Scheduling PC_NOCID"); - atomic_set(&cs->commands_pending, 1); + cs->commands_pending = 1; return; } @@ -973,12 +973,12 @@ static void start_accept(struct at_state_t *at_state) if (retval == 0) { at_state->pending_commands |= PC_ACCEPT; gig_dbg(DEBUG_CMD, "Scheduling PC_ACCEPT"); - atomic_set(&cs->commands_pending, 1); + cs->commands_pending = 1; } else { - //FIXME + /* error reset */ at_state->pending_commands |= PC_HUP; gig_dbg(DEBUG_CMD, "Scheduling PC_HUP"); - atomic_set(&cs->commands_pending, 1); + cs->commands_pending = 1; } } @@ -986,7 +986,7 @@ static void do_start(struct cardstate *cs) { gigaset_free_channels(cs); - if (atomic_read(&cs->mstate) != MS_LOCKED) + if (cs->mstate != MS_LOCKED) schedule_init(cs, MS_INIT); cs->isdn_up = 1; @@ -1000,9 +1000,9 @@ static void do_start(struct cardstate *cs) static void finish_shutdown(struct cardstate *cs) { - if (atomic_read(&cs->mstate) != MS_LOCKED) { - atomic_set(&cs->mstate, MS_UNINITIALIZED); - atomic_set(&cs->mode, M_UNKNOWN); + if (cs->mstate != MS_LOCKED) { + cs->mstate = MS_UNINITIALIZED; + cs->mode = M_UNKNOWN; } /* Tell the LL that the device is not available .. */ @@ -1022,10 +1022,10 @@ static void do_shutdown(struct cardstate *cs) { gigaset_block_channels(cs); - if (atomic_read(&cs->mstate) == MS_READY) { - atomic_set(&cs->mstate, MS_SHUTDOWN); + if (cs->mstate == MS_READY) { + cs->mstate = MS_SHUTDOWN; cs->at_state.pending_commands |= PC_SHUTDOWN; - atomic_set(&cs->commands_pending, 1); + cs->commands_pending = 1; gig_dbg(DEBUG_CMD, "Scheduling PC_SHUTDOWN"); } else finish_shutdown(cs); @@ -1120,7 +1120,7 @@ static void handle_icall(struct cardstate *cs, struct bc_state *bcs, * In fact it doesn't. */ at_state->pending_commands |= PC_HUP; - atomic_set(&cs->commands_pending, 1); + cs->commands_pending = 1; break; } } @@ -1130,7 +1130,7 @@ static int do_lock(struct cardstate *cs) int mode; int i; - switch (atomic_read(&cs->mstate)) { + switch (cs->mstate) { case MS_UNINITIALIZED: case MS_READY: if (cs->cur_at_seq || !list_empty(&cs->temp_at_states) || @@ -1152,20 +1152,20 @@ static int do_lock(struct cardstate *cs) return -EBUSY; } - mode = atomic_read(&cs->mode); - atomic_set(&cs->mstate, MS_LOCKED); - atomic_set(&cs->mode, M_UNKNOWN); + mode = cs->mode; + cs->mstate = MS_LOCKED; + cs->mode = M_UNKNOWN; return mode; } static int do_unlock(struct cardstate *cs) { - if (atomic_read(&cs->mstate) != MS_LOCKED) + if (cs->mstate != MS_LOCKED) return -EINVAL; - atomic_set(&cs->mstate, MS_UNINITIALIZED); - atomic_set(&cs->mode, M_UNKNOWN); + cs->mstate = MS_UNINITIALIZED; + cs->mode = M_UNKNOWN; gigaset_free_channels(cs); if (cs->connected) schedule_init(cs, MS_INIT); @@ -1198,17 +1198,17 @@ static void do_action(int action, struct cardstate *cs, case ACT_INIT: cs->at_state.pending_commands &= ~PC_INIT; cs->cur_at_seq = SEQ_NONE; - atomic_set(&cs->mode, M_UNIMODEM); + cs->mode = M_UNIMODEM; spin_lock_irqsave(&cs->lock, flags); if (!cs->cidmode) { spin_unlock_irqrestore(&cs->lock, flags); gigaset_free_channels(cs); - atomic_set(&cs->mstate, MS_READY); + cs->mstate = MS_READY; break; } spin_unlock_irqrestore(&cs->lock, flags); cs->at_state.pending_commands |= PC_CIDMODE; - atomic_set(&cs->commands_pending, 1); + cs->commands_pending = 1; gig_dbg(DEBUG_CMD, "Scheduling PC_CIDMODE"); break; case ACT_FAILINIT: @@ -1234,22 +1234,20 @@ static void do_action(int action, struct cardstate *cs, | INS_command; break; case ACT_CMODESET: - if (atomic_read(&cs->mstate) == MS_INIT || - atomic_read(&cs->mstate) == MS_RECOVER) { + if (cs->mstate == MS_INIT || cs->mstate == MS_RECOVER) { gigaset_free_channels(cs); - atomic_set(&cs->mstate, MS_READY); + cs->mstate = MS_READY; } - atomic_set(&cs->mode, M_CID); + cs->mode = M_CID; cs->cur_at_seq = SEQ_NONE; break; case ACT_UMODESET: - atomic_set(&cs->mode, M_UNIMODEM); + cs->mode = M_UNIMODEM; cs->cur_at_seq = SEQ_NONE; break; case ACT_FAILCMODE: cs->cur_at_seq = SEQ_NONE; - if (atomic_read(&cs->mstate) == MS_INIT || - atomic_read(&cs->mstate) == MS_RECOVER) { + if (cs->mstate == MS_INIT || cs->mstate == MS_RECOVER) { init_failed(cs, M_UNKNOWN); break; } @@ -1307,7 +1305,7 @@ static void do_action(int action, struct cardstate *cs, case ACT_CONNECT: if (cs->onechannel) { at_state->pending_commands |= PC_DLE1; - atomic_set(&cs->commands_pending, 1); + cs->commands_pending = 1; break; } bcs->chstate |= CHS_D_UP; @@ -1333,7 +1331,7 @@ static void do_action(int action, struct cardstate *cs, * DLE only used for M10x with one B channel. */ at_state->pending_commands |= PC_DLE0; - atomic_set(&cs->commands_pending, 1); + cs->commands_pending = 1; } else disconnect(p_at_state); break; @@ -1369,7 +1367,7 @@ static void do_action(int action, struct cardstate *cs, "Could not enter DLE mode. Trying to hang up.\n"); channel = cs->curchannel; cs->bcs[channel].at_state.pending_commands |= PC_HUP; - atomic_set(&cs->commands_pending, 1); + cs->commands_pending = 1; break; case ACT_CID: /* got cid; start dialing */ @@ -1379,7 +1377,7 @@ static void do_action(int action, struct cardstate *cs, cs->bcs[channel].at_state.cid = ev->parameter; cs->bcs[channel].at_state.pending_commands |= PC_DIAL; - atomic_set(&cs->commands_pending, 1); + cs->commands_pending = 1; break; } /* fall through */ @@ -1411,14 +1409,14 @@ static void do_action(int action, struct cardstate *cs, case ACT_ABORTDIAL: /* error/timeout during dial preparation */ cs->cur_at_seq = SEQ_NONE; at_state->pending_commands |= PC_HUP; - atomic_set(&cs->commands_pending, 1); + cs->commands_pending = 1; break; case ACT_REMOTEREJECT: /* DISCONNECT_IND after dialling */ case ACT_CONNTIMEOUT: /* timeout waiting for ZSAU=ACTIVE */ case ACT_REMOTEHUP: /* DISCONNECT_IND with established connection */ at_state->pending_commands |= PC_HUP; - atomic_set(&cs->commands_pending, 1); + cs->commands_pending = 1; break; case ACT_GETSTRING: /* warning: RING, ZDLE, ... are not handled properly anymore */ @@ -1515,7 +1513,7 @@ static void do_action(int action, struct cardstate *cs, break; case ACT_HUP: at_state->pending_commands |= PC_HUP; - atomic_set(&cs->commands_pending, 1); + cs->commands_pending = 1; gig_dbg(DEBUG_CMD, "Scheduling PC_HUP"); break; @@ -1558,7 +1556,7 @@ static void do_action(int action, struct cardstate *cs, cs->at_state.pending_commands |= PC_UMMODE; gig_dbg(DEBUG_CMD, "Scheduling PC_UMMODE"); } - atomic_set(&cs->commands_pending, 1); + cs->commands_pending = 1; } spin_unlock_irqrestore(&cs->lock, flags); cs->waiting = 0; @@ -1741,7 +1739,7 @@ static void process_command_flags(struct cardstate *cs) int sequence; unsigned long flags; - atomic_set(&cs->commands_pending, 0); + cs->commands_pending = 0; if (cs->cur_at_seq) { gig_dbg(DEBUG_CMD, "not searching scheduled commands: busy"); @@ -1779,7 +1777,7 @@ static void process_command_flags(struct cardstate *cs) ~(PC_DLE1 | PC_ACCEPT | PC_DIAL); if (at_state->cid > 0) at_state->pending_commands |= PC_HUP; - if (atomic_read(&cs->mstate) == MS_RECOVER) { + if (cs->mstate == MS_RECOVER) { if (at_state->pending_commands & PC_CID) { at_state->pending_commands |= PC_NOCID; at_state->pending_commands &= ~PC_CID; @@ -1793,7 +1791,7 @@ static void process_command_flags(struct cardstate *cs) if (cs->at_state.pending_commands == PC_UMMODE && !cs->cidmode && list_empty(&cs->temp_at_states) - && atomic_read(&cs->mode) == M_CID) { + && cs->mode == M_CID) { sequence = SEQ_UMMODE; at_state = &cs->at_state; for (i = 0; i < cs->channels; ++i) { @@ -1860,7 +1858,7 @@ static void process_command_flags(struct cardstate *cs) } if (cs->at_state.pending_commands & PC_CIDMODE) { cs->at_state.pending_commands &= ~PC_CIDMODE; - if (atomic_read(&cs->mode) == M_UNIMODEM) { + if (cs->mode == M_UNIMODEM) { cs->retry_count = 1; schedule_sequence(cs, &cs->at_state, SEQ_CIDMODE); return; @@ -1886,11 +1884,11 @@ static void process_command_flags(struct cardstate *cs) return; } if (bcs->at_state.pending_commands & PC_CID) { - switch (atomic_read(&cs->mode)) { + switch (cs->mode) { case M_UNIMODEM: cs->at_state.pending_commands |= PC_CIDMODE; gig_dbg(DEBUG_CMD, "Scheduling PC_CIDMODE"); - atomic_set(&cs->commands_pending, 1); + cs->commands_pending = 1; return; #ifdef GIG_MAYINITONDIAL case M_UNKNOWN: @@ -1926,7 +1924,7 @@ static void process_events(struct cardstate *cs) for (i = 0; i < 2 * MAX_EVENTS; ++i) { tail = cs->ev_tail; if (tail == head) { - if (!check_flags && !atomic_read(&cs->commands_pending)) + if (!check_flags && !cs->commands_pending) break; check_flags = 0; spin_unlock_irqrestore(&cs->ev_lock, flags); @@ -1934,7 +1932,7 @@ static void process_events(struct cardstate *cs) spin_lock_irqsave(&cs->ev_lock, flags); tail = cs->ev_tail; if (tail == head) { - if (!atomic_read(&cs->commands_pending)) + if (!cs->commands_pending) break; continue; } @@ -1971,7 +1969,7 @@ void gigaset_handle_event(unsigned long data) struct cardstate *cs = (struct cardstate *) data; /* handle incoming data on control/common channel */ - if (atomic_read(&cs->inbuf->head) != atomic_read(&cs->inbuf->tail)) { + if (cs->inbuf->head != cs->inbuf->tail) { gig_dbg(DEBUG_INTR, "processing new data"); cs->ops->handle_input(cs->inbuf); } diff --git a/drivers/isdn/gigaset/gigaset.h b/drivers/isdn/gigaset/gigaset.h index 02bdaf22d7e..f365993161f 100644 --- a/drivers/isdn/gigaset/gigaset.h +++ b/drivers/isdn/gigaset/gigaset.h @@ -70,22 +70,13 @@ extern int gigaset_debuglevel; /* "needs" cast to (enum debuglevel) */ -/* any combination of these can be given with the 'debug=' parameter to insmod, - * e.g. 'insmod usb_gigaset.o debug=0x2c' will set DEBUG_OPEN, DEBUG_CMD and - * DEBUG_INTR. - */ +/* debug flags, combine by adding/bitwise OR */ enum debuglevel { - DEBUG_REG = 0x0002, /* serial port I/O register operations */ - DEBUG_OPEN = 0x0004, /* open/close serial port */ - DEBUG_INTR = 0x0008, /* interrupt processing */ - DEBUG_INTR_DUMP = 0x0010, /* Activating hexdump debug output on - interrupt requests, not available as - run-time option */ + DEBUG_INTR = 0x00008, /* interrupt processing */ DEBUG_CMD = 0x00020, /* sent/received LL commands */ DEBUG_STREAM = 0x00040, /* application data stream I/O events */ DEBUG_STREAM_DUMP = 0x00080, /* application data stream content */ DEBUG_LLDATA = 0x00100, /* sent/received LL data */ - DEBUG_INTR_0 = 0x00200, /* serial port interrupt processing */ DEBUG_DRIVER = 0x00400, /* driver structure */ DEBUG_HDLC = 0x00800, /* M10x HDLC processing */ DEBUG_WRITE = 0x01000, /* M105 data write */ @@ -93,7 +84,7 @@ enum debuglevel { DEBUG_MCMD = 0x04000, /* COMMANDS THAT ARE SENT VERY OFTEN */ DEBUG_INIT = 0x08000, /* (de)allocation+initialization of data structures */ - DEBUG_LOCK = 0x10000, /* semaphore operations */ + DEBUG_SUSPEND = 0x10000, /* suspend/resume processing */ DEBUG_OUTPUT = 0x20000, /* output to device */ DEBUG_ISO = 0x40000, /* isochronous transfers */ DEBUG_IF = 0x80000, /* character device operations */ @@ -191,6 +182,9 @@ void gigaset_dbg_buffer(enum debuglevel level, const unsigned char *msg, #define HD_OPEN_ATCHANNEL (0x28) // 3070 #define HD_CLOSE_ATCHANNEL (0x29) // 3070 +/* number of B channels supported by base driver */ +#define BAS_CHANNELS 2 + /* USB frames for isochronous transfer */ #define BAS_FRAMETIME 1 /* number of milliseconds between frames */ #define BAS_NUMFRAMES 8 /* number of frames per URB */ @@ -313,7 +307,7 @@ struct inbuf_t { struct bc_state *bcs; struct cardstate *cs; int inputstate; - atomic_t head, tail; + int head, tail; unsigned char data[RBUFSIZE]; }; @@ -335,9 +329,9 @@ struct inbuf_t { * are also filled with that value */ struct isowbuf_t { - atomic_t read; - atomic_t nextread; - atomic_t write; + int read; + int nextread; + int write; atomic_t writesem; int wbits; unsigned char data[BAS_OUTBUFSIZE + BAS_OUTBUFPAD]; @@ -350,11 +344,13 @@ struct isowbuf_t { * - urb: pointer to the URB itself * - bcs: pointer to the B Channel control structure * - limit: end of write buffer area covered by this URB + * - status: URB completion status */ struct isow_urbctx_t { struct urb *urb; struct bc_state *bcs; int limit; + int status; }; /* AT state structure @@ -439,14 +435,15 @@ struct cardstate { unsigned minor_index; struct device *dev; struct device *tty_dev; + unsigned flags; const struct gigaset_ops *ops; /* Stuff to handle communication */ wait_queue_head_t waitqueue; int waiting; - atomic_t mode; /* see M_XXXX */ - atomic_t mstate; /* Modem state: see MS_XXXX */ + int mode; /* see M_XXXX */ + int mstate; /* Modem state: see MS_XXXX */ /* only changed by the event layer */ int cmd_result; @@ -503,7 +500,7 @@ struct cardstate { processed */ int curchannel; /* channel those commands are meant for */ - atomic_t commands_pending; /* flag(s) in xxx.commands_pending have + int commands_pending; /* flag(s) in xxx.commands_pending have been set */ struct tasklet_struct event_tasklet; /* tasklet for serializing AT commands. @@ -543,7 +540,6 @@ struct gigaset_driver { unsigned minor; unsigned minors; struct cardstate *cs; - unsigned *flags; int blocked; const struct gigaset_ops *ops; @@ -559,7 +555,7 @@ struct cmdbuf_t { struct bas_bc_state { /* isochronous output state */ - atomic_t running; + int running; atomic_t corrbytes; spinlock_t isooutlock; struct isow_urbctx_t isoouturbs[BAS_OUTURBS]; @@ -574,6 +570,7 @@ struct bas_bc_state { struct urb *isoinurbs[BAS_INURBS]; unsigned char isoinbuf[BAS_INBUFSIZE * BAS_INURBS]; struct urb *isoindone; /* completed isoc read URB */ + int isoinstatus; /* status of completed URB */ int loststatus; /* status of dropped URB */ unsigned isoinlost; /* number of bytes lost */ /* state of bit unstuffing algorithm @@ -770,10 +767,6 @@ void gigaset_freedriver(struct gigaset_driver *drv); void gigaset_debugdrivers(void); struct cardstate *gigaset_get_cs_by_tty(struct tty_struct *tty); struct cardstate *gigaset_get_cs_by_id(int id); - -/* For drivers without fixed assignment device<->cardstate (usb) */ -struct cardstate *gigaset_getunassignedcs(struct gigaset_driver *drv); -void gigaset_unassign(struct cardstate *cs); void gigaset_blockdriver(struct gigaset_driver *drv); /* Allocate and initialize card state. Calls hardware dependent @@ -792,7 +785,7 @@ int gigaset_start(struct cardstate *cs); void gigaset_stop(struct cardstate *cs); /* Tell common.c that the driver is being unloaded. */ -void gigaset_shutdown(struct cardstate *cs); +int gigaset_shutdown(struct cardstate *cs); /* Tell common.c that an skb has been sent. */ void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb); diff --git a/drivers/isdn/gigaset/interface.c b/drivers/isdn/gigaset/interface.c index eb50f3dab5f..af195b07c19 100644 --- a/drivers/isdn/gigaset/interface.c +++ b/drivers/isdn/gigaset/interface.c @@ -28,12 +28,11 @@ static int if_lock(struct cardstate *cs, int *arg) return -EINVAL; if (cmd < 0) { - *arg = atomic_read(&cs->mstate) == MS_LOCKED; //FIXME remove? + *arg = cs->mstate == MS_LOCKED; return 0; } - if (!cmd && atomic_read(&cs->mstate) == MS_LOCKED - && cs->connected) { + if (!cmd && cs->mstate == MS_LOCKED && cs->connected) { cs->ops->set_modem_ctrl(cs, 0, TIOCM_DTR|TIOCM_RTS); cs->ops->baud_rate(cs, B115200); cs->ops->set_line_ctrl(cs, CS8); @@ -104,7 +103,7 @@ static int if_config(struct cardstate *cs, int *arg) if (*arg != 1) return -EINVAL; - if (atomic_read(&cs->mstate) != MS_LOCKED) + if (cs->mstate != MS_LOCKED) return -EBUSY; if (!cs->connected) { @@ -162,7 +161,7 @@ static int if_open(struct tty_struct *tty, struct file *filp) tty->driver_data = NULL; cs = gigaset_get_cs_by_tty(tty); - if (!cs) + if (!cs || !try_module_get(cs->driver->owner)) return -ENODEV; if (mutex_lock_interruptible(&cs->mutex)) @@ -208,6 +207,8 @@ static void if_close(struct tty_struct *tty, struct file *filp) } mutex_unlock(&cs->mutex); + + module_put(cs->driver->owner); } static int if_ioctl(struct tty_struct *tty, struct file *file, @@ -364,7 +365,7 @@ static int if_write(struct tty_struct *tty, const unsigned char *buf, int count) if (!cs->open_count) warn("%s: device not opened", __func__); - else if (atomic_read(&cs->mstate) != MS_LOCKED) { + else if (cs->mstate != MS_LOCKED) { warn("can't write to unlocked device"); retval = -EBUSY; } else if (!cs->connected) { @@ -398,9 +399,9 @@ static int if_write_room(struct tty_struct *tty) if (!cs->open_count) warn("%s: device not opened", __func__); - else if (atomic_read(&cs->mstate) != MS_LOCKED) { + else if (cs->mstate != MS_LOCKED) { warn("can't write to unlocked device"); - retval = -EBUSY; //FIXME + retval = -EBUSY; } else if (!cs->connected) { gig_dbg(DEBUG_ANY, "can't write to unplugged device"); retval = -EBUSY; //FIXME @@ -430,7 +431,7 @@ static int if_chars_in_buffer(struct tty_struct *tty) if (!cs->open_count) warn("%s: device not opened", __func__); - else if (atomic_read(&cs->mstate) != MS_LOCKED) { + else if (cs->mstate != MS_LOCKED) { warn("can't write to unlocked device"); retval = -EBUSY; } else if (!cs->connected) { diff --git a/drivers/isdn/gigaset/isocdata.c b/drivers/isdn/gigaset/isocdata.c index e0505f23880..e30a7773f93 100644 --- a/drivers/isdn/gigaset/isocdata.c +++ b/drivers/isdn/gigaset/isocdata.c @@ -23,9 +23,9 @@ */ void gigaset_isowbuf_init(struct isowbuf_t *iwb, unsigned char idle) { - atomic_set(&iwb->read, 0); - atomic_set(&iwb->nextread, 0); - atomic_set(&iwb->write, 0); + iwb->read = 0; + iwb->nextread = 0; + iwb->write = 0; atomic_set(&iwb->writesem, 1); iwb->wbits = 0; iwb->idle = idle; @@ -39,8 +39,8 @@ static inline int isowbuf_freebytes(struct isowbuf_t *iwb) { int read, write, freebytes; - read = atomic_read(&iwb->read); - write = atomic_read(&iwb->write); + read = iwb->read; + write = iwb->write; if ((freebytes = read - write) > 0) { /* no wraparound: need padding space within regular area */ return freebytes - BAS_OUTBUFPAD; @@ -62,7 +62,7 @@ static inline int isowbuf_poscmp(struct isowbuf_t *iwb, int a, int b) int read; if (a == b) return 0; - read = atomic_read(&iwb->read); + read = iwb->read; if (a < b) { if (a < read && read <= b) return +1; @@ -91,18 +91,18 @@ static inline int isowbuf_startwrite(struct isowbuf_t *iwb) #ifdef CONFIG_GIGASET_DEBUG gig_dbg(DEBUG_ISO, "%s: acquired iso write semaphore, data[write]=%02x, nbits=%d", - __func__, iwb->data[atomic_read(&iwb->write)], iwb->wbits); + __func__, iwb->data[iwb->write], iwb->wbits); #endif return 1; } /* finish writing - * release the write semaphore and update the maximum buffer fill level + * release the write semaphore * returns the current write position */ static inline int isowbuf_donewrite(struct isowbuf_t *iwb) { - int write = atomic_read(&iwb->write); + int write = iwb->write; atomic_inc(&iwb->writesem); return write; } @@ -116,7 +116,7 @@ static inline int isowbuf_donewrite(struct isowbuf_t *iwb) */ static inline void isowbuf_putbits(struct isowbuf_t *iwb, u32 data, int nbits) { - int write = atomic_read(&iwb->write); + int write = iwb->write; data <<= iwb->wbits; data |= iwb->data[write]; nbits += iwb->wbits; @@ -128,7 +128,7 @@ static inline void isowbuf_putbits(struct isowbuf_t *iwb, u32 data, int nbits) } iwb->wbits = nbits; iwb->data[write] = data & 0xff; - atomic_set(&iwb->write, write); + iwb->write = write; } /* put final flag on HDLC bitstream @@ -142,7 +142,7 @@ static inline void isowbuf_putflag(struct isowbuf_t *iwb) /* add two flags, thus reliably covering one byte */ isowbuf_putbits(iwb, 0x7e7e, 8); /* recover the idle flag byte */ - write = atomic_read(&iwb->write); + write = iwb->write; iwb->idle = iwb->data[write]; gig_dbg(DEBUG_ISO, "idle fill byte %02x", iwb->idle); /* mask extraneous bits in buffer */ @@ -160,8 +160,8 @@ int gigaset_isowbuf_getbytes(struct isowbuf_t *iwb, int size) int read, write, limit, src, dst; unsigned char pbyte; - read = atomic_read(&iwb->nextread); - write = atomic_read(&iwb->write); + read = iwb->nextread; + write = iwb->write; if (likely(read == write)) { /* return idle frame */ return read < BAS_OUTBUFPAD ? @@ -176,7 +176,7 @@ int gigaset_isowbuf_getbytes(struct isowbuf_t *iwb, int size) err("invalid size %d", size); return -EINVAL; } - src = atomic_read(&iwb->read); + src = iwb->read; if (unlikely(limit > BAS_OUTBUFSIZE + BAS_OUTBUFPAD || (read < src && limit >= src))) { err("isoc write buffer frame reservation violated"); @@ -191,7 +191,8 @@ int gigaset_isowbuf_getbytes(struct isowbuf_t *iwb, int size) if (!isowbuf_startwrite(iwb)) return -EBUSY; /* write position could have changed */ - if (limit >= (write = atomic_read(&iwb->write))) { + write = iwb->write; + if (limit >= write) { pbyte = iwb->data[write]; /* save partial byte */ limit = write + BAS_OUTBUFPAD; @@ -213,7 +214,7 @@ int gigaset_isowbuf_getbytes(struct isowbuf_t *iwb, int size) __func__, pbyte, limit); iwb->data[limit] = pbyte; /* restore partial byte */ - atomic_set(&iwb->write, limit); + iwb->write = limit; } isowbuf_donewrite(iwb); } @@ -233,7 +234,7 @@ int gigaset_isowbuf_getbytes(struct isowbuf_t *iwb, int size) limit = src; } } - atomic_set(&iwb->nextread, limit); + iwb->nextread = limit; return read; } @@ -477,7 +478,7 @@ static inline int trans_buildframe(struct isowbuf_t *iwb, unsigned char c; if (unlikely(count <= 0)) - return atomic_read(&iwb->write); /* better ideas? */ + return iwb->write; if (isowbuf_freebytes(iwb) < count || !isowbuf_startwrite(iwb)) { @@ -486,13 +487,13 @@ static inline int trans_buildframe(struct isowbuf_t *iwb, } gig_dbg(DEBUG_STREAM, "put %d bytes", count); - write = atomic_read(&iwb->write); + write = iwb->write; do { c = bitrev8(*in++); iwb->data[write++] = c; write %= BAS_OUTBUFSIZE; } while (--count > 0); - atomic_set(&iwb->write, write); + iwb->write = write; iwb->idle = c; return isowbuf_donewrite(iwb); @@ -947,8 +948,8 @@ void gigaset_isoc_input(struct inbuf_t *inbuf) unsigned tail, head, numbytes; unsigned char *src; - head = atomic_read(&inbuf->head); - while (head != (tail = atomic_read(&inbuf->tail))) { + head = inbuf->head; + while (head != (tail = inbuf->tail)) { gig_dbg(DEBUG_INTR, "buffer state: %u -> %u", head, tail); if (head > tail) tail = RBUFSIZE; @@ -956,7 +957,7 @@ void gigaset_isoc_input(struct inbuf_t *inbuf) numbytes = tail - head; gig_dbg(DEBUG_INTR, "processing %u bytes", numbytes); - if (atomic_read(&cs->mstate) == MS_LOCKED) { + if (cs->mstate == MS_LOCKED) { gigaset_dbg_buffer(DEBUG_LOCKCMD, "received response", numbytes, src); gigaset_if_receive(inbuf->cs, src, numbytes); @@ -970,7 +971,7 @@ void gigaset_isoc_input(struct inbuf_t *inbuf) if (head == RBUFSIZE) head = 0; gig_dbg(DEBUG_INTR, "setting head to %u", head); - atomic_set(&inbuf->head, head); + inbuf->head = head; } } diff --git a/drivers/isdn/gigaset/ser-gigaset.c b/drivers/isdn/gigaset/ser-gigaset.c index ea44302e6e7..fceeb1d5768 100644 --- a/drivers/isdn/gigaset/ser-gigaset.c +++ b/drivers/isdn/gigaset/ser-gigaset.c @@ -17,6 +17,7 @@ #include <linux/platform_device.h> #include <linux/tty.h> #include <linux/poll.h> +#include <linux/completion.h> /* Version Information */ #define DRIVER_AUTHOR "Tilman Schmidt" @@ -48,7 +49,7 @@ struct ser_cardstate { struct platform_device dev; struct tty_struct *tty; atomic_t refcnt; - struct mutex dead_mutex; + struct completion dead_cmp; }; static struct platform_driver device_driver = { @@ -240,7 +241,7 @@ static int gigaset_write_cmd(struct cardstate *cs, const unsigned char *buf, struct cmdbuf_t *cb; unsigned long flags; - gigaset_dbg_buffer(atomic_read(&cs->mstate) != MS_LOCKED ? + gigaset_dbg_buffer(cs->mstate != MS_LOCKED ? DEBUG_TRANSCMD : DEBUG_LOCKCMD, "CMD Transmit", len, buf); @@ -498,7 +499,7 @@ static struct cardstate *cs_get(struct tty_struct *tty) static void cs_put(struct cardstate *cs) { if (atomic_dec_and_test(&cs->hw.ser->refcnt)) - mutex_unlock(&cs->hw.ser->dead_mutex); + complete(&cs->hw.ser->dead_cmp); } /* @@ -527,8 +528,8 @@ gigaset_tty_open(struct tty_struct *tty) cs->dev = &cs->hw.ser->dev.dev; cs->hw.ser->tty = tty; - mutex_init(&cs->hw.ser->dead_mutex); atomic_set(&cs->hw.ser->refcnt, 1); + init_completion(&cs->hw.ser->dead_cmp); tty->disc_data = cs; @@ -536,14 +537,13 @@ gigaset_tty_open(struct tty_struct *tty) * startup system and notify the LL that we are ready to run */ if (startmode == SM_LOCKED) - atomic_set(&cs->mstate, MS_LOCKED); + cs->mstate = MS_LOCKED; if (!gigaset_start(cs)) { tasklet_kill(&cs->write_tasklet); goto error; } gig_dbg(DEBUG_INIT, "Startup of HLL done"); - mutex_lock(&cs->hw.ser->dead_mutex); return 0; error: @@ -577,7 +577,7 @@ gigaset_tty_close(struct tty_struct *tty) else { /* wait for running methods to finish */ if (!atomic_dec_and_test(&cs->hw.ser->refcnt)) - mutex_lock(&cs->hw.ser->dead_mutex); + wait_for_completion(&cs->hw.ser->dead_cmp); } /* stop operations */ @@ -714,8 +714,8 @@ gigaset_tty_receive(struct tty_struct *tty, const unsigned char *buf, return; } - tail = atomic_read(&inbuf->tail); - head = atomic_read(&inbuf->head); + tail = inbuf->tail; + head = inbuf->head; gig_dbg(DEBUG_INTR, "buffer state: %u -> %u, receive %u bytes", head, tail, count); @@ -742,7 +742,7 @@ gigaset_tty_receive(struct tty_struct *tty, const unsigned char *buf, } gig_dbg(DEBUG_INTR, "setting tail to %u", tail); - atomic_set(&inbuf->tail, tail); + inbuf->tail = tail; /* Everything was received .. Push data into handler */ gig_dbg(DEBUG_INTR, "%s-->BH", __func__); diff --git a/drivers/isdn/gigaset/usb-gigaset.c b/drivers/isdn/gigaset/usb-gigaset.c index ca4bee173cf..77d20ab0cd4 100644 --- a/drivers/isdn/gigaset/usb-gigaset.c +++ b/drivers/isdn/gigaset/usb-gigaset.c @@ -104,12 +104,17 @@ MODULE_DEVICE_TABLE(usb, gigaset_table); * flags per packet. */ +/* functions called if a device of this driver is connected/disconnected */ static int gigaset_probe(struct usb_interface *interface, const struct usb_device_id *id); static void gigaset_disconnect(struct usb_interface *interface); +/* functions called before/after suspend */ +static int gigaset_suspend(struct usb_interface *intf, pm_message_t message); +static int gigaset_resume(struct usb_interface *intf); +static int gigaset_pre_reset(struct usb_interface *intf); + static struct gigaset_driver *driver = NULL; -static struct cardstate *cardstate = NULL; /* usb specific object needed to register this driver with the usb subsystem */ static struct usb_driver gigaset_usb_driver = { @@ -117,12 +122,17 @@ static struct usb_driver gigaset_usb_driver = { .probe = gigaset_probe, .disconnect = gigaset_disconnect, .id_table = gigaset_table, + .suspend = gigaset_suspend, + .resume = gigaset_resume, + .reset_resume = gigaset_resume, + .pre_reset = gigaset_pre_reset, + .post_reset = gigaset_resume, }; struct usb_cardstate { struct usb_device *udev; /* usb device pointer */ struct usb_interface *interface; /* interface for this device */ - atomic_t busy; /* bulk output in progress */ + int busy; /* bulk output in progress */ /* Output buffer */ unsigned char *bulk_out_buffer; @@ -314,7 +324,7 @@ static void gigaset_modem_fill(unsigned long data) gig_dbg(DEBUG_OUTPUT, "modem_fill"); - if (atomic_read(&cs->hw.usb->busy)) { + if (cs->hw.usb->busy) { gig_dbg(DEBUG_OUTPUT, "modem_fill: busy"); return; } @@ -361,18 +371,13 @@ static void gigaset_read_int_callback(struct urb *urb) { struct inbuf_t *inbuf = urb->context; struct cardstate *cs = inbuf->cs; - int resubmit = 0; + int status = urb->status; int r; unsigned numbytes; unsigned char *src; unsigned long flags; - if (!urb->status) { - if (!cs->connected) { - err("%s: disconnected", __func__); /* should never happen */ - return; - } - + if (!status) { numbytes = urb->actual_length; if (numbytes) { @@ -389,28 +394,26 @@ static void gigaset_read_int_callback(struct urb *urb) } } else gig_dbg(DEBUG_INTR, "Received zero block length"); - resubmit = 1; } else { /* The urb might have been killed. */ - gig_dbg(DEBUG_ANY, "%s - nonzero read bulk status received: %d", - __func__, urb->status); - if (urb->status != -ENOENT) { /* not killed */ - if (!cs->connected) { - err("%s: disconnected", __func__); /* should never happen */ - return; - } - resubmit = 1; - } + gig_dbg(DEBUG_ANY, "%s - nonzero status received: %d", + __func__, status); + if (status == -ENOENT || status == -ESHUTDOWN) + /* killed or endpoint shutdown: don't resubmit */ + return; } - if (resubmit) { - spin_lock_irqsave(&cs->lock, flags); - r = cs->connected ? usb_submit_urb(urb, GFP_ATOMIC) : -ENODEV; + /* resubmit URB */ + spin_lock_irqsave(&cs->lock, flags); + if (!cs->connected) { spin_unlock_irqrestore(&cs->lock, flags); - if (r) - dev_err(cs->dev, "error %d when resubmitting urb.\n", - -r); + err("%s: disconnected", __func__); + return; } + r = usb_submit_urb(urb, GFP_ATOMIC); + spin_unlock_irqrestore(&cs->lock, flags); + if (r) + dev_err(cs->dev, "error %d resubmitting URB\n", -r); } @@ -418,19 +421,28 @@ static void gigaset_read_int_callback(struct urb *urb) static void gigaset_write_bulk_callback(struct urb *urb) { struct cardstate *cs = urb->context; + int status = urb->status; unsigned long flags; - if (urb->status) + switch (status) { + case 0: /* normal completion */ + break; + case -ENOENT: /* killed */ + gig_dbg(DEBUG_ANY, "%s: killed", __func__); + cs->hw.usb->busy = 0; + return; + default: dev_err(cs->dev, "bulk transfer failed (status %d)\n", - -urb->status); + -status); /* That's all we can do. Communication problems are handled by timeouts or network protocols. */ + } spin_lock_irqsave(&cs->lock, flags); if (!cs->connected) { err("%s: not connected", __func__); } else { - atomic_set(&cs->hw.usb->busy, 0); + cs->hw.usb->busy = 0; tasklet_schedule(&cs->write_tasklet); } spin_unlock_irqrestore(&cs->lock, flags); @@ -478,14 +490,14 @@ static int send_cb(struct cardstate *cs, struct cmdbuf_t *cb) cb->offset += count; cb->len -= count; - atomic_set(&ucs->busy, 1); + ucs->busy = 1; spin_lock_irqsave(&cs->lock, flags); status = cs->connected ? usb_submit_urb(ucs->bulk_out_urb, GFP_ATOMIC) : -ENODEV; spin_unlock_irqrestore(&cs->lock, flags); if (status) { - atomic_set(&ucs->busy, 0); + ucs->busy = 0; err("could not submit urb (error %d)\n", -status); cb->len = 0; /* skip urb => remove cb+wakeup @@ -504,7 +516,7 @@ static int gigaset_write_cmd(struct cardstate *cs, const unsigned char *buf, struct cmdbuf_t *cb; unsigned long flags; - gigaset_dbg_buffer(atomic_read(&cs->mstate) != MS_LOCKED ? + gigaset_dbg_buffer(cs->mstate != MS_LOCKED ? DEBUG_TRANSCMD : DEBUG_LOCKCMD, "CMD Transmit", len, buf); @@ -641,7 +653,7 @@ static int write_modem(struct cardstate *cs) count = min(bcs->tx_skb->len, (unsigned) ucs->bulk_out_size); skb_copy_from_linear_data(bcs->tx_skb, ucs->bulk_out_buffer, count); skb_pull(bcs->tx_skb, count); - atomic_set(&ucs->busy, 1); + ucs->busy = 1; gig_dbg(DEBUG_OUTPUT, "write_modem: send %d bytes", count); spin_lock_irqsave(&cs->lock, flags); @@ -659,7 +671,7 @@ static int write_modem(struct cardstate *cs) if (ret) { err("could not submit urb (error %d)\n", -ret); - atomic_set(&ucs->busy, 0); + ucs->busy = 0; } if (!bcs->tx_skb->len) { @@ -680,53 +692,44 @@ static int gigaset_probe(struct usb_interface *interface, { int retval; struct usb_device *udev = interface_to_usbdev(interface); - unsigned int ifnum; - struct usb_host_interface *hostif; + struct usb_host_interface *hostif = interface->cur_altsetting; struct cardstate *cs = NULL; struct usb_cardstate *ucs = NULL; struct usb_endpoint_descriptor *endpoint; int buffer_size; - int alt; - gig_dbg(DEBUG_ANY, - "%s: Check if device matches .. (Vendor: 0x%x, Product: 0x%x)", - __func__, le16_to_cpu(udev->descriptor.idVendor), - le16_to_cpu(udev->descriptor.idProduct)); - - retval = -ENODEV; //FIXME + gig_dbg(DEBUG_ANY, "%s: Check if device matches ...", __func__); /* See if the device offered us matches what we can accept */ if ((le16_to_cpu(udev->descriptor.idVendor) != USB_M105_VENDOR_ID) || - (le16_to_cpu(udev->descriptor.idProduct) != USB_M105_PRODUCT_ID)) + (le16_to_cpu(udev->descriptor.idProduct) != USB_M105_PRODUCT_ID)) { + gig_dbg(DEBUG_ANY, "device ID (0x%x, 0x%x) not for me - skip", + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct)); return -ENODEV; - - /* this starts to become ascii art... */ - hostif = interface->cur_altsetting; - alt = hostif->desc.bAlternateSetting; - ifnum = hostif->desc.bInterfaceNumber; // FIXME ? - - if (alt != 0 || ifnum != 0) { - dev_warn(&udev->dev, "ifnum %d, alt %d\n", ifnum, alt); + } + if (hostif->desc.bInterfaceNumber != 0) { + gig_dbg(DEBUG_ANY, "interface %d not for me - skip", + hostif->desc.bInterfaceNumber); + return -ENODEV; + } + if (hostif->desc.bAlternateSetting != 0) { + dev_notice(&udev->dev, "unsupported altsetting %d - skip", + hostif->desc.bAlternateSetting); return -ENODEV; } - - /* Reject application specific intefaces - * - */ if (hostif->desc.bInterfaceClass != 255) { - dev_info(&udev->dev, - "%s: Device matched but iface_desc[%d]->bInterfaceClass==%d!\n", - __func__, ifnum, hostif->desc.bInterfaceClass); + dev_notice(&udev->dev, "unsupported interface class %d - skip", + hostif->desc.bInterfaceClass); return -ENODEV; } dev_info(&udev->dev, "%s: Device matched ... !\n", __func__); - cs = gigaset_getunassignedcs(driver); - if (!cs) { - dev_warn(&udev->dev, "no free cardstate\n"); + /* allocate memory for our device state and intialize it */ + cs = gigaset_initcs(driver, 1, 1, 0, cidmode, GIGASET_MODULENAME); + if (!cs) return -ENODEV; - } ucs = cs->hw.usb; /* save off device structure ptrs for later use */ @@ -759,7 +762,7 @@ static int gigaset_probe(struct usb_interface *interface, endpoint = &hostif->endpoint[1].desc; - atomic_set(&ucs->busy, 0); + ucs->busy = 0; ucs->read_urb = usb_alloc_urb(0, GFP_KERNEL); if (!ucs->read_urb) { @@ -792,7 +795,7 @@ static int gigaset_probe(struct usb_interface *interface, /* tell common part that the device is ready */ if (startmode == SM_LOCKED) - atomic_set(&cs->mstate, MS_LOCKED); + cs->mstate = MS_LOCKED; if (!gigaset_start(cs)) { tasklet_kill(&cs->write_tasklet); @@ -813,7 +816,7 @@ error: usb_put_dev(ucs->udev); ucs->udev = NULL; ucs->interface = NULL; - gigaset_unassign(cs); + gigaset_freecs(cs); return retval; } @@ -824,6 +827,9 @@ static void gigaset_disconnect(struct usb_interface *interface) cs = usb_get_intfdata(interface); ucs = cs->hw.usb; + + dev_info(cs->dev, "disconnecting Gigaset USB adapter\n"); + usb_kill_urb(ucs->read_urb); gigaset_stop(cs); @@ -831,7 +837,7 @@ static void gigaset_disconnect(struct usb_interface *interface) usb_set_intfdata(interface, NULL); tasklet_kill(&cs->write_tasklet); - usb_kill_urb(ucs->bulk_out_urb); /* FIXME: only if active? */ + usb_kill_urb(ucs->bulk_out_urb); kfree(ucs->bulk_out_buffer); usb_free_urb(ucs->bulk_out_urb); @@ -844,7 +850,53 @@ static void gigaset_disconnect(struct usb_interface *interface) ucs->interface = NULL; ucs->udev = NULL; cs->dev = NULL; - gigaset_unassign(cs); + gigaset_freecs(cs); +} + +/* gigaset_suspend + * This function is called before the USB connection is suspended or reset. + */ +static int gigaset_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct cardstate *cs = usb_get_intfdata(intf); + + /* stop activity */ + cs->connected = 0; /* prevent rescheduling */ + usb_kill_urb(cs->hw.usb->read_urb); + tasklet_kill(&cs->write_tasklet); + usb_kill_urb(cs->hw.usb->bulk_out_urb); + + gig_dbg(DEBUG_SUSPEND, "suspend complete"); + return 0; +} + +/* gigaset_resume + * This function is called after the USB connection has been resumed or reset. + */ +static int gigaset_resume(struct usb_interface *intf) +{ + struct cardstate *cs = usb_get_intfdata(intf); + int rc; + + /* resubmit interrupt URB */ + cs->connected = 1; + rc = usb_submit_urb(cs->hw.usb->read_urb, GFP_KERNEL); + if (rc) { + dev_err(cs->dev, "Could not submit read URB (error %d)\n", -rc); + return rc; + } + + gig_dbg(DEBUG_SUSPEND, "resume complete"); + return 0; +} + +/* gigaset_pre_reset + * This function is called before the USB connection is reset. + */ +static int gigaset_pre_reset(struct usb_interface *intf) +{ + /* same as suspend */ + return gigaset_suspend(intf, PMSG_ON); } static const struct gigaset_ops ops = { @@ -880,11 +932,6 @@ static int __init usb_gigaset_init(void) &ops, THIS_MODULE)) == NULL) goto error; - /* allocate memory for our device state and intialize it */ - cardstate = gigaset_initcs(driver, 1, 1, 0, cidmode, GIGASET_MODULENAME); - if (!cardstate) - goto error; - /* register this driver with the USB subsystem */ result = usb_register(&gigaset_usb_driver); if (result < 0) { @@ -897,9 +944,7 @@ static int __init usb_gigaset_init(void) info(DRIVER_DESC); return 0; -error: if (cardstate) - gigaset_freecs(cardstate); - cardstate = NULL; +error: if (driver) gigaset_freedriver(driver); driver = NULL; @@ -913,11 +958,16 @@ error: if (cardstate) */ static void __exit usb_gigaset_exit(void) { + int i; + gigaset_blockdriver(driver); /* => probe will fail * => no gigaset_start any more */ - gigaset_shutdown(cardstate); + /* stop all connected devices */ + for (i = 0; i < driver->minors; i++) + gigaset_shutdown(driver->cs + i); + /* from now on, no isdn callback should be possible */ /* deregister this driver with the USB subsystem */ @@ -925,8 +975,6 @@ static void __exit usb_gigaset_exit(void) /* this will call the disconnect-callback */ /* from now on, no disconnect/probe callback should be running */ - gigaset_freecs(cardstate); - cardstate = NULL; gigaset_freedriver(driver); driver = NULL; } diff --git a/drivers/isdn/hardware/eicon/debug.c b/drivers/isdn/hardware/eicon/debug.c index 0db9cc661e2..84318ec8d13 100644 --- a/drivers/isdn/hardware/eicon/debug.c +++ b/drivers/isdn/hardware/eicon/debug.c @@ -1188,7 +1188,7 @@ int SuperTraceASSIGN (void* AdapterHandle, byte* data) { if ((features[0] & DIVA_XDI_EXTENDED_FEATURES_VALID) && (features[0] & DIVA_XDI_EXTENDED_FEATURE_MANAGEMENT_DMA)) { - dword rx_dma_magic; + dword uninitialized_var(rx_dma_magic); if ((pC->dma_handle = diva_get_dma_descriptor (pC->request, &rx_dma_magic)) >= 0) { pC->xbuffer[0] = LLI; pC->xbuffer[1] = 8; diff --git a/drivers/isdn/hardware/eicon/diva.c b/drivers/isdn/hardware/eicon/diva.c index ffa2afa77c2..1403a5458e6 100644 --- a/drivers/isdn/hardware/eicon/diva.c +++ b/drivers/isdn/hardware/eicon/diva.c @@ -515,12 +515,11 @@ diva_xdi_read(void *adapter, void *os_handle, void __user *dst, irqreturn_t diva_os_irq_wrapper(int irq, void *context) { - diva_os_xdi_adapter_t *a = (diva_os_xdi_adapter_t *) context; + diva_os_xdi_adapter_t *a = context; diva_xdi_clear_interrupts_proc_t clear_int_proc; - if (!a || !a->xdi_adapter.diva_isr_handler) { + if (!a || !a->xdi_adapter.diva_isr_handler) return IRQ_NONE; - } if ((clear_int_proc = a->clear_interrupts_proc)) { (*clear_int_proc) (a); diff --git a/drivers/isdn/hardware/eicon/message.c b/drivers/isdn/hardware/eicon/message.c index b9177ca4369..1ff98e7eb79 100644 --- a/drivers/isdn/hardware/eicon/message.c +++ b/drivers/isdn/hardware/eicon/message.c @@ -9027,7 +9027,7 @@ static byte AddInfo(byte **add_i, /* facility is a nested structure */ /* FTY can be more than once */ - if(esc_chi[0] && !(esc_chi[esc_chi[0]])&0x7f ) + if (esc_chi[0] && !(esc_chi[esc_chi[0]] & 0x7f)) { add_i[0] = (byte *)"\x02\x02\x00"; /* use neither b nor d channel */ } diff --git a/drivers/isdn/hisax/avm_pci.c b/drivers/isdn/hisax/avm_pci.c index 035d158779d..0f1db1f669b 100644 --- a/drivers/isdn/hisax/avm_pci.c +++ b/drivers/isdn/hisax/avm_pci.c @@ -263,11 +263,7 @@ hdlc_empty_fifo(struct BCState *bcs, int count) outl(idx, cs->hw.avm.cfg_reg + 4); while (cnt < count) { #ifdef __powerpc__ -#ifdef CONFIG_APUS - *ptr++ = in_le32((unsigned *)(cs->hw.avm.isac +_IO_BASE)); -#else *ptr++ = in_be32((unsigned *)(cs->hw.avm.isac +_IO_BASE)); -#endif /* CONFIG_APUS */ #else *ptr++ = inl(cs->hw.avm.isac); #endif /* __powerpc__ */ @@ -328,11 +324,7 @@ hdlc_fill_fifo(struct BCState *bcs) if (cs->subtyp == AVM_FRITZ_PCI) { while (cnt<count) { #ifdef __powerpc__ -#ifdef CONFIG_APUS - out_le32((unsigned *)(cs->hw.avm.isac +_IO_BASE), *ptr++); -#else out_be32((unsigned *)(cs->hw.avm.isac +_IO_BASE), *ptr++); -#endif /* CONFIG_APUS */ #else outl(*ptr++, cs->hw.avm.isac); #endif /* __powerpc__ */ diff --git a/drivers/isdn/i4l/isdn_tty.c b/drivers/isdn/i4l/isdn_tty.c index 9cb6e5021ad..133eb18e65c 100644 --- a/drivers/isdn/i4l/isdn_tty.c +++ b/drivers/isdn/i4l/isdn_tty.c @@ -1917,7 +1917,6 @@ isdn_tty_modem_init(void) info->owner = THIS_MODULE; #endif spin_lock_init(&info->readlock); - init_MUTEX(&info->write_sem); sprintf(info->last_cause, "0000"); sprintf(info->last_num, "none"); info->last_dir = 0; diff --git a/drivers/isdn/i4l/isdn_ttyfax.c b/drivers/isdn/i4l/isdn_ttyfax.c index a943d078bac..f93de4a3035 100644 --- a/drivers/isdn/i4l/isdn_ttyfax.c +++ b/drivers/isdn/i4l/isdn_ttyfax.c @@ -834,7 +834,7 @@ isdn_tty_cmd_FCLASS2(char **p, modem_info * info) char *rp = &f->resolution; p[0] += 2; - if (!info->faxonline & 1) /* not outgoing connection */ + if (!(info->faxonline & 1)) /* not outgoing connection */ PARSE_ERROR1; for (i = 0; (((*p[0] >= '0') && (*p[0] <= '9')) || (*p[0] == ',')) && (i < 4); i++) { diff --git a/drivers/isdn/icn/icn.c b/drivers/isdn/icn/icn.c index 82d957bde29..bf7997abc4a 100644 --- a/drivers/isdn/icn/icn.c +++ b/drivers/isdn/icn/icn.c @@ -1302,7 +1302,7 @@ icn_command(isdn_ctrl * c, icn_card * card) } break; case ISDN_CMD_DIAL: - if (!card->flags & ICN_FLAGS_RUNNING) + if (!(card->flags & ICN_FLAGS_RUNNING)) return -ENODEV; if (card->leased) break; @@ -1328,7 +1328,7 @@ icn_command(isdn_ctrl * c, icn_card * card) } break; case ISDN_CMD_ACCEPTD: - if (!card->flags & ICN_FLAGS_RUNNING) + if (!(card->flags & ICN_FLAGS_RUNNING)) return -ENODEV; if (c->arg < ICN_BCH) { a = c->arg + 1; @@ -1348,7 +1348,7 @@ icn_command(isdn_ctrl * c, icn_card * card) } break; case ISDN_CMD_ACCEPTB: - if (!card->flags & ICN_FLAGS_RUNNING) + if (!(card->flags & ICN_FLAGS_RUNNING)) return -ENODEV; if (c->arg < ICN_BCH) { a = c->arg + 1; @@ -1366,7 +1366,7 @@ icn_command(isdn_ctrl * c, icn_card * card) } break; case ISDN_CMD_HANGUP: - if (!card->flags & ICN_FLAGS_RUNNING) + if (!(card->flags & ICN_FLAGS_RUNNING)) return -ENODEV; if (c->arg < ICN_BCH) { a = c->arg + 1; @@ -1375,7 +1375,7 @@ icn_command(isdn_ctrl * c, icn_card * card) } break; case ISDN_CMD_SETEAZ: - if (!card->flags & ICN_FLAGS_RUNNING) + if (!(card->flags & ICN_FLAGS_RUNNING)) return -ENODEV; if (card->leased) break; @@ -1391,7 +1391,7 @@ icn_command(isdn_ctrl * c, icn_card * card) } break; case ISDN_CMD_CLREAZ: - if (!card->flags & ICN_FLAGS_RUNNING) + if (!(card->flags & ICN_FLAGS_RUNNING)) return -ENODEV; if (card->leased) break; @@ -1405,7 +1405,7 @@ icn_command(isdn_ctrl * c, icn_card * card) } break; case ISDN_CMD_SETL2: - if (!card->flags & ICN_FLAGS_RUNNING) + if (!(card->flags & ICN_FLAGS_RUNNING)) return -ENODEV; if ((c->arg & 255) < ICN_BCH) { a = c->arg; @@ -1424,7 +1424,7 @@ icn_command(isdn_ctrl * c, icn_card * card) } break; case ISDN_CMD_SETL3: - if (!card->flags & ICN_FLAGS_RUNNING) + if (!(card->flags & ICN_FLAGS_RUNNING)) return -ENODEV; return 0; default: @@ -1471,7 +1471,7 @@ if_writecmd(const u_char __user *buf, int len, int id, int channel) icn_card *card = icn_findcard(id); if (card) { - if (!card->flags & ICN_FLAGS_RUNNING) + if (!(card->flags & ICN_FLAGS_RUNNING)) return -ENODEV; return (icn_writecmd(buf, len, 1, card)); } @@ -1486,7 +1486,7 @@ if_readstatus(u_char __user *buf, int len, int id, int channel) icn_card *card = icn_findcard(id); if (card) { - if (!card->flags & ICN_FLAGS_RUNNING) + if (!(card->flags & ICN_FLAGS_RUNNING)) return -ENODEV; return (icn_readstatus(buf, len, card)); } @@ -1501,7 +1501,7 @@ if_sendbuf(int id, int channel, int ack, struct sk_buff *skb) icn_card *card = icn_findcard(id); if (card) { - if (!card->flags & ICN_FLAGS_RUNNING) + if (!(card->flags & ICN_FLAGS_RUNNING)) return -ENODEV; return (icn_sendbuf(channel, ack, skb, card)); } diff --git a/drivers/isdn/isdnloop/isdnloop.c b/drivers/isdn/isdnloop/isdnloop.c index bb92e3cd933..655ef9a3f4d 100644 --- a/drivers/isdn/isdnloop/isdnloop.c +++ b/drivers/isdn/isdnloop/isdnloop.c @@ -1184,7 +1184,7 @@ isdnloop_command(isdn_ctrl * c, isdnloop_card * card) } break; case ISDN_CMD_DIAL: - if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + if (!(card->flags & ISDNLOOP_FLAGS_RUNNING)) return -ENODEV; if (card->leased) break; @@ -1210,7 +1210,7 @@ isdnloop_command(isdn_ctrl * c, isdnloop_card * card) } break; case ISDN_CMD_ACCEPTD: - if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + if (!(card->flags & ISDNLOOP_FLAGS_RUNNING)) return -ENODEV; if (c->arg < ISDNLOOP_BCH) { a = c->arg + 1; @@ -1238,7 +1238,7 @@ isdnloop_command(isdn_ctrl * c, isdnloop_card * card) } break; case ISDN_CMD_ACCEPTB: - if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + if (!(card->flags & ISDNLOOP_FLAGS_RUNNING)) return -ENODEV; if (c->arg < ISDNLOOP_BCH) { a = c->arg + 1; @@ -1264,7 +1264,7 @@ isdnloop_command(isdn_ctrl * c, isdnloop_card * card) i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); break; case ISDN_CMD_HANGUP: - if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + if (!(card->flags & ISDNLOOP_FLAGS_RUNNING)) return -ENODEV; if (c->arg < ISDNLOOP_BCH) { a = c->arg + 1; @@ -1273,7 +1273,7 @@ isdnloop_command(isdn_ctrl * c, isdnloop_card * card) } break; case ISDN_CMD_SETEAZ: - if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + if (!(card->flags & ISDNLOOP_FLAGS_RUNNING)) return -ENODEV; if (card->leased) break; @@ -1303,7 +1303,7 @@ isdnloop_command(isdn_ctrl * c, isdnloop_card * card) } break; case ISDN_CMD_SETL2: - if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + if (!(card->flags & ISDNLOOP_FLAGS_RUNNING)) return -ENODEV; if ((c->arg & 255) < ISDNLOOP_BCH) { a = c->arg; @@ -1395,7 +1395,7 @@ if_readstatus(u_char __user *buf, int len, int id, int channel) isdnloop_card *card = isdnloop_findcard(id); if (card) { - if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + if (!(card->flags & ISDNLOOP_FLAGS_RUNNING)) return -ENODEV; return (isdnloop_readstatus(buf, len, card)); } @@ -1410,7 +1410,7 @@ if_sendbuf(int id, int channel, int ack, struct sk_buff *skb) isdnloop_card *card = isdnloop_findcard(id); if (card) { - if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + if (!(card->flags & ISDNLOOP_FLAGS_RUNNING)) return -ENODEV; /* ack request stored in skb scratch area */ *(skb->head) = ack; diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c index 1b1ef3130e6..a0585fb6da9 100644 --- a/drivers/md/bitmap.c +++ b/drivers/md/bitmap.c @@ -237,7 +237,7 @@ static struct page *read_sb_page(mddev_t *mddev, long offset, unsigned long inde if (!page) return ERR_PTR(-ENOMEM); - ITERATE_RDEV(mddev, rdev, tmp) { + rdev_for_each(rdev, tmp, mddev) { if (! test_bit(In_sync, &rdev->flags) || test_bit(Faulty, &rdev->flags)) continue; @@ -261,7 +261,7 @@ static int write_sb_page(struct bitmap *bitmap, struct page *page, int wait) struct list_head *tmp; mddev_t *mddev = bitmap->mddev; - ITERATE_RDEV(mddev, rdev, tmp) + rdev_for_each(rdev, tmp, mddev) if (test_bit(In_sync, &rdev->flags) && !test_bit(Faulty, &rdev->flags)) { int size = PAGE_SIZE; @@ -1348,14 +1348,38 @@ void bitmap_close_sync(struct bitmap *bitmap) */ sector_t sector = 0; int blocks; - if (!bitmap) return; + if (!bitmap) + return; while (sector < bitmap->mddev->resync_max_sectors) { bitmap_end_sync(bitmap, sector, &blocks, 0); -/* - if (sector < 500) printk("bitmap_close_sync: sec %llu blks %d\n", - (unsigned long long)sector, blocks); -*/ sector += blocks; + sector += blocks; + } +} + +void bitmap_cond_end_sync(struct bitmap *bitmap, sector_t sector) +{ + sector_t s = 0; + int blocks; + + if (!bitmap) + return; + if (sector == 0) { + bitmap->last_end_sync = jiffies; + return; + } + if (time_before(jiffies, (bitmap->last_end_sync + + bitmap->daemon_sleep * HZ))) + return; + wait_event(bitmap->mddev->recovery_wait, + atomic_read(&bitmap->mddev->recovery_active) == 0); + + sector &= ~((1ULL << CHUNK_BLOCK_SHIFT(bitmap)) - 1); + s = 0; + while (s < sector && s < bitmap->mddev->resync_max_sectors) { + bitmap_end_sync(bitmap, s, &blocks, 0); + s += blocks; } + bitmap->last_end_sync = jiffies; } static void bitmap_set_memory_bits(struct bitmap *bitmap, sector_t offset, int needed) @@ -1565,3 +1589,4 @@ EXPORT_SYMBOL(bitmap_start_sync); EXPORT_SYMBOL(bitmap_end_sync); EXPORT_SYMBOL(bitmap_unplug); EXPORT_SYMBOL(bitmap_close_sync); +EXPORT_SYMBOL(bitmap_cond_end_sync); diff --git a/drivers/md/faulty.c b/drivers/md/faulty.c index cf2ddce3411..d107ddceefc 100644 --- a/drivers/md/faulty.c +++ b/drivers/md/faulty.c @@ -294,7 +294,7 @@ static int run(mddev_t *mddev) } conf->nfaults = 0; - ITERATE_RDEV(mddev, rdev, tmp) + rdev_for_each(rdev, tmp, mddev) conf->rdev = rdev; mddev->array_size = mddev->size; diff --git a/drivers/md/linear.c b/drivers/md/linear.c index 3dac1cfb818..0b8511776b3 100644 --- a/drivers/md/linear.c +++ b/drivers/md/linear.c @@ -122,7 +122,7 @@ static linear_conf_t *linear_conf(mddev_t *mddev, int raid_disks) cnt = 0; conf->array_size = 0; - ITERATE_RDEV(mddev,rdev,tmp) { + rdev_for_each(rdev, tmp, mddev) { int j = rdev->raid_disk; dev_info_t *disk = conf->disks + j; diff --git a/drivers/md/md.c b/drivers/md/md.c index c28a120b416..5fc326d3970 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -195,7 +195,7 @@ static DEFINE_SPINLOCK(all_mddevs_lock); * Any code which breaks out of this loop while own * a reference to the current mddev and must mddev_put it. */ -#define ITERATE_MDDEV(mddev,tmp) \ +#define for_each_mddev(mddev,tmp) \ \ for (({ spin_lock(&all_mddevs_lock); \ tmp = all_mddevs.next; \ @@ -275,6 +275,7 @@ static mddev_t * mddev_find(dev_t unit) spin_lock_init(&new->write_lock); init_waitqueue_head(&new->sb_wait); new->reshape_position = MaxSector; + new->resync_max = MaxSector; new->queue = blk_alloc_queue(GFP_KERNEL); if (!new->queue) { @@ -310,7 +311,7 @@ static mdk_rdev_t * find_rdev_nr(mddev_t *mddev, int nr) mdk_rdev_t * rdev; struct list_head *tmp; - ITERATE_RDEV(mddev,rdev,tmp) { + rdev_for_each(rdev, tmp, mddev) { if (rdev->desc_nr == nr) return rdev; } @@ -322,7 +323,7 @@ static mdk_rdev_t * find_rdev(mddev_t * mddev, dev_t dev) struct list_head *tmp; mdk_rdev_t *rdev; - ITERATE_RDEV(mddev,rdev,tmp) { + rdev_for_each(rdev, tmp, mddev) { if (rdev->bdev->bd_dev == dev) return rdev; } @@ -773,12 +774,16 @@ static int super_90_validate(mddev_t *mddev, mdk_rdev_t *rdev) __u64 ev1 = md_event(sb); rdev->raid_disk = -1; - rdev->flags = 0; + clear_bit(Faulty, &rdev->flags); + clear_bit(In_sync, &rdev->flags); + clear_bit(WriteMostly, &rdev->flags); + clear_bit(BarriersNotsupp, &rdev->flags); + if (mddev->raid_disks == 0) { mddev->major_version = 0; mddev->minor_version = sb->minor_version; mddev->patch_version = sb->patch_version; - mddev->persistent = ! sb->not_persistent; + mddev->external = 0; mddev->chunk_size = sb->chunk_size; mddev->ctime = sb->ctime; mddev->utime = sb->utime; @@ -904,7 +909,7 @@ static void super_90_sync(mddev_t *mddev, mdk_rdev_t *rdev) sb->size = mddev->size; sb->raid_disks = mddev->raid_disks; sb->md_minor = mddev->md_minor; - sb->not_persistent = !mddev->persistent; + sb->not_persistent = 0; sb->utime = mddev->utime; sb->state = 0; sb->events_hi = (mddev->events>>32); @@ -938,7 +943,7 @@ static void super_90_sync(mddev_t *mddev, mdk_rdev_t *rdev) sb->state |= (1<<MD_SB_BITMAP_PRESENT); sb->disks[0].state = (1<<MD_DISK_REMOVED); - ITERATE_RDEV(mddev,rdev2,tmp) { + rdev_for_each(rdev2, tmp, mddev) { mdp_disk_t *d; int desc_nr; if (rdev2->raid_disk >= 0 && test_bit(In_sync, &rdev2->flags) @@ -1153,11 +1158,15 @@ static int super_1_validate(mddev_t *mddev, mdk_rdev_t *rdev) __u64 ev1 = le64_to_cpu(sb->events); rdev->raid_disk = -1; - rdev->flags = 0; + clear_bit(Faulty, &rdev->flags); + clear_bit(In_sync, &rdev->flags); + clear_bit(WriteMostly, &rdev->flags); + clear_bit(BarriersNotsupp, &rdev->flags); + if (mddev->raid_disks == 0) { mddev->major_version = 1; mddev->patch_version = 0; - mddev->persistent = 1; + mddev->external = 0; mddev->chunk_size = le32_to_cpu(sb->chunksize) << 9; mddev->ctime = le64_to_cpu(sb->ctime) & ((1ULL << 32)-1); mddev->utime = le64_to_cpu(sb->utime) & ((1ULL << 32)-1); @@ -1286,7 +1295,7 @@ static void super_1_sync(mddev_t *mddev, mdk_rdev_t *rdev) } max_dev = 0; - ITERATE_RDEV(mddev,rdev2,tmp) + rdev_for_each(rdev2, tmp, mddev) if (rdev2->desc_nr+1 > max_dev) max_dev = rdev2->desc_nr+1; @@ -1295,7 +1304,7 @@ static void super_1_sync(mddev_t *mddev, mdk_rdev_t *rdev) for (i=0; i<max_dev;i++) sb->dev_roles[i] = cpu_to_le16(0xfffe); - ITERATE_RDEV(mddev,rdev2,tmp) { + rdev_for_each(rdev2, tmp, mddev) { i = rdev2->desc_nr; if (test_bit(Faulty, &rdev2->flags)) sb->dev_roles[i] = cpu_to_le16(0xfffe); @@ -1333,8 +1342,8 @@ static int match_mddev_units(mddev_t *mddev1, mddev_t *mddev2) struct list_head *tmp, *tmp2; mdk_rdev_t *rdev, *rdev2; - ITERATE_RDEV(mddev1,rdev,tmp) - ITERATE_RDEV(mddev2, rdev2, tmp2) + rdev_for_each(rdev, tmp, mddev1) + rdev_for_each(rdev2, tmp2, mddev2) if (rdev->bdev->bd_contains == rdev2->bdev->bd_contains) return 1; @@ -1401,7 +1410,7 @@ static int bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev) goto fail; } list_add(&rdev->same_set, &mddev->disks); - bd_claim_by_disk(rdev->bdev, rdev, mddev->gendisk); + bd_claim_by_disk(rdev->bdev, rdev->bdev->bd_holder, mddev->gendisk); return 0; fail: @@ -1410,10 +1419,11 @@ static int bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev) return err; } -static void delayed_delete(struct work_struct *ws) +static void md_delayed_delete(struct work_struct *ws) { mdk_rdev_t *rdev = container_of(ws, mdk_rdev_t, del_work); kobject_del(&rdev->kobj); + kobject_put(&rdev->kobj); } static void unbind_rdev_from_array(mdk_rdev_t * rdev) @@ -1432,7 +1442,8 @@ static void unbind_rdev_from_array(mdk_rdev_t * rdev) /* We need to delay this, otherwise we can deadlock when * writing to 'remove' to "dev/state" */ - INIT_WORK(&rdev->del_work, delayed_delete); + INIT_WORK(&rdev->del_work, md_delayed_delete); + kobject_get(&rdev->kobj); schedule_work(&rdev->del_work); } @@ -1441,7 +1452,7 @@ static void unbind_rdev_from_array(mdk_rdev_t * rdev) * otherwise reused by a RAID array (or any other kernel * subsystem), by bd_claiming the device. */ -static int lock_rdev(mdk_rdev_t *rdev, dev_t dev) +static int lock_rdev(mdk_rdev_t *rdev, dev_t dev, int shared) { int err = 0; struct block_device *bdev; @@ -1453,13 +1464,15 @@ static int lock_rdev(mdk_rdev_t *rdev, dev_t dev) __bdevname(dev, b)); return PTR_ERR(bdev); } - err = bd_claim(bdev, rdev); + err = bd_claim(bdev, shared ? (mdk_rdev_t *)lock_rdev : rdev); if (err) { printk(KERN_ERR "md: could not bd_claim %s.\n", bdevname(bdev, b)); blkdev_put(bdev); return err; } + if (!shared) + set_bit(AllReserved, &rdev->flags); rdev->bdev = bdev; return err; } @@ -1503,7 +1516,7 @@ static void export_array(mddev_t *mddev) struct list_head *tmp; mdk_rdev_t *rdev; - ITERATE_RDEV(mddev,rdev,tmp) { + rdev_for_each(rdev, tmp, mddev) { if (!rdev->mddev) { MD_BUG(); continue; @@ -1581,17 +1594,17 @@ static void md_print_devices(void) printk("md: **********************************\n"); printk("md: * <COMPLETE RAID STATE PRINTOUT> *\n"); printk("md: **********************************\n"); - ITERATE_MDDEV(mddev,tmp) { + for_each_mddev(mddev, tmp) { if (mddev->bitmap) bitmap_print_sb(mddev->bitmap); else printk("%s: ", mdname(mddev)); - ITERATE_RDEV(mddev,rdev,tmp2) + rdev_for_each(rdev, tmp2, mddev) printk("<%s>", bdevname(rdev->bdev,b)); printk("\n"); - ITERATE_RDEV(mddev,rdev,tmp2) + rdev_for_each(rdev, tmp2, mddev) print_rdev(rdev); } printk("md: **********************************\n"); @@ -1610,7 +1623,7 @@ static void sync_sbs(mddev_t * mddev, int nospares) mdk_rdev_t *rdev; struct list_head *tmp; - ITERATE_RDEV(mddev,rdev,tmp) { + rdev_for_each(rdev, tmp, mddev) { if (rdev->sb_events == mddev->events || (nospares && rdev->raid_disk < 0 && @@ -1696,18 +1709,20 @@ repeat: MD_BUG(); mddev->events --; } - sync_sbs(mddev, nospares); /* * do not write anything to disk if using * nonpersistent superblocks */ if (!mddev->persistent) { - clear_bit(MD_CHANGE_PENDING, &mddev->flags); + if (!mddev->external) + clear_bit(MD_CHANGE_PENDING, &mddev->flags); + spin_unlock_irq(&mddev->write_lock); wake_up(&mddev->sb_wait); return; } + sync_sbs(mddev, nospares); spin_unlock_irq(&mddev->write_lock); dprintk(KERN_INFO @@ -1715,7 +1730,7 @@ repeat: mdname(mddev),mddev->in_sync); bitmap_update_sb(mddev->bitmap); - ITERATE_RDEV(mddev,rdev,tmp) { + rdev_for_each(rdev, tmp, mddev) { char b[BDEVNAME_SIZE]; dprintk(KERN_INFO "md: "); if (rdev->sb_loaded != 1) @@ -1785,7 +1800,7 @@ static ssize_t state_show(mdk_rdev_t *rdev, char *page) { char *sep = ""; - int len=0; + size_t len = 0; if (test_bit(Faulty, &rdev->flags)) { len+= sprintf(page+len, "%sfaulty",sep); @@ -1887,20 +1902,45 @@ static ssize_t slot_store(mdk_rdev_t *rdev, const char *buf, size_t len) { char *e; + int err; + char nm[20]; int slot = simple_strtoul(buf, &e, 10); if (strncmp(buf, "none", 4)==0) slot = -1; else if (e==buf || (*e && *e!= '\n')) return -EINVAL; - if (rdev->mddev->pers) - /* Cannot set slot in active array (yet) */ - return -EBUSY; - if (slot >= rdev->mddev->raid_disks) - return -ENOSPC; - rdev->raid_disk = slot; - /* assume it is working */ - rdev->flags = 0; - set_bit(In_sync, &rdev->flags); + if (rdev->mddev->pers) { + /* Setting 'slot' on an active array requires also + * updating the 'rd%d' link, and communicating + * with the personality with ->hot_*_disk. + * For now we only support removing + * failed/spare devices. This normally happens automatically, + * but not when the metadata is externally managed. + */ + if (slot != -1) + return -EBUSY; + if (rdev->raid_disk == -1) + return -EEXIST; + /* personality does all needed checks */ + if (rdev->mddev->pers->hot_add_disk == NULL) + return -EINVAL; + err = rdev->mddev->pers-> + hot_remove_disk(rdev->mddev, rdev->raid_disk); + if (err) + return err; + sprintf(nm, "rd%d", rdev->raid_disk); + sysfs_remove_link(&rdev->mddev->kobj, nm); + set_bit(MD_RECOVERY_NEEDED, &rdev->mddev->recovery); + md_wakeup_thread(rdev->mddev->thread); + } else { + if (slot >= rdev->mddev->raid_disks) + return -ENOSPC; + rdev->raid_disk = slot; + /* assume it is working */ + clear_bit(Faulty, &rdev->flags); + clear_bit(WriteMostly, &rdev->flags); + set_bit(In_sync, &rdev->flags); + } return len; } @@ -1923,6 +1963,10 @@ offset_store(mdk_rdev_t *rdev, const char *buf, size_t len) return -EINVAL; if (rdev->mddev->pers) return -EBUSY; + if (rdev->size && rdev->mddev->external) + /* Must set offset before size, so overlap checks + * can be sane */ + return -EBUSY; rdev->data_offset = offset; return len; } @@ -1936,16 +1980,69 @@ rdev_size_show(mdk_rdev_t *rdev, char *page) return sprintf(page, "%llu\n", (unsigned long long)rdev->size); } +static int overlaps(sector_t s1, sector_t l1, sector_t s2, sector_t l2) +{ + /* check if two start/length pairs overlap */ + if (s1+l1 <= s2) + return 0; + if (s2+l2 <= s1) + return 0; + return 1; +} + static ssize_t rdev_size_store(mdk_rdev_t *rdev, const char *buf, size_t len) { char *e; unsigned long long size = simple_strtoull(buf, &e, 10); + unsigned long long oldsize = rdev->size; if (e==buf || (*e && *e != '\n')) return -EINVAL; if (rdev->mddev->pers) return -EBUSY; rdev->size = size; + if (size > oldsize && rdev->mddev->external) { + /* need to check that all other rdevs with the same ->bdev + * do not overlap. We need to unlock the mddev to avoid + * a deadlock. We have already changed rdev->size, and if + * we have to change it back, we will have the lock again. + */ + mddev_t *mddev; + int overlap = 0; + struct list_head *tmp, *tmp2; + + mddev_unlock(rdev->mddev); + for_each_mddev(mddev, tmp) { + mdk_rdev_t *rdev2; + + mddev_lock(mddev); + rdev_for_each(rdev2, tmp2, mddev) + if (test_bit(AllReserved, &rdev2->flags) || + (rdev->bdev == rdev2->bdev && + rdev != rdev2 && + overlaps(rdev->data_offset, rdev->size, + rdev2->data_offset, rdev2->size))) { + overlap = 1; + break; + } + mddev_unlock(mddev); + if (overlap) { + mddev_put(mddev); + break; + } + } + mddev_lock(rdev->mddev); + if (overlap) { + /* Someone else could have slipped in a size + * change here, but doing so is just silly. + * We put oldsize back because we *know* it is + * safe, and trust userspace not to race with + * itself + */ + rdev->size = oldsize; + return -EBUSY; + } + } if (size < rdev->mddev->size || rdev->mddev->size == 0) rdev->mddev->size = size; return len; @@ -1980,12 +2077,18 @@ rdev_attr_store(struct kobject *kobj, struct attribute *attr, { struct rdev_sysfs_entry *entry = container_of(attr, struct rdev_sysfs_entry, attr); mdk_rdev_t *rdev = container_of(kobj, mdk_rdev_t, kobj); + int rv; if (!entry->store) return -EIO; if (!capable(CAP_SYS_ADMIN)) return -EACCES; - return entry->store(rdev, page, length); + rv = mddev_lock(rdev->mddev); + if (!rv) { + rv = entry->store(rdev, page, length); + mddev_unlock(rdev->mddev); + } + return rv; } static void rdev_free(struct kobject *ko) @@ -2029,7 +2132,7 @@ static mdk_rdev_t *md_import_device(dev_t newdev, int super_format, int super_mi if ((err = alloc_disk_sb(rdev))) goto abort_free; - err = lock_rdev(rdev, newdev); + err = lock_rdev(rdev, newdev, super_format == -2); if (err) goto abort_free; @@ -2099,7 +2202,7 @@ static void analyze_sbs(mddev_t * mddev) char b[BDEVNAME_SIZE]; freshest = NULL; - ITERATE_RDEV(mddev,rdev,tmp) + rdev_for_each(rdev, tmp, mddev) switch (super_types[mddev->major_version]. load_super(rdev, freshest, mddev->minor_version)) { case 1: @@ -2120,7 +2223,7 @@ static void analyze_sbs(mddev_t * mddev) validate_super(mddev, freshest); i = 0; - ITERATE_RDEV(mddev,rdev,tmp) { + rdev_for_each(rdev, tmp, mddev) { if (rdev != freshest) if (super_types[mddev->major_version]. validate_super(mddev, rdev)) { @@ -2215,7 +2318,7 @@ level_show(mddev_t *mddev, char *page) static ssize_t level_store(mddev_t *mddev, const char *buf, size_t len) { - int rv = len; + ssize_t rv = len; if (mddev->pers) return -EBUSY; if (len == 0) @@ -2425,6 +2528,8 @@ array_state_show(mddev_t *mddev, char *page) case 0: if (mddev->in_sync) st = clean; + else if (test_bit(MD_CHANGE_CLEAN, &mddev->flags)) + st = write_pending; else if (mddev->safemode) st = active_idle; else @@ -2455,11 +2560,9 @@ array_state_store(mddev_t *mddev, const char *buf, size_t len) break; case clear: /* stopping an active array */ - if (mddev->pers) { - if (atomic_read(&mddev->active) > 1) - return -EBUSY; - err = do_md_stop(mddev, 0); - } + if (atomic_read(&mddev->active) > 1) + return -EBUSY; + err = do_md_stop(mddev, 0); break; case inactive: /* stopping an active array */ @@ -2467,7 +2570,8 @@ array_state_store(mddev_t *mddev, const char *buf, size_t len) if (atomic_read(&mddev->active) > 1) return -EBUSY; err = do_md_stop(mddev, 2); - } + } else + err = 0; /* already inactive */ break; case suspended: break; /* not supported yet */ @@ -2495,9 +2599,15 @@ array_state_store(mddev_t *mddev, const char *buf, size_t len) restart_array(mddev); spin_lock_irq(&mddev->write_lock); if (atomic_read(&mddev->writes_pending) == 0) { - mddev->in_sync = 1; - set_bit(MD_CHANGE_CLEAN, &mddev->flags); - } + if (mddev->in_sync == 0) { + mddev->in_sync = 1; + if (mddev->persistent) + set_bit(MD_CHANGE_CLEAN, + &mddev->flags); + } + err = 0; + } else + err = -EBUSY; spin_unlock_irq(&mddev->write_lock); } else { mddev->ro = 0; @@ -2508,7 +2618,8 @@ array_state_store(mddev_t *mddev, const char *buf, size_t len) case active: if (mddev->pers) { restart_array(mddev); - clear_bit(MD_CHANGE_CLEAN, &mddev->flags); + if (mddev->external) + clear_bit(MD_CHANGE_CLEAN, &mddev->flags); wake_up(&mddev->sb_wait); err = 0; } else { @@ -2574,7 +2685,9 @@ new_dev_store(mddev_t *mddev, const char *buf, size_t len) if (err < 0) goto out; } - } else + } else if (mddev->external) + rdev = md_import_device(dev, -2, -1); + else rdev = md_import_device(dev, -1, -1); if (IS_ERR(rdev)) @@ -2659,7 +2772,9 @@ __ATTR(component_size, S_IRUGO|S_IWUSR, size_show, size_store); /* Metdata version. - * This is either 'none' for arrays with externally managed metadata, + * This is one of + * 'none' for arrays with no metadata (good luck...) + * 'external' for arrays with externally managed metadata, * or N.M for internally known formats */ static ssize_t @@ -2668,6 +2783,8 @@ metadata_show(mddev_t *mddev, char *page) if (mddev->persistent) return sprintf(page, "%d.%d\n", mddev->major_version, mddev->minor_version); + else if (mddev->external) + return sprintf(page, "external:%s\n", mddev->metadata_type); else return sprintf(page, "none\n"); } @@ -2682,6 +2799,21 @@ metadata_store(mddev_t *mddev, const char *buf, size_t len) if (cmd_match(buf, "none")) { mddev->persistent = 0; + mddev->external = 0; + mddev->major_version = 0; + mddev->minor_version = 90; + return len; + } + if (strncmp(buf, "external:", 9) == 0) { + size_t namelen = len-9; + if (namelen >= sizeof(mddev->metadata_type)) + namelen = sizeof(mddev->metadata_type)-1; + strncpy(mddev->metadata_type, buf+9, namelen); + mddev->metadata_type[namelen] = 0; + if (namelen && mddev->metadata_type[namelen-1] == '\n') + mddev->metadata_type[--namelen] = 0; + mddev->persistent = 0; + mddev->external = 1; mddev->major_version = 0; mddev->minor_version = 90; return len; @@ -2698,6 +2830,7 @@ metadata_store(mddev_t *mddev, const char *buf, size_t len) mddev->major_version = major; mddev->minor_version = minor; mddev->persistent = 1; + mddev->external = 0; return len; } @@ -2865,6 +2998,43 @@ sync_completed_show(mddev_t *mddev, char *page) static struct md_sysfs_entry md_sync_completed = __ATTR_RO(sync_completed); static ssize_t +max_sync_show(mddev_t *mddev, char *page) +{ + if (mddev->resync_max == MaxSector) + return sprintf(page, "max\n"); + else + return sprintf(page, "%llu\n", + (unsigned long long)mddev->resync_max); +} +static ssize_t +max_sync_store(mddev_t *mddev, const char *buf, size_t len) +{ + if (strncmp(buf, "max", 3) == 0) + mddev->resync_max = MaxSector; + else { + char *ep; + unsigned long long max = simple_strtoull(buf, &ep, 10); + if (ep == buf || (*ep != 0 && *ep != '\n')) + return -EINVAL; + if (max < mddev->resync_max && + test_bit(MD_RECOVERY_RUNNING, &mddev->recovery)) + return -EBUSY; + + /* Must be a multiple of chunk_size */ + if (mddev->chunk_size) { + if (max & (sector_t)((mddev->chunk_size>>9)-1)) + return -EINVAL; + } + mddev->resync_max = max; + } + wake_up(&mddev->recovery_wait); + return len; +} + +static struct md_sysfs_entry md_max_sync = +__ATTR(sync_max, S_IRUGO|S_IWUSR, max_sync_show, max_sync_store); + +static ssize_t suspend_lo_show(mddev_t *mddev, char *page) { return sprintf(page, "%llu\n", (unsigned long long)mddev->suspend_lo); @@ -2974,6 +3144,7 @@ static struct attribute *md_redundancy_attrs[] = { &md_sync_max.attr, &md_sync_speed.attr, &md_sync_completed.attr, + &md_max_sync.attr, &md_suspend_lo.attr, &md_suspend_hi.attr, &md_bitmap.attr, @@ -3118,8 +3289,11 @@ static int do_md_run(mddev_t * mddev) /* * Analyze all RAID superblock(s) */ - if (!mddev->raid_disks) + if (!mddev->raid_disks) { + if (!mddev->persistent) + return -EINVAL; analyze_sbs(mddev); + } chunk_size = mddev->chunk_size; @@ -3143,7 +3317,7 @@ static int do_md_run(mddev_t * mddev) } /* devices must have minimum size of one chunk */ - ITERATE_RDEV(mddev,rdev,tmp) { + rdev_for_each(rdev, tmp, mddev) { if (test_bit(Faulty, &rdev->flags)) continue; if (rdev->size < chunk_size / 1024) { @@ -3170,7 +3344,7 @@ static int do_md_run(mddev_t * mddev) * the only valid external interface is through the md * device. */ - ITERATE_RDEV(mddev,rdev,tmp) { + rdev_for_each(rdev, tmp, mddev) { if (test_bit(Faulty, &rdev->flags)) continue; sync_blockdev(rdev->bdev); @@ -3236,8 +3410,8 @@ static int do_md_run(mddev_t * mddev) mdk_rdev_t *rdev2; struct list_head *tmp2; int warned = 0; - ITERATE_RDEV(mddev, rdev, tmp) { - ITERATE_RDEV(mddev, rdev2, tmp2) { + rdev_for_each(rdev, tmp, mddev) { + rdev_for_each(rdev2, tmp2, mddev) { if (rdev < rdev2 && rdev->bdev->bd_contains == rdev2->bdev->bd_contains) { @@ -3297,7 +3471,7 @@ static int do_md_run(mddev_t * mddev) mddev->safemode_delay = (200 * HZ)/1000 +1; /* 200 msec delay */ mddev->in_sync = 1; - ITERATE_RDEV(mddev,rdev,tmp) + rdev_for_each(rdev, tmp, mddev) if (rdev->raid_disk >= 0) { char nm[20]; sprintf(nm, "rd%d", rdev->raid_disk); @@ -3330,7 +3504,7 @@ static int do_md_run(mddev_t * mddev) if (mddev->degraded && !mddev->sync_thread) { struct list_head *rtmp; int spares = 0; - ITERATE_RDEV(mddev,rdev,rtmp) + rdev_for_each(rdev, rtmp, mddev) if (rdev->raid_disk >= 0 && !test_bit(In_sync, &rdev->flags) && !test_bit(Faulty, &rdev->flags)) @@ -3507,14 +3681,14 @@ static int do_md_stop(mddev_t * mddev, int mode) } mddev->bitmap_offset = 0; - ITERATE_RDEV(mddev,rdev,tmp) + rdev_for_each(rdev, tmp, mddev) if (rdev->raid_disk >= 0) { char nm[20]; sprintf(nm, "rd%d", rdev->raid_disk); sysfs_remove_link(&mddev->kobj, nm); } - /* make sure all delayed_delete calls have finished */ + /* make sure all md_delayed_delete calls have finished */ flush_scheduled_work(); export_array(mddev); @@ -3523,7 +3697,10 @@ static int do_md_stop(mddev_t * mddev, int mode) mddev->size = 0; mddev->raid_disks = 0; mddev->recovery_cp = 0; + mddev->resync_max = MaxSector; mddev->reshape_position = MaxSector; + mddev->external = 0; + mddev->persistent = 0; } else if (mddev->pers) printk(KERN_INFO "md: %s switched to read-only mode.\n", @@ -3546,7 +3723,7 @@ static void autorun_array(mddev_t *mddev) printk(KERN_INFO "md: running: "); - ITERATE_RDEV(mddev,rdev,tmp) { + rdev_for_each(rdev, tmp, mddev) { char b[BDEVNAME_SIZE]; printk("<%s>", bdevname(rdev->bdev,b)); } @@ -3589,7 +3766,7 @@ static void autorun_devices(int part) printk(KERN_INFO "md: considering %s ...\n", bdevname(rdev0->bdev,b)); INIT_LIST_HEAD(&candidates); - ITERATE_RDEV_PENDING(rdev,tmp) + rdev_for_each_list(rdev, tmp, pending_raid_disks) if (super_90_load(rdev, rdev0, 0) >= 0) { printk(KERN_INFO "md: adding %s ...\n", bdevname(rdev->bdev,b)); @@ -3632,7 +3809,8 @@ static void autorun_devices(int part) mddev_unlock(mddev); } else { printk(KERN_INFO "md: created %s\n", mdname(mddev)); - ITERATE_RDEV_GENERIC(candidates,rdev,tmp) { + mddev->persistent = 1; + rdev_for_each_list(rdev, tmp, candidates) { list_del_init(&rdev->same_set); if (bind_rdev_to_array(rdev, mddev)) export_rdev(rdev); @@ -3643,7 +3821,7 @@ static void autorun_devices(int part) /* on success, candidates will be empty, on error * it won't... */ - ITERATE_RDEV_GENERIC(candidates,rdev,tmp) + rdev_for_each_list(rdev, tmp, candidates) export_rdev(rdev); mddev_put(mddev); } @@ -3673,7 +3851,7 @@ static int get_array_info(mddev_t * mddev, void __user * arg) struct list_head *tmp; nr=working=active=failed=spare=0; - ITERATE_RDEV(mddev,rdev,tmp) { + rdev_for_each(rdev, tmp, mddev) { nr++; if (test_bit(Faulty, &rdev->flags)) failed++; @@ -3919,8 +4097,6 @@ static int add_new_disk(mddev_t * mddev, mdu_disk_info_t *info) else rdev->raid_disk = -1; - rdev->flags = 0; - if (rdev->raid_disk < mddev->raid_disks) if (info->state & (1<<MD_DISK_SYNC)) set_bit(In_sync, &rdev->flags); @@ -4165,13 +4341,15 @@ static int set_array_info(mddev_t * mddev, mdu_array_info_t *info) else mddev->recovery_cp = 0; mddev->persistent = ! info->not_persistent; + mddev->external = 0; mddev->layout = info->layout; mddev->chunk_size = info->chunk_size; mddev->max_disks = MD_SB_DISKS; - mddev->flags = 0; + if (mddev->persistent) + mddev->flags = 0; set_bit(MD_CHANGE_DEVS, &mddev->flags); mddev->default_bitmap_offset = MD_SB_BYTES >> 9; @@ -4213,7 +4391,7 @@ static int update_size(mddev_t *mddev, unsigned long size) */ if (mddev->sync_thread) return -EBUSY; - ITERATE_RDEV(mddev,rdev,tmp) { + rdev_for_each(rdev, tmp, mddev) { sector_t avail; avail = rdev->size * 2; @@ -4471,9 +4649,10 @@ static int md_ioctl(struct inode *inode, struct file *file, */ /* if we are not initialised yet, only ADD_NEW_DISK, STOP_ARRAY, * RUN_ARRAY, and GET_ and SET_BITMAP_FILE are allowed */ - if (!mddev->raid_disks && cmd != ADD_NEW_DISK && cmd != STOP_ARRAY - && cmd != RUN_ARRAY && cmd != SET_BITMAP_FILE - && cmd != GET_BITMAP_FILE) { + if ((!mddev->raid_disks && !mddev->external) + && cmd != ADD_NEW_DISK && cmd != STOP_ARRAY + && cmd != RUN_ARRAY && cmd != SET_BITMAP_FILE + && cmd != GET_BITMAP_FILE) { err = -ENODEV; goto abort_unlock; } @@ -4757,7 +4936,7 @@ static void status_unused(struct seq_file *seq) seq_printf(seq, "unused devices: "); - ITERATE_RDEV_PENDING(rdev,tmp) { + rdev_for_each_list(rdev, tmp, pending_raid_disks) { char b[BDEVNAME_SIZE]; i++; seq_printf(seq, "%s ", @@ -4953,7 +5132,7 @@ static int md_seq_show(struct seq_file *seq, void *v) } size = 0; - ITERATE_RDEV(mddev,rdev,tmp2) { + rdev_for_each(rdev, tmp2, mddev) { char b[BDEVNAME_SIZE]; seq_printf(seq, " %s[%d]", bdevname(rdev->bdev,b), rdev->desc_nr); @@ -4982,7 +5161,10 @@ static int md_seq_show(struct seq_file *seq, void *v) mddev->major_version, mddev->minor_version); } - } else + } else if (mddev->external) + seq_printf(seq, " super external:%s", + mddev->metadata_type); + else seq_printf(seq, " super non-persistent"); if (mddev->pers) { @@ -5106,7 +5288,7 @@ static int is_mddev_idle(mddev_t *mddev) long curr_events; idle = 1; - ITERATE_RDEV(mddev,rdev,tmp) { + rdev_for_each(rdev, tmp, mddev) { struct gendisk *disk = rdev->bdev->bd_contains->bd_disk; curr_events = disk_stat_read(disk, sectors[0]) + disk_stat_read(disk, sectors[1]) - @@ -5283,7 +5465,7 @@ void md_do_sync(mddev_t *mddev) set_bit(MD_RECOVERY_INTR, &mddev->recovery); goto skip; } - ITERATE_MDDEV(mddev2,tmp) { + for_each_mddev(mddev2, tmp) { if (mddev2 == mddev) continue; if (mddev2->curr_resync && @@ -5333,7 +5515,7 @@ void md_do_sync(mddev_t *mddev) /* recovery follows the physical size of devices */ max_sectors = mddev->size << 1; j = MaxSector; - ITERATE_RDEV(mddev,rdev,rtmp) + rdev_for_each(rdev, rtmp, mddev) if (rdev->raid_disk >= 0 && !test_bit(Faulty, &rdev->flags) && !test_bit(In_sync, &rdev->flags) && @@ -5381,8 +5563,16 @@ void md_do_sync(mddev_t *mddev) sector_t sectors; skipped = 0; + if (j >= mddev->resync_max) { + sysfs_notify(&mddev->kobj, NULL, "sync_completed"); + wait_event(mddev->recovery_wait, + mddev->resync_max > j + || kthread_should_stop()); + } + if (kthread_should_stop()) + goto interrupted; sectors = mddev->pers->sync_request(mddev, j, &skipped, - currspeed < speed_min(mddev)); + currspeed < speed_min(mddev)); if (sectors == 0) { set_bit(MD_RECOVERY_ERR, &mddev->recovery); goto out; @@ -5424,15 +5614,9 @@ void md_do_sync(mddev_t *mddev) } - if (kthread_should_stop()) { - /* - * got a signal, exit. - */ - printk(KERN_INFO - "md: md_do_sync() got signal ... exiting\n"); - set_bit(MD_RECOVERY_INTR, &mddev->recovery); - goto out; - } + if (kthread_should_stop()) + goto interrupted; + /* * this loop exits only if either when we are slower than @@ -5484,7 +5668,7 @@ void md_do_sync(mddev_t *mddev) } else { if (!test_bit(MD_RECOVERY_INTR, &mddev->recovery)) mddev->curr_resync = MaxSector; - ITERATE_RDEV(mddev,rdev,rtmp) + rdev_for_each(rdev, rtmp, mddev) if (rdev->raid_disk >= 0 && !test_bit(Faulty, &rdev->flags) && !test_bit(In_sync, &rdev->flags) && @@ -5496,9 +5680,22 @@ void md_do_sync(mddev_t *mddev) skip: mddev->curr_resync = 0; + mddev->resync_max = MaxSector; + sysfs_notify(&mddev->kobj, NULL, "sync_completed"); wake_up(&resync_wait); set_bit(MD_RECOVERY_DONE, &mddev->recovery); md_wakeup_thread(mddev->thread); + return; + + interrupted: + /* + * got a signal, exit. + */ + printk(KERN_INFO + "md: md_do_sync() got signal ... exiting\n"); + set_bit(MD_RECOVERY_INTR, &mddev->recovery); + goto out; + } EXPORT_SYMBOL_GPL(md_do_sync); @@ -5509,8 +5706,9 @@ static int remove_and_add_spares(mddev_t *mddev) struct list_head *rtmp; int spares = 0; - ITERATE_RDEV(mddev,rdev,rtmp) + rdev_for_each(rdev, rtmp, mddev) if (rdev->raid_disk >= 0 && + !mddev->external && (test_bit(Faulty, &rdev->flags) || ! test_bit(In_sync, &rdev->flags)) && atomic_read(&rdev->nr_pending)==0) { @@ -5524,7 +5722,7 @@ static int remove_and_add_spares(mddev_t *mddev) } if (mddev->degraded) { - ITERATE_RDEV(mddev,rdev,rtmp) + rdev_for_each(rdev, rtmp, mddev) if (rdev->raid_disk < 0 && !test_bit(Faulty, &rdev->flags)) { rdev->recovery_offset = 0; @@ -5589,7 +5787,7 @@ void md_check_recovery(mddev_t *mddev) } if ( ! ( - mddev->flags || + (mddev->flags && !mddev->external) || test_bit(MD_RECOVERY_NEEDED, &mddev->recovery) || test_bit(MD_RECOVERY_DONE, &mddev->recovery) || (mddev->safemode == 1) || @@ -5605,7 +5803,8 @@ void md_check_recovery(mddev_t *mddev) if (mddev->safemode && !atomic_read(&mddev->writes_pending) && !mddev->in_sync && mddev->recovery_cp == MaxSector) { mddev->in_sync = 1; - set_bit(MD_CHANGE_CLEAN, &mddev->flags); + if (mddev->persistent) + set_bit(MD_CHANGE_CLEAN, &mddev->flags); } if (mddev->safemode == 1) mddev->safemode = 0; @@ -5637,7 +5836,7 @@ void md_check_recovery(mddev_t *mddev) * information must be scrapped */ if (!mddev->degraded) - ITERATE_RDEV(mddev,rdev,rtmp) + rdev_for_each(rdev, rtmp, mddev) rdev->saved_raid_disk = -1; mddev->recovery = 0; @@ -5714,7 +5913,7 @@ static int md_notify_reboot(struct notifier_block *this, printk(KERN_INFO "md: stopping all md devices.\n"); - ITERATE_MDDEV(mddev,tmp) + for_each_mddev(mddev, tmp) if (mddev_trylock(mddev)) { do_md_stop (mddev, 1); mddev_unlock(mddev); @@ -5848,7 +6047,7 @@ static __exit void md_exit(void) unregister_reboot_notifier(&md_notifier); unregister_sysctl_table(raid_table_header); remove_proc_entry("mdstat", NULL); - ITERATE_MDDEV(mddev,tmp) { + for_each_mddev(mddev, tmp) { struct gendisk *disk = mddev->gendisk; if (!disk) continue; diff --git a/drivers/md/mktables.c b/drivers/md/mktables.c index adef299908c..b61d5767aae 100644 --- a/drivers/md/mktables.c +++ b/drivers/md/mktables.c @@ -1,13 +1,10 @@ -#ident "$Id: mktables.c,v 1.2 2002/12/12 22:41:27 hpa Exp $" -/* ----------------------------------------------------------------------- * +/* -*- linux-c -*- ------------------------------------------------------- * * - * Copyright 2002 H. Peter Anvin - All Rights Reserved + * Copyright 2002-2007 H. Peter Anvin - All Rights Reserved * - * 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, Inc., 53 Temple Place Ste 330, - * Bostom MA 02111-1307, USA; either version 2 of the License, or - * (at your option) any later version; incorporated herein by reference. + * This file is part of the Linux kernel, and is made available under + * the terms of the GNU General Public License version 2 or (at your + * option) any later version; incorporated herein by reference. * * ----------------------------------------------------------------------- */ @@ -26,100 +23,98 @@ static uint8_t gfmul(uint8_t a, uint8_t b) { - uint8_t v = 0; - - while ( b ) { - if ( b & 1 ) v ^= a; - a = (a << 1) ^ (a & 0x80 ? 0x1d : 0); - b >>= 1; - } - return v; + uint8_t v = 0; + + while (b) { + if (b & 1) + v ^= a; + a = (a << 1) ^ (a & 0x80 ? 0x1d : 0); + b >>= 1; + } + + return v; } static uint8_t gfpow(uint8_t a, int b) { - uint8_t v = 1; - - b %= 255; - if ( b < 0 ) - b += 255; - - while ( b ) { - if ( b & 1 ) v = gfmul(v,a); - a = gfmul(a,a); - b >>= 1; - } - return v; + uint8_t v = 1; + + b %= 255; + if (b < 0) + b += 255; + + while (b) { + if (b & 1) + v = gfmul(v, a); + a = gfmul(a, a); + b >>= 1; + } + + return v; } int main(int argc, char *argv[]) { - int i, j, k; - uint8_t v; - uint8_t exptbl[256], invtbl[256]; - - printf("#include \"raid6.h\"\n"); - - /* Compute multiplication table */ - printf("\nconst u8 __attribute__((aligned(256)))\n" - "raid6_gfmul[256][256] =\n" - "{\n"); - for ( i = 0 ; i < 256 ; i++ ) { - printf("\t{\n"); - for ( j = 0 ; j < 256 ; j += 8 ) { - printf("\t\t"); - for ( k = 0 ; k < 8 ; k++ ) { - printf("0x%02x, ", gfmul(i,j+k)); - } - printf("\n"); - } - printf("\t},\n"); - } - printf("};\n"); - - /* Compute power-of-2 table (exponent) */ - v = 1; - printf("\nconst u8 __attribute__((aligned(256)))\n" - "raid6_gfexp[256] =\n" - "{\n"); - for ( i = 0 ; i < 256 ; i += 8 ) { - printf("\t"); - for ( j = 0 ; j < 8 ; j++ ) { - exptbl[i+j] = v; - printf("0x%02x, ", v); - v = gfmul(v,2); - if ( v == 1 ) v = 0; /* For entry 255, not a real entry */ - } - printf("\n"); - } - printf("};\n"); - - /* Compute inverse table x^-1 == x^254 */ - printf("\nconst u8 __attribute__((aligned(256)))\n" - "raid6_gfinv[256] =\n" - "{\n"); - for ( i = 0 ; i < 256 ; i += 8 ) { - printf("\t"); - for ( j = 0 ; j < 8 ; j++ ) { - invtbl[i+j] = v = gfpow(i+j,254); - printf("0x%02x, ", v); - } - printf("\n"); - } - printf("};\n"); - - /* Compute inv(2^x + 1) (exponent-xor-inverse) table */ - printf("\nconst u8 __attribute__((aligned(256)))\n" - "raid6_gfexi[256] =\n" - "{\n"); - for ( i = 0 ; i < 256 ; i += 8 ) { - printf("\t"); - for ( j = 0 ; j < 8 ; j++ ) { - printf("0x%02x, ", invtbl[exptbl[i+j]^1]); - } - printf("\n"); - } - printf("};\n\n"); - - return 0; + int i, j, k; + uint8_t v; + uint8_t exptbl[256], invtbl[256]; + + printf("#include \"raid6.h\"\n"); + + /* Compute multiplication table */ + printf("\nconst u8 __attribute__((aligned(256)))\n" + "raid6_gfmul[256][256] =\n" + "{\n"); + for (i = 0; i < 256; i++) { + printf("\t{\n"); + for (j = 0; j < 256; j += 8) { + printf("\t\t"); + for (k = 0; k < 8; k++) + printf("0x%02x,%c", gfmul(i, j + k), + (k == 7) ? '\n' : ' '); + } + printf("\t},\n"); + } + printf("};\n"); + + /* Compute power-of-2 table (exponent) */ + v = 1; + printf("\nconst u8 __attribute__((aligned(256)))\n" + "raid6_gfexp[256] =\n" "{\n"); + for (i = 0; i < 256; i += 8) { + printf("\t"); + for (j = 0; j < 8; j++) { + exptbl[i + j] = v; + printf("0x%02x,%c", v, (j == 7) ? '\n' : ' '); + v = gfmul(v, 2); + if (v == 1) + v = 0; /* For entry 255, not a real entry */ + } + } + printf("};\n"); + + /* Compute inverse table x^-1 == x^254 */ + printf("\nconst u8 __attribute__((aligned(256)))\n" + "raid6_gfinv[256] =\n" "{\n"); + for (i = 0; i < 256; i += 8) { + printf("\t"); + for (j = 0; j < 8; j++) { + invtbl[i + j] = v = gfpow(i + j, 254); + printf("0x%02x,%c", v, (j == 7) ? '\n' : ' '); + } + } + printf("};\n"); + + /* Compute inv(2^x + 1) (exponent-xor-inverse) table */ + printf("\nconst u8 __attribute__((aligned(256)))\n" + "raid6_gfexi[256] =\n" "{\n"); + for (i = 0; i < 256; i += 8) { + printf("\t"); + for (j = 0; j < 8; j++) + printf("0x%02x,%c", invtbl[exptbl[i + j] ^ 1], + (j == 7) ? '\n' : ' '); + } + printf("};\n"); + + return 0; } diff --git a/drivers/md/multipath.c b/drivers/md/multipath.c index eb631ebed68..3f299d835a2 100644 --- a/drivers/md/multipath.c +++ b/drivers/md/multipath.c @@ -436,7 +436,7 @@ static int multipath_run (mddev_t *mddev) } conf->working_disks = 0; - ITERATE_RDEV(mddev,rdev,tmp) { + rdev_for_each(rdev, tmp, mddev) { disk_idx = rdev->raid_disk; if (disk_idx < 0 || disk_idx >= mddev->raid_disks) diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c index f8e591708d1..818b4828409 100644 --- a/drivers/md/raid0.c +++ b/drivers/md/raid0.c @@ -72,11 +72,11 @@ static int create_strip_zones (mddev_t *mddev) */ conf->nr_strip_zones = 0; - ITERATE_RDEV(mddev,rdev1,tmp1) { + rdev_for_each(rdev1, tmp1, mddev) { printk("raid0: looking at %s\n", bdevname(rdev1->bdev,b)); c = 0; - ITERATE_RDEV(mddev,rdev2,tmp2) { + rdev_for_each(rdev2, tmp2, mddev) { printk("raid0: comparing %s(%llu)", bdevname(rdev1->bdev,b), (unsigned long long)rdev1->size); @@ -124,7 +124,7 @@ static int create_strip_zones (mddev_t *mddev) cnt = 0; smallest = NULL; zone->dev = conf->devlist; - ITERATE_RDEV(mddev, rdev1, tmp1) { + rdev_for_each(rdev1, tmp1, mddev) { int j = rdev1->raid_disk; if (j < 0 || j >= mddev->raid_disks) { @@ -293,7 +293,7 @@ static int raid0_run (mddev_t *mddev) /* calculate array device size */ mddev->array_size = 0; - ITERATE_RDEV(mddev,rdev,tmp) + rdev_for_each(rdev, tmp, mddev) mddev->array_size += rdev->size; printk("raid0 : md_size is %llu blocks.\n", diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 4a69c416e04..5c7fef091ce 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -1684,6 +1684,7 @@ static sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *skipped, i if (!go_faster && conf->nr_waiting) msleep_interruptible(1000); + bitmap_cond_end_sync(mddev->bitmap, sector_nr); raise_barrier(conf); conf->next_resync = sector_nr; @@ -1766,6 +1767,8 @@ static sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *skipped, i return rv; } + if (max_sector > mddev->resync_max) + max_sector = mddev->resync_max; /* Don't do IO beyond here */ nr_sectors = 0; sync_blocks = 0; do { @@ -1884,7 +1887,7 @@ static int run(mddev_t *mddev) if (!conf->r1bio_pool) goto out_no_mem; - ITERATE_RDEV(mddev, rdev, tmp) { + rdev_for_each(rdev, tmp, mddev) { disk_idx = rdev->raid_disk; if (disk_idx >= mddev->raid_disks || disk_idx < 0) diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 5cdcc938620..017f58113c3 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -1657,6 +1657,9 @@ static sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *skipped, i return (max_sector - sector_nr) + sectors_skipped; } + if (max_sector > mddev->resync_max) + max_sector = mddev->resync_max; /* Don't do IO beyond here */ + /* make sure whole request will fit in a chunk - if chunks * are meaningful */ @@ -1670,6 +1673,8 @@ static sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *skipped, i if (!go_faster && conf->nr_waiting) msleep_interruptible(1000); + bitmap_cond_end_sync(mddev->bitmap, sector_nr); + /* Again, very different code for resync and recovery. * Both must result in an r10bio with a list of bios that * have bi_end_io, bi_sector, bi_bdev set, @@ -2021,7 +2026,7 @@ static int run(mddev_t *mddev) goto out_free_conf; } - ITERATE_RDEV(mddev, rdev, tmp) { + rdev_for_each(rdev, tmp, mddev) { disk_idx = rdev->raid_disk; if (disk_idx >= mddev->raid_disks || disk_idx < 0) diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index e8c8157b02f..2d6f1a51359 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -3159,7 +3159,8 @@ static void raid5_activate_delayed(raid5_conf_t *conf) atomic_inc(&conf->preread_active_stripes); list_add_tail(&sh->lru, &conf->handle_list); } - } + } else + blk_plug_device(conf->mddev->queue); } static void activate_bit_delay(raid5_conf_t *conf) @@ -3549,7 +3550,8 @@ static int make_request(struct request_queue *q, struct bio * bi) goto retry; } finish_wait(&conf->wait_for_overlap, &w); - handle_stripe(sh, NULL); + set_bit(STRIPE_HANDLE, &sh->state); + clear_bit(STRIPE_DELAYED, &sh->state); release_stripe(sh); } else { /* cannot get stripe for read-ahead, just give-up */ @@ -3698,6 +3700,25 @@ static sector_t reshape_request(mddev_t *mddev, sector_t sector_nr, int *skipped release_stripe(sh); first_sector += STRIPE_SECTORS; } + /* If this takes us to the resync_max point where we have to pause, + * then we need to write out the superblock. + */ + sector_nr += conf->chunk_size>>9; + if (sector_nr >= mddev->resync_max) { + /* Cannot proceed until we've updated the superblock... */ + wait_event(conf->wait_for_overlap, + atomic_read(&conf->reshape_stripes) == 0); + mddev->reshape_position = conf->expand_progress; + set_bit(MD_CHANGE_DEVS, &mddev->flags); + md_wakeup_thread(mddev->thread); + wait_event(mddev->sb_wait, + !test_bit(MD_CHANGE_DEVS, &mddev->flags) + || kthread_should_stop()); + spin_lock_irq(&conf->device_lock); + conf->expand_lo = mddev->reshape_position; + spin_unlock_irq(&conf->device_lock); + wake_up(&conf->wait_for_overlap); + } return conf->chunk_size>>9; } @@ -3734,6 +3755,12 @@ static inline sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *ski if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery)) return reshape_request(mddev, sector_nr, skipped); + /* No need to check resync_max as we never do more than one + * stripe, and as resync_max will always be on a chunk boundary, + * if the check in md_do_sync didn't fire, there is no chance + * of overstepping resync_max here + */ + /* if there is too many failed drives and we are trying * to resync, then assert that we are finished, because there is * nothing we can do. @@ -3753,6 +3780,9 @@ static inline sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *ski return sync_blocks * STRIPE_SECTORS; /* keep things rounded to whole stripes */ } + + bitmap_cond_end_sync(mddev->bitmap, sector_nr); + pd_idx = stripe_to_pdidx(sector_nr, conf, raid_disks); sh = get_active_stripe(conf, sector_nr, raid_disks, pd_idx, 1); if (sh == NULL) { @@ -3864,7 +3894,7 @@ static int retry_aligned_read(raid5_conf_t *conf, struct bio *raid_bio) * During the scan, completed stripes are saved for us by the interrupt * handler, so that they will not have to wait for our next wakeup. */ -static void raid5d (mddev_t *mddev) +static void raid5d(mddev_t *mddev) { struct stripe_head *sh; raid5_conf_t *conf = mddev_to_conf(mddev); @@ -3889,12 +3919,6 @@ static void raid5d (mddev_t *mddev) activate_bit_delay(conf); } - if (list_empty(&conf->handle_list) && - atomic_read(&conf->preread_active_stripes) < IO_THRESHOLD && - !blk_queue_plugged(mddev->queue) && - !list_empty(&conf->delayed_list)) - raid5_activate_delayed(conf); - while ((bio = remove_bio_from_retry(conf))) { int ok; spin_unlock_irq(&conf->device_lock); @@ -4108,7 +4132,7 @@ static int run(mddev_t *mddev) pr_debug("raid5: run(%s) called.\n", mdname(mddev)); - ITERATE_RDEV(mddev,rdev,tmp) { + rdev_for_each(rdev, tmp, mddev) { raid_disk = rdev->raid_disk; if (raid_disk >= conf->raid_disks || raid_disk < 0) @@ -4521,7 +4545,7 @@ static int raid5_start_reshape(mddev_t *mddev) if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery)) return -EBUSY; - ITERATE_RDEV(mddev, rdev, rtmp) + rdev_for_each(rdev, rtmp, mddev) if (rdev->raid_disk < 0 && !test_bit(Faulty, &rdev->flags)) spares++; @@ -4543,7 +4567,7 @@ static int raid5_start_reshape(mddev_t *mddev) /* Add some new drives, as many as will fit. * We know there are enough to make the newly sized array work. */ - ITERATE_RDEV(mddev, rdev, rtmp) + rdev_for_each(rdev, rtmp, mddev) if (rdev->raid_disk < 0 && !test_bit(Faulty, &rdev->flags)) { if (raid5_add_disk(mddev, rdev)) { diff --git a/drivers/md/raid6test/test.c b/drivers/md/raid6test/test.c index 0d5cd57accd..559cc41b258 100644 --- a/drivers/md/raid6test/test.c +++ b/drivers/md/raid6test/test.c @@ -1,12 +1,10 @@ /* -*- linux-c -*- ------------------------------------------------------- * * - * Copyright 2002 H. Peter Anvin - All Rights Reserved + * Copyright 2002-2007 H. Peter Anvin - All Rights Reserved * - * 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, Inc., 53 Temple Place Ste 330, - * Bostom MA 02111-1307, USA; either version 2 of the License, or - * (at your option) any later version; incorporated herein by reference. + * This file is part of the Linux kernel, and is made available under + * the terms of the GNU General Public License version 2 or (at your + * option) any later version; incorporated herein by reference. * * ----------------------------------------------------------------------- */ @@ -30,67 +28,87 @@ char *dataptrs[NDISKS]; char data[NDISKS][PAGE_SIZE]; char recovi[PAGE_SIZE], recovj[PAGE_SIZE]; -void makedata(void) +static void makedata(void) { int i, j; - for ( i = 0 ; i < NDISKS ; i++ ) { - for ( j = 0 ; j < PAGE_SIZE ; j++ ) { + for (i = 0; i < NDISKS; i++) { + for (j = 0; j < PAGE_SIZE; j++) data[i][j] = rand(); - } + dataptrs[i] = data[i]; } } +static char disk_type(int d) +{ + switch (d) { + case NDISKS-2: + return 'P'; + case NDISKS-1: + return 'Q'; + default: + return 'D'; + } +} + +static int test_disks(int i, int j) +{ + int erra, errb; + + memset(recovi, 0xf0, PAGE_SIZE); + memset(recovj, 0xba, PAGE_SIZE); + + dataptrs[i] = recovi; + dataptrs[j] = recovj; + + raid6_dual_recov(NDISKS, PAGE_SIZE, i, j, (void **)&dataptrs); + + erra = memcmp(data[i], recovi, PAGE_SIZE); + errb = memcmp(data[j], recovj, PAGE_SIZE); + + if (i < NDISKS-2 && j == NDISKS-1) { + /* We don't implement the DQ failure scenario, since it's + equivalent to a RAID-5 failure (XOR, then recompute Q) */ + erra = errb = 0; + } else { + printf("algo=%-8s faila=%3d(%c) failb=%3d(%c) %s\n", + raid6_call.name, + i, disk_type(i), + j, disk_type(j), + (!erra && !errb) ? "OK" : + !erra ? "ERRB" : + !errb ? "ERRA" : "ERRAB"); + } + + dataptrs[i] = data[i]; + dataptrs[j] = data[j]; + + return erra || errb; +} + int main(int argc, char *argv[]) { - const struct raid6_calls * const * algo; + const struct raid6_calls *const *algo; int i, j; - int erra, errb; + int err = 0; makedata(); - for ( algo = raid6_algos ; *algo ; algo++ ) { - if ( !(*algo)->valid || (*algo)->valid() ) { + for (algo = raid6_algos; *algo; algo++) { + if (!(*algo)->valid || (*algo)->valid()) { raid6_call = **algo; /* Nuke syndromes */ memset(data[NDISKS-2], 0xee, 2*PAGE_SIZE); /* Generate assumed good syndrome */ - raid6_call.gen_syndrome(NDISKS, PAGE_SIZE, (void **)&dataptrs); - - for ( i = 0 ; i < NDISKS-1 ; i++ ) { - for ( j = i+1 ; j < NDISKS ; j++ ) { - memset(recovi, 0xf0, PAGE_SIZE); - memset(recovj, 0xba, PAGE_SIZE); - - dataptrs[i] = recovi; - dataptrs[j] = recovj; - - raid6_dual_recov(NDISKS, PAGE_SIZE, i, j, (void **)&dataptrs); - - erra = memcmp(data[i], recovi, PAGE_SIZE); - errb = memcmp(data[j], recovj, PAGE_SIZE); - - if ( i < NDISKS-2 && j == NDISKS-1 ) { - /* We don't implement the DQ failure scenario, since it's - equivalent to a RAID-5 failure (XOR, then recompute Q) */ - } else { - printf("algo=%-8s faila=%3d(%c) failb=%3d(%c) %s\n", - raid6_call.name, - i, (i==NDISKS-2)?'P':'D', - j, (j==NDISKS-1)?'Q':(j==NDISKS-2)?'P':'D', - (!erra && !errb) ? "OK" : - !erra ? "ERRB" : - !errb ? "ERRA" : - "ERRAB"); - } - - dataptrs[i] = data[i]; - dataptrs[j] = data[j]; - } - } + raid6_call.gen_syndrome(NDISKS, PAGE_SIZE, + (void **)&dataptrs); + + for (i = 0; i < NDISKS-1; i++) + for (j = i+1; j < NDISKS; j++) + err += test_disks(i, j); } printf("\n"); } @@ -99,5 +117,8 @@ int main(int argc, char *argv[]) /* Pick the best algorithm test */ raid6_select_algo(); - return 0; + if (err) + printf("\n*** ERRORS FOUND ***\n"); + + return err; } diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 28ddd146c1c..850b8c6f457 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -22,7 +22,6 @@ obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o obj-$(CONFIG_VIDEO_TDA7432) += tda7432.o obj-$(CONFIG_VIDEO_TDA9875) += tda9875.o -obj-$(CONFIG_SOUND_TVMIXER) += tvmixer.o obj-$(CONFIG_VIDEO_SAA6588) += saa6588.o obj-$(CONFIG_VIDEO_SAA5246A) += saa5246a.o diff --git a/drivers/media/video/tvmixer.c b/drivers/media/video/tvmixer.c deleted file mode 100644 index 9fa5b702e07..00000000000 --- a/drivers/media/video/tvmixer.c +++ /dev/null @@ -1,336 +0,0 @@ -/* - */ - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/string.h> -#include <linux/timer.h> -#include <linux/delay.h> -#include <linux/errno.h> -#include <linux/slab.h> -#include <linux/i2c.h> -#include <linux/videodev.h> -#include <linux/init.h> -#include <linux/kdev_t.h> -#include <linux/sound.h> -#include <linux/soundcard.h> - -#include <asm/semaphore.h> -#include <asm/uaccess.h> - - -#define DEV_MAX 4 - -static int devnr = -1; -module_param(devnr, int, 0644); - -MODULE_AUTHOR("Gerd Knorr"); -MODULE_LICENSE("GPL"); - -/* ----------------------------------------------------------------------- */ - -struct TVMIXER { - struct i2c_client *dev; - int minor; - int count; -}; - -static struct TVMIXER devices[DEV_MAX]; - -static int tvmixer_adapters(struct i2c_adapter *adap); -static int tvmixer_clients(struct i2c_client *client); - -/* ----------------------------------------------------------------------- */ - -static int mix_to_v4l(int i) -{ - int r; - - r = ((i & 0xff) * 65536 + 50) / 100; - if (r > 65535) r = 65535; - if (r < 0) r = 0; - return r; -} - -static int v4l_to_mix(int i) -{ - int r; - - r = (i * 100 + 32768) / 65536; - if (r > 100) r = 100; - if (r < 0) r = 0; - return r | (r << 8); -} - -static int v4l_to_mix2(int l, int r) -{ - r = (r * 100 + 32768) / 65536; - if (r > 100) r = 100; - if (r < 0) r = 0; - l = (l * 100 + 32768) / 65536; - if (l > 100) l = 100; - if (l < 0) l = 0; - return (r << 8) | l; -} - -static int tvmixer_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - struct video_audio va; - int left,right,ret,val = 0; - struct TVMIXER *mix = file->private_data; - struct i2c_client *client = mix->dev; - void __user *argp = (void __user *)arg; - int __user *p = argp; - - if (NULL == client) - return -ENODEV; - - if (cmd == SOUND_MIXER_INFO) { - mixer_info info; - strlcpy(info.id, "tv card", sizeof(info.id)); - strlcpy(info.name, client->name, sizeof(info.name)); - info.modify_counter = 42 /* FIXME */; - if (copy_to_user(argp, &info, sizeof(info))) - return -EFAULT; - return 0; - } - if (cmd == SOUND_OLD_MIXER_INFO) { - _old_mixer_info info; - strlcpy(info.id, "tv card", sizeof(info.id)); - strlcpy(info.name, client->name, sizeof(info.name)); - if (copy_to_user(argp, &info, sizeof(info))) - return -EFAULT; - return 0; - } - if (cmd == OSS_GETVERSION) - return put_user(SOUND_VERSION, p); - - if (_SIOC_DIR(cmd) & _SIOC_WRITE) - if (get_user(val, p)) - return -EFAULT; - - /* read state */ - memset(&va,0,sizeof(va)); - client->driver->command(client,VIDIOCGAUDIO,&va); - - switch (cmd) { - case MIXER_READ(SOUND_MIXER_RECMASK): - case MIXER_READ(SOUND_MIXER_CAPS): - case MIXER_READ(SOUND_MIXER_RECSRC): - case MIXER_WRITE(SOUND_MIXER_RECSRC): - ret = 0; - break; - - case MIXER_READ(SOUND_MIXER_STEREODEVS): - ret = SOUND_MASK_VOLUME; - break; - case MIXER_READ(SOUND_MIXER_DEVMASK): - ret = SOUND_MASK_VOLUME; - if (va.flags & VIDEO_AUDIO_BASS) - ret |= SOUND_MASK_BASS; - if (va.flags & VIDEO_AUDIO_TREBLE) - ret |= SOUND_MASK_TREBLE; - break; - - case MIXER_WRITE(SOUND_MIXER_VOLUME): - left = mix_to_v4l(val); - right = mix_to_v4l(val >> 8); - va.volume = max(left,right); - va.balance = (32768*min(left,right)) / (va.volume ? va.volume : 1); - va.balance = (left<right) ? (65535-va.balance) : va.balance; - if (va.volume) - va.flags &= ~VIDEO_AUDIO_MUTE; - client->driver->command(client,VIDIOCSAUDIO,&va); - client->driver->command(client,VIDIOCGAUDIO,&va); - /* fall throuth */ - case MIXER_READ(SOUND_MIXER_VOLUME): - left = (min(65536 - va.balance,32768) * - va.volume) / 32768; - right = (min(va.balance,(u16)32768) * - va.volume) / 32768; - ret = v4l_to_mix2(left,right); - break; - - case MIXER_WRITE(SOUND_MIXER_BASS): - va.bass = mix_to_v4l(val); - client->driver->command(client,VIDIOCSAUDIO,&va); - client->driver->command(client,VIDIOCGAUDIO,&va); - /* fall throuth */ - case MIXER_READ(SOUND_MIXER_BASS): - ret = v4l_to_mix(va.bass); - break; - - case MIXER_WRITE(SOUND_MIXER_TREBLE): - va.treble = mix_to_v4l(val); - client->driver->command(client,VIDIOCSAUDIO,&va); - client->driver->command(client,VIDIOCGAUDIO,&va); - /* fall throuth */ - case MIXER_READ(SOUND_MIXER_TREBLE): - ret = v4l_to_mix(va.treble); - break; - - default: - return -EINVAL; - } - if (put_user(ret, p)) - return -EFAULT; - return 0; -} - -static int tvmixer_open(struct inode *inode, struct file *file) -{ - int i, minor = iminor(inode); - struct TVMIXER *mix = NULL; - struct i2c_client *client = NULL; - - for (i = 0; i < DEV_MAX; i++) { - if (devices[i].minor == minor) { - mix = devices+i; - client = mix->dev; - break; - } - } - - if (NULL == client) - return -ENODEV; - - /* lock bttv in memory while the mixer is in use */ - file->private_data = mix; - if (client->adapter->owner) - try_module_get(client->adapter->owner); - return 0; -} - -static int tvmixer_release(struct inode *inode, struct file *file) -{ - struct TVMIXER *mix = file->private_data; - struct i2c_client *client; - - client = mix->dev; - if (NULL == client) { - return -ENODEV; - } - - module_put(client->adapter->owner); - return 0; -} - -static struct i2c_driver driver = { - .driver = { - .name = "tvmixer", - }, - .id = I2C_DRIVERID_TVMIXER, - .detach_adapter = tvmixer_adapters, - .attach_adapter = tvmixer_adapters, - .detach_client = tvmixer_clients, -}; - -static const struct file_operations tvmixer_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .ioctl = tvmixer_ioctl, - .open = tvmixer_open, - .release = tvmixer_release, -}; - -/* ----------------------------------------------------------------------- */ - -static int tvmixer_adapters(struct i2c_adapter *adap) -{ - struct i2c_client *client; - - list_for_each_entry(client, &adap->clients, list) - tvmixer_clients(client); - return 0; -} - -static int tvmixer_clients(struct i2c_client *client) -{ - struct video_audio va; - int i,minor; - - if (!(client->adapter->class & I2C_CLASS_TV_ANALOG)) - return -1; - - /* unregister ?? */ - for (i = 0; i < DEV_MAX; i++) { - if (devices[i].dev == client) { - /* unregister */ - unregister_sound_mixer(devices[i].minor); - devices[i].dev = NULL; - devices[i].minor = -1; - printk("tvmixer: %s unregistered (#1)\n", - client->name); - return 0; - } - } - - /* look for a free slot */ - for (i = 0; i < DEV_MAX; i++) - if (NULL == devices[i].dev) - break; - if (i == DEV_MAX) { - printk(KERN_WARNING "tvmixer: DEV_MAX too small\n"); - return -1; - } - - /* audio chip with mixer ??? */ - if (NULL == client->driver->command) - return -1; - memset(&va,0,sizeof(va)); - if (0 != client->driver->command(client,VIDIOCGAUDIO,&va)) - return -1; - if (0 == (va.flags & VIDEO_AUDIO_VOLUME)) - return -1; - - /* everything is fine, register */ - if ((minor = register_sound_mixer(&tvmixer_fops,devnr)) < 0) { - printk(KERN_ERR "tvmixer: cannot allocate mixer device\n"); - return -1; - } - - devices[i].minor = minor; - devices[i].count = 0; - devices[i].dev = client; - printk("tvmixer: %s (%s) registered with minor %d\n", - client->name,client->adapter->name,minor); - - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static int __init tvmixer_init_module(void) -{ - int i; - - for (i = 0; i < DEV_MAX; i++) - devices[i].minor = -1; - - return i2c_add_driver(&driver); -} - -static void __exit tvmixer_cleanup_module(void) -{ - int i; - - i2c_del_driver(&driver); - for (i = 0; i < DEV_MAX; i++) { - if (devices[i].minor != -1) { - unregister_sound_mixer(devices[i].minor); - printk("tvmixer: %s unregistered (#2)\n", - devices[i].dev->name); - } - } -} - -module_init(tvmixer_init_module); -module_exit(tvmixer_cleanup_module); - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/misc/asus-laptop.c b/drivers/misc/asus-laptop.c index 7dce318df1b..0846c33296b 100644 --- a/drivers/misc/asus-laptop.c +++ b/drivers/misc/asus-laptop.c @@ -33,7 +33,6 @@ * Sam Lin - GPS support */ -#include <linux/autoconf.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> diff --git a/drivers/misc/fujitsu-laptop.c b/drivers/misc/fujitsu-laptop.c index c8d62c268b1..1cfd7f3f129 100644 --- a/drivers/misc/fujitsu-laptop.c +++ b/drivers/misc/fujitsu-laptop.c @@ -50,7 +50,6 @@ #include <linux/dmi.h> #include <linux/backlight.h> #include <linux/platform_device.h> -#include <linux/autoconf.h> #define FUJITSU_DRIVER_VERSION "0.3" diff --git a/drivers/misc/lkdtm.c b/drivers/misc/lkdtm.c index 552b7957a92..c884730c5ea 100644 --- a/drivers/misc/lkdtm.c +++ b/drivers/misc/lkdtm.c @@ -129,27 +129,28 @@ module_param(cpoint_count, int, 0644); MODULE_PARM_DESC(cpoint_count, " Crash Point Count, number of times the "\ "crash point is to be hit to trigger action"); -unsigned int jp_do_irq(unsigned int irq) +static unsigned int jp_do_irq(unsigned int irq) { lkdtm_handler(); jprobe_return(); return 0; } -irqreturn_t jp_handle_irq_event(unsigned int irq, struct irqaction *action) +static irqreturn_t jp_handle_irq_event(unsigned int irq, + struct irqaction *action) { lkdtm_handler(); jprobe_return(); return 0; } -void jp_tasklet_action(struct softirq_action *a) +static void jp_tasklet_action(struct softirq_action *a) { lkdtm_handler(); jprobe_return(); } -void jp_ll_rw_block(int rw, int nr, struct buffer_head *bhs[]) +static void jp_ll_rw_block(int rw, int nr, struct buffer_head *bhs[]) { lkdtm_handler(); jprobe_return(); @@ -157,23 +158,24 @@ void jp_ll_rw_block(int rw, int nr, struct buffer_head *bhs[]) struct scan_control; -unsigned long jp_shrink_inactive_list(unsigned long max_scan, - struct zone *zone, struct scan_control *sc) +static unsigned long jp_shrink_inactive_list(unsigned long max_scan, + struct zone *zone, + struct scan_control *sc) { lkdtm_handler(); jprobe_return(); return 0; } -int jp_hrtimer_start(struct hrtimer *timer, ktime_t tim, - const enum hrtimer_mode mode) +static int jp_hrtimer_start(struct hrtimer *timer, ktime_t tim, + const enum hrtimer_mode mode) { lkdtm_handler(); jprobe_return(); return 0; } -int jp_scsi_dispatch_cmd(struct scsi_cmnd *cmd) +static int jp_scsi_dispatch_cmd(struct scsi_cmnd *cmd) { lkdtm_handler(); jprobe_return(); @@ -270,7 +272,7 @@ void lkdtm_handler(void) } } -int lkdtm_module_init(void) +static int __init lkdtm_module_init(void) { int ret; @@ -331,7 +333,7 @@ int lkdtm_module_init(void) return 0; } -void lkdtm_module_exit(void) +static void __exit lkdtm_module_exit(void) { unregister_jprobe(&lkdtm); printk(KERN_INFO "lkdtm : Crash point unregistered\n"); diff --git a/drivers/misc/msi-laptop.c b/drivers/misc/msi-laptop.c index 83679c76292..de898c6938f 100644 --- a/drivers/misc/msi-laptop.c +++ b/drivers/misc/msi-laptop.c @@ -58,7 +58,6 @@ #include <linux/dmi.h> #include <linux/backlight.h> #include <linux/platform_device.h> -#include <linux/autoconf.h> #define MSI_DRIVER_VERSION "0.5" diff --git a/drivers/misc/phantom.c b/drivers/misc/phantom.c index cd221fd0fb9..7fa61e907e1 100644 --- a/drivers/misc/phantom.c +++ b/drivers/misc/phantom.c @@ -25,7 +25,7 @@ #include <asm/atomic.h> #include <asm/io.h> -#define PHANTOM_VERSION "n0.9.7" +#define PHANTOM_VERSION "n0.9.8" #define PHANTOM_MAX_MINORS 8 @@ -456,8 +456,9 @@ static int phantom_resume(struct pci_dev *pdev) #endif static struct pci_device_id phantom_pci_tbl[] __devinitdata = { - { PCI_DEVICE(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050), - .class = PCI_CLASS_BRIDGE_OTHER << 8, .class_mask = 0xffff00 }, + { .vendor = PCI_VENDOR_ID_PLX, .device = PCI_DEVICE_ID_PLX_9050, + .subvendor = PCI_VENDOR_ID_PLX, .subdevice = PCI_DEVICE_ID_PLX_9050, + .class = PCI_CLASS_BRIDGE_OTHER << 8, .class_mask = 0xffff00 }, { 0, } }; MODULE_DEVICE_TABLE(pci, phantom_pci_tbl); diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index be4b9948c76..eeaaa9dce6e 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -4,7 +4,7 @@ * block2mtd.c - create an mtd from a block device * * Copyright (C) 2001,2002 Simon Evans <spse@secret.org.uk> - * Copyright (C) 2004-2006 Jörn Engel <joern@wh.fh-wedel.de> + * Copyright (C) 2004-2006 Joern Engel <joern@wh.fh-wedel.de> * * Licence: GPL */ @@ -485,5 +485,5 @@ module_init(block2mtd_init); module_exit(block2mtd_exit); MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Simon Evans <spse@secret.org.uk> and others"); +MODULE_AUTHOR("Joern Engel <joern@lazybastard.org>"); MODULE_DESCRIPTION("Emulate an MTD using a block device"); diff --git a/drivers/mtd/devices/phram.c b/drivers/mtd/devices/phram.c index 56cc1ca7ffd..180298b92a7 100644 --- a/drivers/mtd/devices/phram.c +++ b/drivers/mtd/devices/phram.c @@ -2,7 +2,7 @@ * $Id: phram.c,v 1.16 2005/11/07 11:14:25 gleixner Exp $ * * Copyright (c) ???? Jochen Schäuble <psionic@psionic.de> - * Copyright (c) 2003-2004 Jörn Engel <joern@wh.fh-wedel.de> + * Copyright (c) 2003-2004 Joern Engel <joern@wh.fh-wedel.de> * * Usage: * @@ -299,5 +299,5 @@ module_init(init_phram); module_exit(cleanup_phram); MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Jörn Engel <joern@wh.fh-wedel.de>"); +MODULE_AUTHOR("Joern Engel <joern@wh.fh-wedel.de>"); MODULE_DESCRIPTION("MTD driver for physical RAM"); diff --git a/drivers/mtd/maps/mtx-1_flash.c b/drivers/mtd/maps/mtx-1_flash.c index d884f2be28f..2a8fde9b92f 100644 --- a/drivers/mtd/maps/mtx-1_flash.c +++ b/drivers/mtd/maps/mtx-1_flash.c @@ -4,7 +4,7 @@ * $Id: mtx-1_flash.c,v 1.2 2005/11/07 11:14:27 gleixner Exp $ * * (C) 2005 Bruno Randolf <bruno.randolf@4g-systems.biz> - * (C) 2005 Jörn Engel <joern@wohnheim.fh-wedel.de> + * (C) 2005 Joern Engel <joern@wohnheim.fh-wedel.de> * */ diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c index 36342230a6d..d4843d014bc 100644 --- a/drivers/net/forcedeth.c +++ b/drivers/net/forcedeth.c @@ -323,8 +323,8 @@ enum { NvRegMIIStatus = 0x180, #define NVREG_MIISTAT_ERROR 0x0001 #define NVREG_MIISTAT_LINKCHANGE 0x0008 -#define NVREG_MIISTAT_MASK 0x000f -#define NVREG_MIISTAT_MASK2 0x000f +#define NVREG_MIISTAT_MASK_RW 0x0007 +#define NVREG_MIISTAT_MASK_ALL 0x000f NvRegMIIMask = 0x184, #define NVREG_MII_LINKCHANGE 0x0008 @@ -624,6 +624,9 @@ union ring_type { #define NV_MSI_X_VECTOR_TX 0x1 #define NV_MSI_X_VECTOR_OTHER 0x2 +#define NV_RESTART_TX 0x1 +#define NV_RESTART_RX 0x2 + /* statistics */ struct nv_ethtool_str { char name[ETH_GSTRING_LEN]; @@ -1061,7 +1064,7 @@ static int mii_rw(struct net_device *dev, int addr, int miireg, int value) u32 reg; int retval; - writel(NVREG_MIISTAT_MASK, base + NvRegMIIStatus); + writel(NVREG_MIISTAT_MASK_RW, base + NvRegMIIStatus); reg = readl(base + NvRegMIIControl); if (reg & NVREG_MIICTL_INUSE) { @@ -1432,16 +1435,30 @@ static void nv_mac_reset(struct net_device *dev) { struct fe_priv *np = netdev_priv(dev); u8 __iomem *base = get_hwbase(dev); + u32 temp1, temp2, temp3; dprintk(KERN_DEBUG "%s: nv_mac_reset\n", dev->name); + writel(NVREG_TXRXCTL_BIT2 | NVREG_TXRXCTL_RESET | np->txrxctl_bits, base + NvRegTxRxControl); pci_push(base); + + /* save registers since they will be cleared on reset */ + temp1 = readl(base + NvRegMacAddrA); + temp2 = readl(base + NvRegMacAddrB); + temp3 = readl(base + NvRegTransmitPoll); + writel(NVREG_MAC_RESET_ASSERT, base + NvRegMacReset); pci_push(base); udelay(NV_MAC_RESET_DELAY); writel(0, base + NvRegMacReset); pci_push(base); udelay(NV_MAC_RESET_DELAY); + + /* restore saved registers */ + writel(temp1, base + NvRegMacAddrA); + writel(temp2, base + NvRegMacAddrB); + writel(temp3, base + NvRegTransmitPoll); + writel(NVREG_TXRXCTL_BIT2 | np->txrxctl_bits, base + NvRegTxRxControl); pci_push(base); } @@ -2767,6 +2784,7 @@ static int nv_update_linkspeed(struct net_device *dev) int mii_status; int retval = 0; u32 control_1000, status_1000, phyreg, pause_flags, txreg; + u32 txrxFlags = 0; /* BMSR_LSTATUS is latched, read it twice: * we want the current value. @@ -2862,6 +2880,16 @@ set_speed: np->duplex = newdup; np->linkspeed = newls; + /* The transmitter and receiver must be restarted for safe update */ + if (readl(base + NvRegTransmitterControl) & NVREG_XMITCTL_START) { + txrxFlags |= NV_RESTART_TX; + nv_stop_tx(dev); + } + if (readl(base + NvRegReceiverControl) & NVREG_RCVCTL_START) { + txrxFlags |= NV_RESTART_RX; + nv_stop_rx(dev); + } + if (np->gigabit == PHY_GIGABIT) { phyreg = readl(base + NvRegRandomSeed); phyreg &= ~(0x3FF00); @@ -2950,6 +2978,11 @@ set_speed: } nv_update_pause(dev, pause_flags); + if (txrxFlags & NV_RESTART_TX) + nv_start_tx(dev); + if (txrxFlags & NV_RESTART_RX) + nv_start_rx(dev); + return retval; } @@ -2976,7 +3009,7 @@ static void nv_link_irq(struct net_device *dev) u32 miistat; miistat = readl(base + NvRegMIIStatus); - writel(NVREG_MIISTAT_MASK, base + NvRegMIIStatus); + writel(NVREG_MIISTAT_LINKCHANGE, base + NvRegMIIStatus); dprintk(KERN_INFO "%s: link change irq, status 0x%x.\n", dev->name, miistat); if (miistat & (NVREG_MIISTAT_LINKCHANGE)) @@ -4851,7 +4884,7 @@ static int nv_open(struct net_device *dev) writel(0, base + NvRegMIIMask); writel(NVREG_IRQSTAT_MASK, base + NvRegIrqStatus); - writel(NVREG_MIISTAT_MASK2, base + NvRegMIIStatus); + writel(NVREG_MIISTAT_MASK_ALL, base + NvRegMIIStatus); writel(NVREG_MISC1_FORCE | NVREG_MISC1_HD, base + NvRegMisc1); writel(readl(base + NvRegTransmitterStatus), base + NvRegTransmitterStatus); @@ -4889,7 +4922,7 @@ static int nv_open(struct net_device *dev) nv_disable_hw_interrupts(dev, np->irqmask); pci_push(base); - writel(NVREG_MIISTAT_MASK2, base + NvRegMIIStatus); + writel(NVREG_MIISTAT_MASK_ALL, base + NvRegMIIStatus); writel(NVREG_IRQSTAT_MASK, base + NvRegIrqStatus); pci_push(base); @@ -4912,7 +4945,7 @@ static int nv_open(struct net_device *dev) { u32 miistat; miistat = readl(base + NvRegMIIStatus); - writel(NVREG_MIISTAT_MASK, base + NvRegMIIStatus); + writel(NVREG_MIISTAT_MASK_ALL, base + NvRegMIIStatus); dprintk(KERN_INFO "startup: got 0x%08x.\n", miistat); } /* set linkspeed to invalid value, thus force nv_update_linkspeed @@ -5280,7 +5313,7 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i phystate &= ~NVREG_ADAPTCTL_RUNNING; writel(phystate, base + NvRegAdapterControl); } - writel(NVREG_MIISTAT_MASK, base + NvRegMIIStatus); + writel(NVREG_MIISTAT_MASK_ALL, base + NvRegMIIStatus); if (id->driver_data & DEV_HAS_MGMT_UNIT) { /* management unit running on the mac? */ diff --git a/drivers/net/gianfar_mii.c b/drivers/net/gianfar_mii.c index 100bf410bf5..6a647d95e6e 100644 --- a/drivers/net/gianfar_mii.c +++ b/drivers/net/gianfar_mii.c @@ -127,7 +127,7 @@ int gfar_mdio_reset(struct mii_bus *bus) struct gfar_mii __iomem *regs = (void __iomem *)bus->priv; unsigned int timeout = PHY_INIT_TIMEOUT; - spin_lock_bh(&bus->mdio_lock); + mutex_lock(&bus->mdio_lock); /* Reset the management interface */ gfar_write(®s->miimcfg, MIIMCFG_RESET); @@ -140,7 +140,7 @@ int gfar_mdio_reset(struct mii_bus *bus) timeout--) cpu_relax(); - spin_unlock_bh(&bus->mdio_lock); + mutex_unlock(&bus->mdio_lock); if(timeout <= 0) { printk(KERN_ERR "%s: The MII Bus is stuck!\n", diff --git a/drivers/net/iseries_veth.c b/drivers/net/iseries_veth.c index 419861cbc65..58d3bb622da 100644 --- a/drivers/net/iseries_veth.c +++ b/drivers/net/iseries_veth.c @@ -1020,7 +1020,7 @@ static const struct ethtool_ops ops = { .get_link = veth_get_link, }; -static struct net_device * __init veth_probe_one(int vlan, +static struct net_device *veth_probe_one(int vlan, struct vio_dev *vio_dev) { struct net_device *dev; diff --git a/drivers/net/ixgbe/ixgbe.h b/drivers/net/ixgbe/ixgbe.h index a021a6e7264..d0bf206632c 100644 --- a/drivers/net/ixgbe/ixgbe.h +++ b/drivers/net/ixgbe/ixgbe.h @@ -136,8 +136,6 @@ struct ixgbe_ring { u16 head; u16 tail; - /* To protect race between sender and clean_tx_irq */ - spinlock_t tx_lock; struct ixgbe_queue_stats stats; @@ -174,7 +172,6 @@ struct ixgbe_adapter { struct vlan_group *vlgrp; u16 bd_number; u16 rx_buf_len; - atomic_t irq_sem; struct work_struct reset_task; /* TX */ @@ -244,6 +241,7 @@ extern const char ixgbe_driver_version[]; extern int ixgbe_up(struct ixgbe_adapter *adapter); extern void ixgbe_down(struct ixgbe_adapter *adapter); +extern void ixgbe_reinit_locked(struct ixgbe_adapter *adapter); extern void ixgbe_reset(struct ixgbe_adapter *adapter); extern void ixgbe_update_stats(struct ixgbe_adapter *adapter); extern void ixgbe_set_ethtool_ops(struct net_device *netdev); diff --git a/drivers/net/ixgbe/ixgbe_ethtool.c b/drivers/net/ixgbe/ixgbe_ethtool.c index 36353447716..a119cbd8dbb 100644 --- a/drivers/net/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ixgbe/ixgbe_ethtool.c @@ -103,21 +103,41 @@ static int ixgbe_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) { struct ixgbe_adapter *adapter = netdev_priv(netdev); + struct ixgbe_hw *hw = &adapter->hw; + u32 link_speed = 0; + bool link_up; - ecmd->supported = (SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE); - ecmd->advertising = (ADVERTISED_10000baseT_Full | ADVERTISED_FIBRE); - ecmd->port = PORT_FIBRE; + ecmd->supported = SUPPORTED_10000baseT_Full; + ecmd->autoneg = AUTONEG_ENABLE; ecmd->transceiver = XCVR_EXTERNAL; + if (hw->phy.media_type == ixgbe_media_type_copper) { + ecmd->supported |= (SUPPORTED_1000baseT_Full | + SUPPORTED_TP | SUPPORTED_Autoneg); + + ecmd->advertising = (ADVERTISED_TP | ADVERTISED_Autoneg); + if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_10GB_FULL) + ecmd->advertising |= ADVERTISED_10000baseT_Full; + if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_1GB_FULL) + ecmd->advertising |= ADVERTISED_1000baseT_Full; + + ecmd->port = PORT_TP; + } else { + ecmd->supported |= SUPPORTED_FIBRE; + ecmd->advertising = (ADVERTISED_10000baseT_Full | + ADVERTISED_FIBRE); + ecmd->port = PORT_FIBRE; + } - if (netif_carrier_ok(adapter->netdev)) { - ecmd->speed = SPEED_10000; + adapter->hw.mac.ops.check_link(hw, &(link_speed), &link_up); + if (link_up) { + ecmd->speed = (link_speed == IXGBE_LINK_SPEED_10GB_FULL) ? + SPEED_10000 : SPEED_1000; ecmd->duplex = DUPLEX_FULL; } else { ecmd->speed = -1; ecmd->duplex = -1; } - ecmd->autoneg = AUTONEG_DISABLE; return 0; } @@ -125,17 +145,17 @@ static int ixgbe_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) { struct ixgbe_adapter *adapter = netdev_priv(netdev); + struct ixgbe_hw *hw = &adapter->hw; - if (ecmd->autoneg == AUTONEG_ENABLE || - ecmd->speed + ecmd->duplex != SPEED_10000 + DUPLEX_FULL) - return -EINVAL; - - if (netif_running(adapter->netdev)) { - ixgbe_down(adapter); - ixgbe_reset(adapter); - ixgbe_up(adapter); - } else { - ixgbe_reset(adapter); + switch (hw->phy.media_type) { + case ixgbe_media_type_fiber: + if ((ecmd->autoneg == AUTONEG_ENABLE) || + (ecmd->speed + ecmd->duplex != SPEED_10000 + DUPLEX_FULL)) + return -EINVAL; + /* in this case we currently only support 10Gb/FULL */ + break; + default: + break; } return 0; @@ -147,7 +167,7 @@ static void ixgbe_get_pauseparam(struct net_device *netdev, struct ixgbe_adapter *adapter = netdev_priv(netdev); struct ixgbe_hw *hw = &adapter->hw; - pause->autoneg = AUTONEG_DISABLE; + pause->autoneg = (hw->fc.type == ixgbe_fc_full ? 1 : 0); if (hw->fc.type == ixgbe_fc_rx_pause) { pause->rx_pause = 1; @@ -165,10 +185,8 @@ static int ixgbe_set_pauseparam(struct net_device *netdev, struct ixgbe_adapter *adapter = netdev_priv(netdev); struct ixgbe_hw *hw = &adapter->hw; - if (pause->autoneg == AUTONEG_ENABLE) - return -EINVAL; - - if (pause->rx_pause && pause->tx_pause) + if ((pause->autoneg == AUTONEG_ENABLE) || + (pause->rx_pause && pause->tx_pause)) hw->fc.type = ixgbe_fc_full; else if (pause->rx_pause && !pause->tx_pause) hw->fc.type = ixgbe_fc_rx_pause; @@ -176,15 +194,15 @@ static int ixgbe_set_pauseparam(struct net_device *netdev, hw->fc.type = ixgbe_fc_tx_pause; else if (!pause->rx_pause && !pause->tx_pause) hw->fc.type = ixgbe_fc_none; + else + return -EINVAL; hw->fc.original_type = hw->fc.type; - if (netif_running(adapter->netdev)) { - ixgbe_down(adapter); - ixgbe_up(adapter); - } else { + if (netif_running(netdev)) + ixgbe_reinit_locked(adapter); + else ixgbe_reset(adapter); - } return 0; } @@ -203,12 +221,10 @@ static int ixgbe_set_rx_csum(struct net_device *netdev, u32 data) else adapter->flags &= ~IXGBE_FLAG_RX_CSUM_ENABLED; - if (netif_running(netdev)) { - ixgbe_down(adapter); - ixgbe_up(adapter); - } else { + if (netif_running(netdev)) + ixgbe_reinit_locked(adapter); + else ixgbe_reset(adapter); - } return 0; } @@ -662,7 +678,10 @@ static int ixgbe_set_ringparam(struct net_device *netdev, return 0; } - if (netif_running(adapter->netdev)) + while (test_and_set_bit(__IXGBE_RESETTING, &adapter->state)) + msleep(1); + + if (netif_running(netdev)) ixgbe_down(adapter); /* @@ -733,6 +752,7 @@ err_setup: if (netif_running(adapter->netdev)) ixgbe_up(adapter); + clear_bit(__IXGBE_RESETTING, &adapter->state); return err; } @@ -820,11 +840,8 @@ static int ixgbe_nway_reset(struct net_device *netdev) { struct ixgbe_adapter *adapter = netdev_priv(netdev); - if (netif_running(netdev)) { - ixgbe_down(adapter); - ixgbe_reset(adapter); - ixgbe_up(adapter); - } + if (netif_running(netdev)) + ixgbe_reinit_locked(adapter); return 0; } diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c index 3732dd6c4b2..ead49e54f31 100644 --- a/drivers/net/ixgbe/ixgbe_main.c +++ b/drivers/net/ixgbe/ixgbe_main.c @@ -87,6 +87,25 @@ MODULE_VERSION(DRV_VERSION); #define DEFAULT_DEBUG_LEVEL_SHIFT 3 +static void ixgbe_release_hw_control(struct ixgbe_adapter *adapter) +{ + u32 ctrl_ext; + + /* Let firmware take over control of h/w */ + ctrl_ext = IXGBE_READ_REG(&adapter->hw, IXGBE_CTRL_EXT); + IXGBE_WRITE_REG(&adapter->hw, IXGBE_CTRL_EXT, + ctrl_ext & ~IXGBE_CTRL_EXT_DRV_LOAD); +} + +static void ixgbe_get_hw_control(struct ixgbe_adapter *adapter) +{ + u32 ctrl_ext; + + /* Let firmware know the driver has taken over */ + ctrl_ext = IXGBE_READ_REG(&adapter->hw, IXGBE_CTRL_EXT); + IXGBE_WRITE_REG(&adapter->hw, IXGBE_CTRL_EXT, + ctrl_ext | IXGBE_CTRL_EXT_DRV_LOAD); +} #ifdef DEBUG /** @@ -165,6 +184,15 @@ static inline bool ixgbe_check_tx_hang(struct ixgbe_adapter *adapter, return false; } +#define IXGBE_MAX_TXD_PWR 14 +#define IXGBE_MAX_DATA_PER_TXD (1 << IXGBE_MAX_TXD_PWR) + +/* Tx Descriptors needed, worst case */ +#define TXD_USE_COUNT(S) (((S) >> IXGBE_MAX_TXD_PWR) + \ + (((S) & (IXGBE_MAX_DATA_PER_TXD - 1)) ? 1 : 0)) +#define DESC_NEEDED (TXD_USE_COUNT(IXGBE_MAX_DATA_PER_TXD) /* skb->data */ + \ + MAX_SKB_FRAGS * TXD_USE_COUNT(PAGE_SIZE) + 1) /* for context */ + /** * ixgbe_clean_tx_irq - Reclaim resources after transmit completes * @adapter: board private structure @@ -177,18 +205,34 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_adapter *adapter, struct ixgbe_tx_buffer *tx_buffer_info; unsigned int i, eop; bool cleaned = false; - int count = 0; + unsigned int total_tx_bytes = 0, total_tx_packets = 0; i = tx_ring->next_to_clean; eop = tx_ring->tx_buffer_info[i].next_to_watch; eop_desc = IXGBE_TX_DESC_ADV(*tx_ring, eop); while (eop_desc->wb.status & cpu_to_le32(IXGBE_TXD_STAT_DD)) { - for (cleaned = false; !cleaned;) { + cleaned = false; + while (!cleaned) { tx_desc = IXGBE_TX_DESC_ADV(*tx_ring, i); tx_buffer_info = &tx_ring->tx_buffer_info[i]; cleaned = (i == eop); tx_ring->stats.bytes += tx_buffer_info->length; + if (cleaned) { + struct sk_buff *skb = tx_buffer_info->skb; +#ifdef NETIF_F_TSO + unsigned int segs, bytecount; + segs = skb_shinfo(skb)->gso_segs ?: 1; + /* multiply data chunks by size of headers */ + bytecount = ((segs - 1) * skb_headlen(skb)) + + skb->len; + total_tx_packets += segs; + total_tx_bytes += bytecount; +#else + total_tx_packets++; + total_tx_bytes += skb->len; +#endif + } ixgbe_unmap_and_free_tx_resource(adapter, tx_buffer_info); tx_desc->wb.status = 0; @@ -204,29 +248,36 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_adapter *adapter, eop_desc = IXGBE_TX_DESC_ADV(*tx_ring, eop); /* weight of a sort for tx, avoid endless transmit cleanup */ - if (count++ >= tx_ring->work_limit) + if (total_tx_packets >= tx_ring->work_limit) break; } tx_ring->next_to_clean = i; -#define TX_WAKE_THRESHOLD 32 - spin_lock(&tx_ring->tx_lock); - - if (cleaned && netif_carrier_ok(netdev) && - (IXGBE_DESC_UNUSED(tx_ring) >= TX_WAKE_THRESHOLD) && - !test_bit(__IXGBE_DOWN, &adapter->state)) - netif_wake_queue(netdev); - - spin_unlock(&tx_ring->tx_lock); +#define TX_WAKE_THRESHOLD (DESC_NEEDED * 2) + if (total_tx_packets && netif_carrier_ok(netdev) && + (IXGBE_DESC_UNUSED(tx_ring) >= TX_WAKE_THRESHOLD)) { + /* Make sure that anybody stopping the queue after this + * sees the new next_to_clean. + */ + smp_mb(); + if (netif_queue_stopped(netdev) && + !test_bit(__IXGBE_DOWN, &adapter->state)) { + netif_wake_queue(netdev); + adapter->restart_queue++; + } + } if (adapter->detect_tx_hung) if (ixgbe_check_tx_hang(adapter, tx_ring, eop, eop_desc)) netif_stop_queue(netdev); - if (count >= tx_ring->work_limit) + if (total_tx_packets >= tx_ring->work_limit) IXGBE_WRITE_REG(&adapter->hw, IXGBE_EICS, tx_ring->eims_value); + adapter->net_stats.tx_bytes += total_tx_bytes; + adapter->net_stats.tx_packets += total_tx_packets; + cleaned = total_tx_packets ? true : false; return cleaned; } @@ -255,25 +306,40 @@ static void ixgbe_receive_skb(struct ixgbe_adapter *adapter, } } +/** + * ixgbe_rx_checksum - indicate in skb if hw indicated a good cksum + * @adapter: address of board private structure + * @status_err: hardware indication of status of receive + * @skb: skb currently being received and modified + **/ static inline void ixgbe_rx_checksum(struct ixgbe_adapter *adapter, u32 status_err, struct sk_buff *skb) { skb->ip_summed = CHECKSUM_NONE; - /* Ignore Checksum bit is set */ + /* Ignore Checksum bit is set, or rx csum disabled */ if ((status_err & IXGBE_RXD_STAT_IXSM) || - !(adapter->flags & IXGBE_FLAG_RX_CSUM_ENABLED)) + !(adapter->flags & IXGBE_FLAG_RX_CSUM_ENABLED)) return; - /* TCP/UDP checksum error bit is set */ - if (status_err & (IXGBE_RXDADV_ERR_TCPE | IXGBE_RXDADV_ERR_IPE)) { - /* let the stack verify checksum errors */ + + /* if IP and error */ + if ((status_err & IXGBE_RXD_STAT_IPCS) && + (status_err & IXGBE_RXDADV_ERR_IPE)) { adapter->hw_csum_rx_error++; return; } + + if (!(status_err & IXGBE_RXD_STAT_L4CS)) + return; + + if (status_err & IXGBE_RXDADV_ERR_TCPE) { + adapter->hw_csum_rx_error++; + return; + } + /* It must be a TCP or UDP packet with a valid checksum */ - if (status_err & (IXGBE_RXD_STAT_L4CS | IXGBE_RXD_STAT_UDPCS)) - skb->ip_summed = CHECKSUM_UNNECESSARY; + skb->ip_summed = CHECKSUM_UNNECESSARY; adapter->hw_csum_rx_good++; } @@ -379,6 +445,7 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_adapter *adapter, u16 hdr_info, vlan_tag; bool is_vlan, cleaned = false; int cleaned_count = 0; + unsigned int total_rx_bytes = 0, total_rx_packets = 0; i = rx_ring->next_to_clean; upper_len = 0; @@ -458,6 +525,11 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_adapter *adapter, } ixgbe_rx_checksum(adapter, staterr, skb); + + /* probably a little skewed due to removing CRC */ + total_rx_bytes += skb->len; + total_rx_packets++; + skb->protocol = eth_type_trans(skb, netdev); ixgbe_receive_skb(adapter, skb, is_vlan, vlan_tag); netdev->last_rx = jiffies; @@ -486,6 +558,9 @@ next_desc: if (cleaned_count) ixgbe_alloc_rx_buffers(adapter, rx_ring, cleaned_count); + adapter->net_stats.rx_bytes += total_rx_bytes; + adapter->net_stats.rx_packets += total_rx_packets; + return cleaned; } @@ -535,7 +610,9 @@ static irqreturn_t ixgbe_msix_lsc(int irq, void *data) if (!test_bit(__IXGBE_DOWN, &adapter->state)) mod_timer(&adapter->watchdog_timer, jiffies); } - IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMS, IXGBE_EIMS_OTHER); + + if (!test_bit(__IXGBE_DOWN, &adapter->state)) + IXGBE_WRITE_REG(hw, IXGBE_EIMS, IXGBE_EIMS_OTHER); return IRQ_HANDLED; } @@ -713,7 +790,6 @@ static irqreturn_t ixgbe_intr(int irq, void *data) if (netif_rx_schedule_prep(netdev, &adapter->napi)) { /* Disable interrupts and register for poll. The flush of the * posted write is intentionally left out. */ - atomic_inc(&adapter->irq_sem); IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMC, ~0); __netif_rx_schedule(netdev, &adapter->napi); } @@ -801,7 +877,6 @@ static void ixgbe_free_irq(struct ixgbe_adapter *adapter) **/ static inline void ixgbe_irq_disable(struct ixgbe_adapter *adapter) { - atomic_inc(&adapter->irq_sem); IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMC, ~0); IXGBE_WRITE_FLUSH(&adapter->hw); synchronize_irq(adapter->pdev->irq); @@ -813,15 +888,13 @@ static inline void ixgbe_irq_disable(struct ixgbe_adapter *adapter) **/ static inline void ixgbe_irq_enable(struct ixgbe_adapter *adapter) { - if (atomic_dec_and_test(&adapter->irq_sem)) { - if (adapter->flags & IXGBE_FLAG_MSIX_ENABLED) - IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIAC, - (IXGBE_EIMS_ENABLE_MASK & - ~(IXGBE_EIMS_OTHER | IXGBE_EIMS_LSC))); - IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMS, - IXGBE_EIMS_ENABLE_MASK); - IXGBE_WRITE_FLUSH(&adapter->hw); - } + if (adapter->flags & IXGBE_FLAG_MSIX_ENABLED) + IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIAC, + (IXGBE_EIMS_ENABLE_MASK & + ~(IXGBE_EIMS_OTHER | IXGBE_EIMS_LSC))); + IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMS, + IXGBE_EIMS_ENABLE_MASK); + IXGBE_WRITE_FLUSH(&adapter->hw); } /** @@ -1040,7 +1113,8 @@ static void ixgbe_vlan_rx_register(struct net_device *netdev, struct ixgbe_adapter *adapter = netdev_priv(netdev); u32 ctrl; - ixgbe_irq_disable(adapter); + if (!test_bit(__IXGBE_DOWN, &adapter->state)) + ixgbe_irq_disable(adapter); adapter->vlgrp = grp; if (grp) { @@ -1051,7 +1125,8 @@ static void ixgbe_vlan_rx_register(struct net_device *netdev, IXGBE_WRITE_REG(&adapter->hw, IXGBE_VLNCTRL, ctrl); } - ixgbe_irq_enable(adapter); + if (!test_bit(__IXGBE_DOWN, &adapter->state)) + ixgbe_irq_enable(adapter); } static void ixgbe_vlan_rx_add_vid(struct net_device *netdev, u16 vid) @@ -1066,9 +1141,13 @@ static void ixgbe_vlan_rx_kill_vid(struct net_device *netdev, u16 vid) { struct ixgbe_adapter *adapter = netdev_priv(netdev); - ixgbe_irq_disable(adapter); + if (!test_bit(__IXGBE_DOWN, &adapter->state)) + ixgbe_irq_disable(adapter); + vlan_group_set_device(adapter->vlgrp, vid, NULL); - ixgbe_irq_enable(adapter); + + if (!test_bit(__IXGBE_DOWN, &adapter->state)) + ixgbe_irq_enable(adapter); /* remove VID from filter table */ ixgbe_set_vfta(&adapter->hw, vid, 0, false); @@ -1170,6 +1249,8 @@ static int ixgbe_up_complete(struct ixgbe_adapter *adapter) u32 txdctl, rxdctl, mhadd; int max_frame = netdev->mtu + ETH_HLEN + ETH_FCS_LEN; + ixgbe_get_hw_control(adapter); + if (adapter->flags & (IXGBE_FLAG_MSIX_ENABLED | IXGBE_FLAG_MSI_ENABLED)) { if (adapter->flags & IXGBE_FLAG_MSIX_ENABLED) { @@ -1224,6 +1305,16 @@ static int ixgbe_up_complete(struct ixgbe_adapter *adapter) return 0; } +void ixgbe_reinit_locked(struct ixgbe_adapter *adapter) +{ + WARN_ON(in_interrupt()); + while (test_and_set_bit(__IXGBE_RESETTING, &adapter->state)) + msleep(1); + ixgbe_down(adapter); + ixgbe_up(adapter); + clear_bit(__IXGBE_RESETTING, &adapter->state); +} + int ixgbe_up(struct ixgbe_adapter *adapter) { /* hardware has been reset, we need to reload some things */ @@ -1408,7 +1499,6 @@ void ixgbe_down(struct ixgbe_adapter *adapter) msleep(10); napi_disable(&adapter->napi); - atomic_set(&adapter->irq_sem, 0); ixgbe_irq_disable(adapter); @@ -1447,6 +1537,8 @@ static int ixgbe_suspend(struct pci_dev *pdev, pm_message_t state) pci_enable_wake(pdev, PCI_D3hot, 0); pci_enable_wake(pdev, PCI_D3cold, 0); + ixgbe_release_hw_control(adapter); + pci_disable_device(pdev); pci_set_power_state(pdev, pci_choose_state(pdev, state)); @@ -1481,7 +1573,8 @@ static int ixgbe_clean(struct napi_struct *napi, int budget) /* If budget not fully consumed, exit the polling mode */ if (work_done < budget) { netif_rx_complete(netdev, napi); - ixgbe_irq_enable(adapter); + if (!test_bit(__IXGBE_DOWN, &adapter->state)) + ixgbe_irq_enable(adapter); } return work_done; @@ -1506,8 +1599,7 @@ static void ixgbe_reset_task(struct work_struct *work) adapter->tx_timeout_count++; - ixgbe_down(adapter); - ixgbe_up(adapter); + ixgbe_reinit_locked(adapter); } /** @@ -1590,7 +1682,6 @@ static int __devinit ixgbe_sw_init(struct ixgbe_adapter *adapter) return -ENOMEM; } - atomic_set(&adapter->irq_sem, 1); set_bit(__IXGBE_DOWN, &adapter->state); return 0; @@ -1634,7 +1725,6 @@ int ixgbe_setup_tx_resources(struct ixgbe_adapter *adapter, txdr->next_to_use = 0; txdr->next_to_clean = 0; txdr->work_limit = txdr->count; - spin_lock_init(&txdr->tx_lock); return 0; } @@ -1828,10 +1918,8 @@ static int ixgbe_change_mtu(struct net_device *netdev, int new_mtu) netdev->mtu = new_mtu; - if (netif_running(netdev)) { - ixgbe_down(adapter); - ixgbe_up(adapter); - } + if (netif_running(netdev)) + ixgbe_reinit_locked(adapter); return 0; } @@ -1852,14 +1940,8 @@ static int ixgbe_open(struct net_device *netdev) { struct ixgbe_adapter *adapter = netdev_priv(netdev); int err; - u32 ctrl_ext; u32 num_rx_queues = adapter->num_rx_queues; - /* Let firmware know the driver has taken over */ - ctrl_ext = IXGBE_READ_REG(&adapter->hw, IXGBE_CTRL_EXT); - IXGBE_WRITE_REG(&adapter->hw, IXGBE_CTRL_EXT, - ctrl_ext | IXGBE_CTRL_EXT_DRV_LOAD); - try_intr_reinit: /* allocate transmit descriptors */ err = ixgbe_setup_all_tx_resources(adapter); @@ -1910,6 +1992,7 @@ try_intr_reinit: return 0; err_up: + ixgbe_release_hw_control(adapter); ixgbe_free_irq(adapter); err_req_irq: ixgbe_free_all_rx_resources(adapter); @@ -1935,7 +2018,6 @@ err_setup_tx: static int ixgbe_close(struct net_device *netdev) { struct ixgbe_adapter *adapter = netdev_priv(netdev); - u32 ctrl_ext; ixgbe_down(adapter); ixgbe_free_irq(adapter); @@ -1943,9 +2025,7 @@ static int ixgbe_close(struct net_device *netdev) ixgbe_free_all_tx_resources(adapter); ixgbe_free_all_rx_resources(adapter); - ctrl_ext = IXGBE_READ_REG(&adapter->hw, IXGBE_CTRL_EXT); - IXGBE_WRITE_REG(&adapter->hw, IXGBE_CTRL_EXT, - ctrl_ext & ~IXGBE_CTRL_EXT_DRV_LOAD); + ixgbe_release_hw_control(adapter); return 0; } @@ -1957,22 +2037,26 @@ static int ixgbe_close(struct net_device *netdev) void ixgbe_update_stats(struct ixgbe_adapter *adapter) { struct ixgbe_hw *hw = &adapter->hw; - u64 good_rx, missed_rx, bprc; + u64 total_mpc = 0; + u32 i, missed_rx = 0, mpc, bprc, lxon, lxoff, xon_off_tot; adapter->stats.crcerrs += IXGBE_READ_REG(hw, IXGBE_CRCERRS); - good_rx = IXGBE_READ_REG(hw, IXGBE_GPRC); - missed_rx = IXGBE_READ_REG(hw, IXGBE_MPC(0)); - missed_rx += IXGBE_READ_REG(hw, IXGBE_MPC(1)); - missed_rx += IXGBE_READ_REG(hw, IXGBE_MPC(2)); - missed_rx += IXGBE_READ_REG(hw, IXGBE_MPC(3)); - missed_rx += IXGBE_READ_REG(hw, IXGBE_MPC(4)); - missed_rx += IXGBE_READ_REG(hw, IXGBE_MPC(5)); - missed_rx += IXGBE_READ_REG(hw, IXGBE_MPC(6)); - missed_rx += IXGBE_READ_REG(hw, IXGBE_MPC(7)); - adapter->stats.gprc += (good_rx - missed_rx); - - adapter->stats.mpc[0] += missed_rx; + for (i = 0; i < 8; i++) { + /* for packet buffers not used, the register should read 0 */ + mpc = IXGBE_READ_REG(hw, IXGBE_MPC(i)); + missed_rx += mpc; + adapter->stats.mpc[i] += mpc; + total_mpc += adapter->stats.mpc[i]; + adapter->stats.rnbc[i] += IXGBE_READ_REG(hw, IXGBE_RNBC(i)); + } + adapter->stats.gprc += IXGBE_READ_REG(hw, IXGBE_GPRC); + /* work around hardware counting issue */ + adapter->stats.gprc -= missed_rx; + + /* 82598 hardware only has a 32 bit counter in the high register */ adapter->stats.gorc += IXGBE_READ_REG(hw, IXGBE_GORCH); + adapter->stats.gotc += IXGBE_READ_REG(hw, IXGBE_GOTCH); + adapter->stats.tor += IXGBE_READ_REG(hw, IXGBE_TORH); bprc = IXGBE_READ_REG(hw, IXGBE_BPRC); adapter->stats.bprc += bprc; adapter->stats.mprc += IXGBE_READ_REG(hw, IXGBE_MPRC); @@ -1984,35 +2068,37 @@ void ixgbe_update_stats(struct ixgbe_adapter *adapter) adapter->stats.prc511 += IXGBE_READ_REG(hw, IXGBE_PRC511); adapter->stats.prc1023 += IXGBE_READ_REG(hw, IXGBE_PRC1023); adapter->stats.prc1522 += IXGBE_READ_REG(hw, IXGBE_PRC1522); - adapter->stats.rlec += IXGBE_READ_REG(hw, IXGBE_RLEC); adapter->stats.lxonrxc += IXGBE_READ_REG(hw, IXGBE_LXONRXC); - adapter->stats.lxontxc += IXGBE_READ_REG(hw, IXGBE_LXONTXC); adapter->stats.lxoffrxc += IXGBE_READ_REG(hw, IXGBE_LXOFFRXC); - adapter->stats.lxofftxc += IXGBE_READ_REG(hw, IXGBE_LXOFFTXC); + lxon = IXGBE_READ_REG(hw, IXGBE_LXONTXC); + adapter->stats.lxontxc += lxon; + lxoff = IXGBE_READ_REG(hw, IXGBE_LXOFFTXC); + adapter->stats.lxofftxc += lxoff; adapter->stats.ruc += IXGBE_READ_REG(hw, IXGBE_RUC); adapter->stats.gptc += IXGBE_READ_REG(hw, IXGBE_GPTC); - adapter->stats.gotc += IXGBE_READ_REG(hw, IXGBE_GOTCH); - adapter->stats.rnbc[0] += IXGBE_READ_REG(hw, IXGBE_RNBC(0)); + adapter->stats.mptc += IXGBE_READ_REG(hw, IXGBE_MPTC); + /* + * 82598 errata - tx of flow control packets is included in tx counters + */ + xon_off_tot = lxon + lxoff; + adapter->stats.gptc -= xon_off_tot; + adapter->stats.mptc -= xon_off_tot; + adapter->stats.gotc -= (xon_off_tot * (ETH_ZLEN + ETH_FCS_LEN)); adapter->stats.ruc += IXGBE_READ_REG(hw, IXGBE_RUC); adapter->stats.rfc += IXGBE_READ_REG(hw, IXGBE_RFC); adapter->stats.rjc += IXGBE_READ_REG(hw, IXGBE_RJC); - adapter->stats.tor += IXGBE_READ_REG(hw, IXGBE_TORH); adapter->stats.tpr += IXGBE_READ_REG(hw, IXGBE_TPR); adapter->stats.ptc64 += IXGBE_READ_REG(hw, IXGBE_PTC64); + adapter->stats.ptc64 -= xon_off_tot; adapter->stats.ptc127 += IXGBE_READ_REG(hw, IXGBE_PTC127); adapter->stats.ptc255 += IXGBE_READ_REG(hw, IXGBE_PTC255); adapter->stats.ptc511 += IXGBE_READ_REG(hw, IXGBE_PTC511); adapter->stats.ptc1023 += IXGBE_READ_REG(hw, IXGBE_PTC1023); adapter->stats.ptc1522 += IXGBE_READ_REG(hw, IXGBE_PTC1522); - adapter->stats.mptc += IXGBE_READ_REG(hw, IXGBE_MPTC); adapter->stats.bptc += IXGBE_READ_REG(hw, IXGBE_BPTC); /* Fill out the OS statistics structure */ - adapter->net_stats.rx_packets = adapter->stats.gprc; - adapter->net_stats.tx_packets = adapter->stats.gptc; - adapter->net_stats.rx_bytes = adapter->stats.gorc; - adapter->net_stats.tx_bytes = adapter->stats.gotc; adapter->net_stats.multicast = adapter->stats.mprc; /* Rx Errors */ @@ -2021,8 +2107,7 @@ void ixgbe_update_stats(struct ixgbe_adapter *adapter) adapter->net_stats.rx_dropped = 0; adapter->net_stats.rx_length_errors = adapter->stats.rlec; adapter->net_stats.rx_crc_errors = adapter->stats.crcerrs; - adapter->net_stats.rx_missed_errors = adapter->stats.mpc[0]; - + adapter->net_stats.rx_missed_errors = total_mpc; } /** @@ -2076,15 +2161,6 @@ static void ixgbe_watchdog(unsigned long data) round_jiffies(jiffies + 2 * HZ)); } -#define IXGBE_MAX_TXD_PWR 14 -#define IXGBE_MAX_DATA_PER_TXD (1 << IXGBE_MAX_TXD_PWR) - -/* Tx Descriptors needed, worst case */ -#define TXD_USE_COUNT(S) (((S) >> IXGBE_MAX_TXD_PWR) + \ - (((S) & (IXGBE_MAX_DATA_PER_TXD - 1)) ? 1 : 0)) -#define DESC_NEEDED (TXD_USE_COUNT(IXGBE_MAX_DATA_PER_TXD) /* skb->data */ + \ - MAX_SKB_FRAGS * TXD_USE_COUNT(PAGE_SIZE) + 1) /* for context */ - static int ixgbe_tso(struct ixgbe_adapter *adapter, struct ixgbe_ring *tx_ring, struct sk_buff *skb, u32 tx_flags, u8 *hdr_len) @@ -2356,6 +2432,37 @@ static void ixgbe_tx_queue(struct ixgbe_adapter *adapter, writel(i, adapter->hw.hw_addr + tx_ring->tail); } +static int __ixgbe_maybe_stop_tx(struct net_device *netdev, + struct ixgbe_ring *tx_ring, int size) +{ + struct ixgbe_adapter *adapter = netdev_priv(netdev); + + netif_stop_queue(netdev); + /* Herbert's original patch had: + * smp_mb__after_netif_stop_queue(); + * but since that doesn't exist yet, just open code it. */ + smp_mb(); + + /* We need to check again in a case another CPU has just + * made room available. */ + if (likely(IXGBE_DESC_UNUSED(tx_ring) < size)) + return -EBUSY; + + /* A reprieve! - use start_queue because it doesn't call schedule */ + netif_wake_queue(netdev); + ++adapter->restart_queue; + return 0; +} + +static int ixgbe_maybe_stop_tx(struct net_device *netdev, + struct ixgbe_ring *tx_ring, int size) +{ + if (likely(IXGBE_DESC_UNUSED(tx_ring) >= size)) + return 0; + return __ixgbe_maybe_stop_tx(netdev, tx_ring, size); +} + + static int ixgbe_xmit_frame(struct sk_buff *skb, struct net_device *netdev) { struct ixgbe_adapter *adapter = netdev_priv(netdev); @@ -2363,7 +2470,6 @@ static int ixgbe_xmit_frame(struct sk_buff *skb, struct net_device *netdev) unsigned int len = skb->len; unsigned int first; unsigned int tx_flags = 0; - unsigned long flags = 0; u8 hdr_len; int tso; unsigned int mss = 0; @@ -2389,14 +2495,10 @@ static int ixgbe_xmit_frame(struct sk_buff *skb, struct net_device *netdev) for (f = 0; f < nr_frags; f++) count += TXD_USE_COUNT(skb_shinfo(skb)->frags[f].size); - spin_lock_irqsave(&tx_ring->tx_lock, flags); - if (IXGBE_DESC_UNUSED(tx_ring) < (count + 2)) { + if (ixgbe_maybe_stop_tx(netdev, tx_ring, count)) { adapter->tx_busy++; - netif_stop_queue(netdev); - spin_unlock_irqrestore(&tx_ring->tx_lock, flags); return NETDEV_TX_BUSY; } - spin_unlock_irqrestore(&tx_ring->tx_lock, flags); if (adapter->vlgrp && vlan_tx_tag_present(skb)) { tx_flags |= IXGBE_TX_FLAGS_VLAN; tx_flags |= (vlan_tx_tag_get(skb) << IXGBE_TX_FLAGS_VLAN_SHIFT); @@ -2423,11 +2525,7 @@ static int ixgbe_xmit_frame(struct sk_buff *skb, struct net_device *netdev) netdev->trans_start = jiffies; - spin_lock_irqsave(&tx_ring->tx_lock, flags); - /* Make sure there is space in the ring for the next send. */ - if (IXGBE_DESC_UNUSED(tx_ring) < DESC_NEEDED) - netif_stop_queue(netdev); - spin_unlock_irqrestore(&tx_ring->tx_lock, flags); + ixgbe_maybe_stop_tx(netdev, tx_ring, DESC_NEEDED); return NETDEV_TX_OK; } @@ -2697,6 +2795,7 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev, return 0; err_register: + ixgbe_release_hw_control(adapter); err_hw_init: err_sw_init: err_eeprom: @@ -2732,6 +2831,8 @@ static void __devexit ixgbe_remove(struct pci_dev *pdev) unregister_netdev(netdev); + ixgbe_release_hw_control(adapter); + kfree(adapter->tx_ring); kfree(adapter->rx_ring); diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c index 651c2699d5e..b528ce77c40 100644 --- a/drivers/net/mv643xx_eth.c +++ b/drivers/net/mv643xx_eth.c @@ -1652,6 +1652,11 @@ static void eth_tx_fill_frag_descs(struct mv643xx_private *mp, } } +static inline __be16 sum16_as_be(__sum16 sum) +{ + return (__force __be16)sum; +} + /** * eth_tx_submit_descs_for_skb - submit data from an skb to the tx hw * @@ -1689,7 +1694,7 @@ static void eth_tx_submit_descs_for_skb(struct mv643xx_private *mp, desc->buf_ptr = dma_map_single(NULL, skb->data, length, DMA_TO_DEVICE); if (skb->ip_summed == CHECKSUM_PARTIAL) { - BUG_ON(skb->protocol != ETH_P_IP); + BUG_ON(skb->protocol != htons(ETH_P_IP)); cmd_sts |= ETH_GEN_TCP_UDP_CHECKSUM | ETH_GEN_IP_V_4_CHECKSUM | @@ -1698,10 +1703,10 @@ static void eth_tx_submit_descs_for_skb(struct mv643xx_private *mp, switch (ip_hdr(skb)->protocol) { case IPPROTO_UDP: cmd_sts |= ETH_UDP_FRAME; - desc->l4i_chk = udp_hdr(skb)->check; + desc->l4i_chk = ntohs(sum16_as_be(udp_hdr(skb)->check)); break; case IPPROTO_TCP: - desc->l4i_chk = tcp_hdr(skb)->check; + desc->l4i_chk = ntohs(sum16_as_be(tcp_hdr(skb)->check)); break; default: BUG(); diff --git a/drivers/net/pppol2tp.c b/drivers/net/pppol2tp.c index 1b51bb668d3..5aa0a808969 100644 --- a/drivers/net/pppol2tp.c +++ b/drivers/net/pppol2tp.c @@ -2468,9 +2468,10 @@ static int __init pppol2tp_init(void) out: return err; - +#ifdef CONFIG_PROC_FS out_unregister_pppox_proto: unregister_pppox_proto(PX_PROTO_OL2TP); +#endif out_unregister_pppol2tp_proto: proto_unregister(&pppol2tp_sk_proto); goto out; diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index dc062367a1c..9a6295909e4 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -857,7 +857,7 @@ static void sky2_mac_init(struct sky2_hw *hw, unsigned port) sky2_write16(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_OPER_ON); /* On chips without ram buffer, pause is controled by MAC level */ - if (sky2_read8(hw, B2_E_0) == 0) { + if (!(hw->flags & SKY2_HW_RAM_BUFFER)) { sky2_write8(hw, SK_REG(port, RX_GMF_LP_THR), 768/8); sky2_write8(hw, SK_REG(port, RX_GMF_UP_THR), 1024/8); @@ -1194,7 +1194,7 @@ static struct sk_buff *sky2_rx_alloc(struct sky2_port *sky2) struct sk_buff *skb; int i; - if (sky2->hw->flags & SKY2_HW_FIFO_HANG_CHECK) { + if (sky2->hw->flags & SKY2_HW_RAM_BUFFER) { unsigned char *start; /* * Workaround for a bug in FIFO that cause hang @@ -1387,6 +1387,7 @@ static int sky2_up(struct net_device *dev) if (ramsize > 0) { u32 rxspace; + hw->flags |= SKY2_HW_RAM_BUFFER; pr_debug(PFX "%s: ram buffer %dK\n", dev->name, ramsize); if (ramsize < 16) rxspace = ramsize / 2; @@ -2026,7 +2027,7 @@ static int sky2_change_mtu(struct net_device *dev, int new_mtu) synchronize_irq(hw->pdev->irq); - if (sky2_read8(hw, B2_E_0) == 0) + if (!(hw->flags & SKY2_HW_RAM_BUFFER)) sky2_set_tx_stfwd(hw, port); ctl = gma_read16(hw, port, GM_GP_CTRL); @@ -2566,7 +2567,7 @@ static void sky2_watchdog(unsigned long arg) ++active; /* For chips with Rx FIFO, check if stuck */ - if ((hw->flags & SKY2_HW_FIFO_HANG_CHECK) && + if ((hw->flags & SKY2_HW_RAM_BUFFER) && sky2_rx_hung(dev)) { pr_info(PFX "%s: receiver hang detected\n", dev->name); @@ -2722,11 +2723,7 @@ static int __devinit sky2_init(struct sky2_hw *hw) switch(hw->chip_id) { case CHIP_ID_YUKON_XL: - hw->flags = SKY2_HW_GIGABIT - | SKY2_HW_NEWER_PHY; - if (hw->chip_rev < 3) - hw->flags |= SKY2_HW_FIFO_HANG_CHECK; - + hw->flags = SKY2_HW_GIGABIT | SKY2_HW_NEWER_PHY; break; case CHIP_ID_YUKON_EC_U: @@ -2752,7 +2749,7 @@ static int __devinit sky2_init(struct sky2_hw *hw) dev_err(&hw->pdev->dev, "unsupported revision Yukon-EC rev A1\n"); return -EOPNOTSUPP; } - hw->flags = SKY2_HW_GIGABIT | SKY2_HW_FIFO_HANG_CHECK; + hw->flags = SKY2_HW_GIGABIT; break; case CHIP_ID_YUKON_FE: diff --git a/drivers/net/sky2.h b/drivers/net/sky2.h index 2bced1a0898..5ab5c1c7c5a 100644 --- a/drivers/net/sky2.h +++ b/drivers/net/sky2.h @@ -2045,7 +2045,7 @@ struct sky2_hw { #define SKY2_HW_FIBRE_PHY 0x00000002 #define SKY2_HW_GIGABIT 0x00000004 #define SKY2_HW_NEWER_PHY 0x00000008 -#define SKY2_HW_FIFO_HANG_CHECK 0x00000010 +#define SKY2_HW_RAM_BUFFER 0x00000010 #define SKY2_HW_NEW_LE 0x00000020 /* new LSOv2 format */ #define SKY2_HW_AUTO_TX_SUM 0x00000040 /* new IP decode for Tx */ #define SKY2_HW_ADV_POWER_CTL 0x00000080 /* additional PHY power regs */ diff --git a/drivers/net/tlan.c b/drivers/net/tlan.c index c99ce74a7af..3af5b92b48c 100644 --- a/drivers/net/tlan.c +++ b/drivers/net/tlan.c @@ -465,7 +465,7 @@ static struct pci_driver tlan_driver = { static int __init tlan_probe(void) { - static int pad_allocated; + int rc = -ENODEV; printk(KERN_INFO "%s", tlan_banner); @@ -473,17 +473,22 @@ static int __init tlan_probe(void) if (TLanPadBuffer == NULL) { printk(KERN_ERR "TLAN: Could not allocate memory for pad buffer.\n"); - return -ENOMEM; + rc = -ENOMEM; + goto err_out; } memset(TLanPadBuffer, 0, TLAN_MIN_FRAME_SIZE); - pad_allocated = 1; TLAN_DBG(TLAN_DEBUG_PROBE, "Starting PCI Probe....\n"); /* Use new style PCI probing. Now the kernel will do most of this for us */ - pci_register_driver(&tlan_driver); + rc = pci_register_driver(&tlan_driver); + + if (rc != 0) { + printk(KERN_ERR "TLAN: Could not register pci driver.\n"); + goto err_out_pci_free; + } TLAN_DBG(TLAN_DEBUG_PROBE, "Starting EISA Probe....\n"); TLan_EisaProbe(); @@ -493,11 +498,17 @@ static int __init tlan_probe(void) tlan_have_pci, tlan_have_eisa); if (TLanDevicesInstalled == 0) { - pci_unregister_driver(&tlan_driver); - pci_free_consistent(NULL, TLAN_MIN_FRAME_SIZE, TLanPadBuffer, TLanPadBufferDMA); - return -ENODEV; + rc = -ENODEV; + goto err_out_pci_unreg; } return 0; + +err_out_pci_unreg: + pci_unregister_driver(&tlan_driver); +err_out_pci_free: + pci_free_consistent(NULL, TLAN_MIN_FRAME_SIZE, TLanPadBuffer, TLanPadBufferDMA); +err_out: + return rc; } diff --git a/drivers/net/tulip/xircom_cb.c b/drivers/net/tulip/xircom_cb.c index 8fc7274642e..6b93d016911 100644 --- a/drivers/net/tulip/xircom_cb.c +++ b/drivers/net/tulip/xircom_cb.c @@ -441,7 +441,7 @@ static int xircom_start_xmit(struct sk_buff *skb, struct net_device *dev) spin_unlock_irqrestore(&card->lock,flags); trigger_transmit(card); - return -EIO; + return NETDEV_TX_BUSY; } diff --git a/drivers/net/ucc_geth_mii.c b/drivers/net/ucc_geth_mii.c index e3ba14a1991..c69e654d539 100644 --- a/drivers/net/ucc_geth_mii.c +++ b/drivers/net/ucc_geth_mii.c @@ -109,7 +109,7 @@ int uec_mdio_reset(struct mii_bus *bus) struct ucc_mii_mng __iomem *regs = (void __iomem *)bus->priv; unsigned int timeout = PHY_INIT_TIMEOUT; - spin_lock_bh(&bus->mdio_lock); + mutex_lock(&bus->mdio_lock); /* Reset the management interface */ out_be32(®s->miimcfg, MIIMCFG_RESET_MANAGEMENT); @@ -121,7 +121,7 @@ int uec_mdio_reset(struct mii_bus *bus) while ((in_be32(®s->miimind) & MIIMIND_BUSY) && timeout--) cpu_relax(); - spin_unlock_bh(&bus->mdio_lock); + mutex_unlock(&bus->mdio_lock); if (timeout <= 0) { printk(KERN_ERR "%s: The MII Bus is stuck!\n", bus->name); diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index e66de0c12fc..fdc23678117 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -302,10 +302,12 @@ static int virtnet_open(struct net_device *dev) /* If all buffers were filled by other side before we napi_enabled, we * won't get another interrupt, so process any outstanding packets - * now. virtnet_poll wants re-enable the queue, so we disable here. */ - vi->rvq->vq_ops->disable_cb(vi->rvq); - netif_rx_schedule(vi->dev, &vi->napi); - + * now. virtnet_poll wants re-enable the queue, so we disable here. + * We synchronize against interrupts via NAPI_STATE_SCHED */ + if (netif_rx_schedule_prep(dev, &vi->napi)) { + vi->rvq->vq_ops->disable_cb(vi->rvq); + __netif_rx_schedule(dev, &vi->napi); + } return 0; } diff --git a/drivers/net/wan/hdlc.c b/drivers/net/wan/hdlc.c index d553e6f3285..39951d0c34d 100644 --- a/drivers/net/wan/hdlc.c +++ b/drivers/net/wan/hdlc.c @@ -1,7 +1,7 @@ /* * Generic HDLC support routines for Linux * - * Copyright (C) 1999 - 2006 Krzysztof Halasa <khc@pm.waw.pl> + * Copyright (C) 1999 - 2008 Krzysztof Halasa <khc@pm.waw.pl> * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License @@ -39,7 +39,7 @@ #include <net/net_namespace.h> -static const char* version = "HDLC support module revision 1.21"; +static const char* version = "HDLC support module revision 1.22"; #undef DEBUG_LINK @@ -66,19 +66,15 @@ static struct net_device_stats *hdlc_get_stats(struct net_device *dev) static int hdlc_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *p, struct net_device *orig_dev) { - struct hdlc_device_desc *desc = dev_to_desc(dev); + struct hdlc_device *hdlc = dev_to_hdlc(dev); if (dev->nd_net != &init_net) { kfree_skb(skb); return 0; } - if (desc->netif_rx) - return desc->netif_rx(skb); - - desc->stats.rx_dropped++; /* Shouldn't happen */ - dev_kfree_skb(skb); - return NET_RX_DROP; + BUG_ON(!hdlc->proto->netif_rx); + return hdlc->proto->netif_rx(skb); } @@ -87,7 +83,7 @@ static inline void hdlc_proto_start(struct net_device *dev) { hdlc_device *hdlc = dev_to_hdlc(dev); if (hdlc->proto->start) - return hdlc->proto->start(dev); + hdlc->proto->start(dev); } @@ -96,7 +92,7 @@ static inline void hdlc_proto_stop(struct net_device *dev) { hdlc_device *hdlc = dev_to_hdlc(dev); if (hdlc->proto->stop) - return hdlc->proto->stop(dev); + hdlc->proto->stop(dev); } @@ -263,8 +259,7 @@ static void hdlc_setup(struct net_device *dev) struct net_device *alloc_hdlcdev(void *priv) { struct net_device *dev; - dev = alloc_netdev(sizeof(struct hdlc_device_desc) + - sizeof(hdlc_device), "hdlc%d", hdlc_setup); + dev = alloc_netdev(sizeof(struct hdlc_device), "hdlc%d", hdlc_setup); if (dev) dev_to_hdlc(dev)->priv = priv; return dev; @@ -281,7 +276,7 @@ void unregister_hdlc_device(struct net_device *dev) int attach_hdlc_protocol(struct net_device *dev, struct hdlc_proto *proto, - int (*rx)(struct sk_buff *skb), size_t size) + size_t size) { detach_hdlc_protocol(dev); @@ -297,7 +292,6 @@ int attach_hdlc_protocol(struct net_device *dev, struct hdlc_proto *proto, return -ENOBUFS; } dev_to_hdlc(dev)->proto = proto; - dev_to_desc(dev)->netif_rx = rx; return 0; } diff --git a/drivers/net/wan/hdlc_cisco.c b/drivers/net/wan/hdlc_cisco.c index 038a6e748bb..7133c688cf2 100644 --- a/drivers/net/wan/hdlc_cisco.c +++ b/drivers/net/wan/hdlc_cisco.c @@ -250,7 +250,7 @@ static int cisco_rx(struct sk_buff *skb) return NET_RX_DROP; rx_error: - dev_to_desc(dev)->stats.rx_errors++; /* Mark error */ + dev_to_hdlc(dev)->stats.rx_errors++; /* Mark error */ dev_kfree_skb_any(skb); return NET_RX_DROP; } @@ -314,6 +314,7 @@ static struct hdlc_proto proto = { .stop = cisco_stop, .type_trans = cisco_type_trans, .ioctl = cisco_ioctl, + .netif_rx = cisco_rx, .module = THIS_MODULE, }; @@ -360,7 +361,7 @@ static int cisco_ioctl(struct net_device *dev, struct ifreq *ifr) if (result) return result; - result = attach_hdlc_protocol(dev, &proto, cisco_rx, + result = attach_hdlc_protocol(dev, &proto, sizeof(struct cisco_state)); if (result) return result; diff --git a/drivers/net/wan/hdlc_fr.c b/drivers/net/wan/hdlc_fr.c index 071a64cacd5..c4ab0326f91 100644 --- a/drivers/net/wan/hdlc_fr.c +++ b/drivers/net/wan/hdlc_fr.c @@ -42,7 +42,6 @@ #include <linux/init.h> #include <linux/skbuff.h> #include <linux/pkt_sched.h> -#include <linux/random.h> #include <linux/inetdevice.h> #include <linux/lapb.h> #include <linux/rtnetlink.h> @@ -136,6 +135,10 @@ typedef struct pvc_device_struct { }state; }pvc_device; +struct pvc_desc { + struct net_device_stats stats; + pvc_device *pvc; +}; struct frad_state { fr_proto settings; @@ -171,17 +174,20 @@ static inline void dlci_to_q922(u8 *hdr, u16 dlci) } -static inline struct frad_state * state(hdlc_device *hdlc) +static inline struct frad_state* state(hdlc_device *hdlc) { return(struct frad_state *)(hdlc->state); } - -static __inline__ pvc_device* dev_to_pvc(struct net_device *dev) +static inline struct pvc_desc* pvcdev_to_desc(struct net_device *dev) { return dev->priv; } +static inline struct net_device_stats* pvc_get_stats(struct net_device *dev) +{ + return &pvcdev_to_desc(dev)->stats; +} static inline pvc_device* find_pvc(hdlc_device *hdlc, u16 dlci) { @@ -351,7 +357,7 @@ static int fr_hard_header(struct sk_buff **skb_p, u16 dlci) static int pvc_open(struct net_device *dev) { - pvc_device *pvc = dev_to_pvc(dev); + pvc_device *pvc = pvcdev_to_desc(dev)->pvc; if ((pvc->frad->flags & IFF_UP) == 0) return -EIO; /* Frad must be UP in order to activate PVC */ @@ -371,7 +377,7 @@ static int pvc_open(struct net_device *dev) static int pvc_close(struct net_device *dev) { - pvc_device *pvc = dev_to_pvc(dev); + pvc_device *pvc = pvcdev_to_desc(dev)->pvc; if (--pvc->open_count == 0) { hdlc_device *hdlc = dev_to_hdlc(pvc->frad); @@ -390,7 +396,7 @@ static int pvc_close(struct net_device *dev) static int pvc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { - pvc_device *pvc = dev_to_pvc(dev); + pvc_device *pvc = pvcdev_to_desc(dev)->pvc; fr_proto_pvc_info info; if (ifr->ifr_settings.type == IF_GET_PROTO) { @@ -416,17 +422,9 @@ static int pvc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) return -EINVAL; } - -static inline struct net_device_stats *pvc_get_stats(struct net_device *dev) -{ - return &dev_to_desc(dev)->stats; -} - - - static int pvc_xmit(struct sk_buff *skb, struct net_device *dev) { - pvc_device *pvc = dev_to_pvc(dev); + pvc_device *pvc = pvcdev_to_desc(dev)->pvc; struct net_device_stats *stats = pvc_get_stats(dev); if (pvc->state.active) { @@ -957,7 +955,7 @@ static int fr_rx(struct sk_buff *skb) if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) { - dev_to_desc(frad)->stats.rx_dropped++; + dev_to_hdlc(frad)->stats.rx_dropped++; return NET_RX_DROP; } @@ -1018,7 +1016,7 @@ static int fr_rx(struct sk_buff *skb) } rx_error: - dev_to_desc(frad)->stats.rx_errors++; /* Mark error */ + dev_to_hdlc(frad)->stats.rx_errors++; /* Mark error */ dev_kfree_skb_any(skb); return NET_RX_DROP; } @@ -1109,11 +1107,10 @@ static int fr_add_pvc(struct net_device *frad, unsigned int dlci, int type) used = pvc_is_used(pvc); if (type == ARPHRD_ETHER) - dev = alloc_netdev(sizeof(struct net_device_stats), - "pvceth%d", ether_setup); + dev = alloc_netdev(sizeof(struct pvc_desc), "pvceth%d", + ether_setup); else - dev = alloc_netdev(sizeof(struct net_device_stats), - "pvc%d", pvc_setup); + dev = alloc_netdev(sizeof(struct pvc_desc), "pvc%d", pvc_setup); if (!dev) { printk(KERN_WARNING "%s: Memory squeeze on fr_pvc()\n", @@ -1122,10 +1119,9 @@ static int fr_add_pvc(struct net_device *frad, unsigned int dlci, int type) return -ENOBUFS; } - if (type == ARPHRD_ETHER) { - memcpy(dev->dev_addr, "\x00\x01", 2); - get_random_bytes(dev->dev_addr + 2, ETH_ALEN - 2); - } else { + if (type == ARPHRD_ETHER) + random_ether_addr(dev->dev_addr); + else { *(__be16*)dev->dev_addr = htons(dlci); dlci_to_q922(dev->broadcast, dlci); } @@ -1137,7 +1133,7 @@ static int fr_add_pvc(struct net_device *frad, unsigned int dlci, int type) dev->change_mtu = pvc_change_mtu; dev->mtu = HDLC_MAX_MTU; dev->tx_queue_len = 0; - dev->priv = pvc; + pvcdev_to_desc(dev)->pvc = pvc; result = dev_alloc_name(dev, dev->name); if (result < 0) { @@ -1219,6 +1215,7 @@ static struct hdlc_proto proto = { .stop = fr_stop, .detach = fr_destroy, .ioctl = fr_ioctl, + .netif_rx = fr_rx, .module = THIS_MODULE, }; @@ -1277,7 +1274,7 @@ static int fr_ioctl(struct net_device *dev, struct ifreq *ifr) return result; if (dev_to_hdlc(dev)->proto != &proto) { /* Different proto */ - result = attach_hdlc_protocol(dev, &proto, fr_rx, + result = attach_hdlc_protocol(dev, &proto, sizeof(struct frad_state)); if (result) return result; diff --git a/drivers/net/wan/hdlc_ppp.c b/drivers/net/wan/hdlc_ppp.c index 519e1550e2e..10396d9686f 100644 --- a/drivers/net/wan/hdlc_ppp.c +++ b/drivers/net/wan/hdlc_ppp.c @@ -122,7 +122,7 @@ static int ppp_ioctl(struct net_device *dev, struct ifreq *ifr) if (result) return result; - result = attach_hdlc_protocol(dev, &proto, NULL, + result = attach_hdlc_protocol(dev, &proto, sizeof(struct ppp_state)); if (result) return result; diff --git a/drivers/net/wan/hdlc_raw.c b/drivers/net/wan/hdlc_raw.c index e23bc665626..bbbb819d764 100644 --- a/drivers/net/wan/hdlc_raw.c +++ b/drivers/net/wan/hdlc_raw.c @@ -82,7 +82,7 @@ static int raw_ioctl(struct net_device *dev, struct ifreq *ifr) if (result) return result; - result = attach_hdlc_protocol(dev, &proto, NULL, + result = attach_hdlc_protocol(dev, &proto, sizeof(raw_hdlc_proto)); if (result) return result; diff --git a/drivers/net/wan/hdlc_raw_eth.c b/drivers/net/wan/hdlc_raw_eth.c index 8895394e600..d20c685f671 100644 --- a/drivers/net/wan/hdlc_raw_eth.c +++ b/drivers/net/wan/hdlc_raw_eth.c @@ -18,7 +18,6 @@ #include <linux/init.h> #include <linux/skbuff.h> #include <linux/pkt_sched.h> -#include <linux/random.h> #include <linux/inetdevice.h> #include <linux/lapb.h> #include <linux/rtnetlink.h> @@ -96,7 +95,7 @@ static int raw_eth_ioctl(struct net_device *dev, struct ifreq *ifr) if (result) return result; - result = attach_hdlc_protocol(dev, &proto, NULL, + result = attach_hdlc_protocol(dev, &proto, sizeof(raw_hdlc_proto)); if (result) return result; @@ -107,8 +106,7 @@ static int raw_eth_ioctl(struct net_device *dev, struct ifreq *ifr) ether_setup(dev); dev->change_mtu = old_ch_mtu; dev->tx_queue_len = old_qlen; - memcpy(dev->dev_addr, "\x00\x01", 2); - get_random_bytes(dev->dev_addr + 2, ETH_ALEN - 2); + random_ether_addr(dev->dev_addr); netif_dormant_off(dev); return 0; } diff --git a/drivers/net/wan/hdlc_x25.c b/drivers/net/wan/hdlc_x25.c index cd7b22f50ed..c15cc11e399 100644 --- a/drivers/net/wan/hdlc_x25.c +++ b/drivers/net/wan/hdlc_x25.c @@ -164,17 +164,17 @@ static void x25_close(struct net_device *dev) static int x25_rx(struct sk_buff *skb) { - struct hdlc_device_desc *desc = dev_to_desc(skb->dev); + struct hdlc_device *hdlc = dev_to_hdlc(skb->dev); if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) { - desc->stats.rx_dropped++; + hdlc->stats.rx_dropped++; return NET_RX_DROP; } if (lapb_data_received(skb->dev, skb) == LAPB_OK) return NET_RX_SUCCESS; - desc->stats.rx_errors++; + hdlc->stats.rx_errors++; dev_kfree_skb_any(skb); return NET_RX_DROP; } @@ -184,6 +184,7 @@ static struct hdlc_proto proto = { .open = x25_open, .close = x25_close, .ioctl = x25_ioctl, + .netif_rx = x25_rx, .module = THIS_MODULE, }; @@ -211,8 +212,7 @@ static int x25_ioctl(struct net_device *dev, struct ifreq *ifr) if (result) return result; - if ((result = attach_hdlc_protocol(dev, &proto, - x25_rx, 0)) != 0) + if ((result = attach_hdlc_protocol(dev, &proto, 0))) return result; dev->hard_start_xmit = x25_xmit; dev->type = ARPHRD_X25; diff --git a/drivers/net/wireless/b43/dma.c b/drivers/net/wireless/b43/dma.c index 8a708b77925..3dfb28a34be 100644 --- a/drivers/net/wireless/b43/dma.c +++ b/drivers/net/wireless/b43/dma.c @@ -337,7 +337,7 @@ static inline int txring_to_priority(struct b43_dmaring *ring) return idx_to_prio[index]; } -u16 b43_dmacontroller_base(int dma64bit, int controller_idx) +static u16 b43_dmacontroller_base(enum b43_dmatype type, int controller_idx) { static const u16 map64[] = { B43_MMIO_DMA64_BASE0, @@ -356,7 +356,7 @@ u16 b43_dmacontroller_base(int dma64bit, int controller_idx) B43_MMIO_DMA32_BASE5, }; - if (dma64bit) { + if (type == B43_DMA_64BIT) { B43_WARN_ON(!(controller_idx >= 0 && controller_idx < ARRAY_SIZE(map64))); return map64[controller_idx]; @@ -437,7 +437,7 @@ static int alloc_ringmemory(struct b43_dmaring *ring) * 02, which uses 64-bit DMA, needs the ring buffer in very low memory, * which accounts for the GFP_DMA flag below. */ - if (ring->dma64) + if (ring->type == B43_DMA_64BIT) flags |= GFP_DMA; ring->descbase = dma_alloc_coherent(dev, B43_DMA_RINGMEMSIZE, &(ring->dmabase), flags); @@ -459,7 +459,8 @@ static void free_ringmemory(struct b43_dmaring *ring) } /* Reset the RX DMA channel */ -int b43_dmacontroller_rx_reset(struct b43_wldev *dev, u16 mmio_base, int dma64) +static int b43_dmacontroller_rx_reset(struct b43_wldev *dev, u16 mmio_base, + enum b43_dmatype type) { int i; u32 value; @@ -467,12 +468,13 @@ int b43_dmacontroller_rx_reset(struct b43_wldev *dev, u16 mmio_base, int dma64) might_sleep(); - offset = dma64 ? B43_DMA64_RXCTL : B43_DMA32_RXCTL; + offset = (type == B43_DMA_64BIT) ? B43_DMA64_RXCTL : B43_DMA32_RXCTL; b43_write32(dev, mmio_base + offset, 0); for (i = 0; i < 10; i++) { - offset = dma64 ? B43_DMA64_RXSTATUS : B43_DMA32_RXSTATUS; + offset = (type == B43_DMA_64BIT) ? B43_DMA64_RXSTATUS : + B43_DMA32_RXSTATUS; value = b43_read32(dev, mmio_base + offset); - if (dma64) { + if (type == B43_DMA_64BIT) { value &= B43_DMA64_RXSTAT; if (value == B43_DMA64_RXSTAT_DISABLED) { i = -1; @@ -496,7 +498,8 @@ int b43_dmacontroller_rx_reset(struct b43_wldev *dev, u16 mmio_base, int dma64) } /* Reset the TX DMA channel */ -int b43_dmacontroller_tx_reset(struct b43_wldev *dev, u16 mmio_base, int dma64) +static int b43_dmacontroller_tx_reset(struct b43_wldev *dev, u16 mmio_base, + enum b43_dmatype type) { int i; u32 value; @@ -505,9 +508,10 @@ int b43_dmacontroller_tx_reset(struct b43_wldev *dev, u16 mmio_base, int dma64) might_sleep(); for (i = 0; i < 10; i++) { - offset = dma64 ? B43_DMA64_TXSTATUS : B43_DMA32_TXSTATUS; + offset = (type == B43_DMA_64BIT) ? B43_DMA64_TXSTATUS : + B43_DMA32_TXSTATUS; value = b43_read32(dev, mmio_base + offset); - if (dma64) { + if (type == B43_DMA_64BIT) { value &= B43_DMA64_TXSTAT; if (value == B43_DMA64_TXSTAT_DISABLED || value == B43_DMA64_TXSTAT_IDLEWAIT || @@ -522,12 +526,13 @@ int b43_dmacontroller_tx_reset(struct b43_wldev *dev, u16 mmio_base, int dma64) } msleep(1); } - offset = dma64 ? B43_DMA64_TXCTL : B43_DMA32_TXCTL; + offset = (type == B43_DMA_64BIT) ? B43_DMA64_TXCTL : B43_DMA32_TXCTL; b43_write32(dev, mmio_base + offset, 0); for (i = 0; i < 10; i++) { - offset = dma64 ? B43_DMA64_TXSTATUS : B43_DMA32_TXSTATUS; + offset = (type == B43_DMA_64BIT) ? B43_DMA64_TXSTATUS : + B43_DMA32_TXSTATUS; value = b43_read32(dev, mmio_base + offset); - if (dma64) { + if (type == B43_DMA_64BIT) { value &= B43_DMA64_TXSTAT; if (value == B43_DMA64_TXSTAT_DISABLED) { i = -1; @@ -552,6 +557,33 @@ int b43_dmacontroller_tx_reset(struct b43_wldev *dev, u16 mmio_base, int dma64) return 0; } +/* Check if a DMA mapping address is invalid. */ +static bool b43_dma_mapping_error(struct b43_dmaring *ring, + dma_addr_t addr, + size_t buffersize) +{ + if (unlikely(dma_mapping_error(addr))) + return 1; + + switch (ring->type) { + case B43_DMA_30BIT: + if ((u64)addr + buffersize > (1ULL << 30)) + return 1; + break; + case B43_DMA_32BIT: + if ((u64)addr + buffersize > (1ULL << 32)) + return 1; + break; + case B43_DMA_64BIT: + /* Currently we can't have addresses beyond + * 64bit in the kernel. */ + break; + } + + /* The address is OK. */ + return 0; +} + static int setup_rx_descbuffer(struct b43_dmaring *ring, struct b43_dmadesc_generic *desc, struct b43_dmadesc_meta *meta, gfp_t gfp_flags) @@ -567,7 +599,7 @@ static int setup_rx_descbuffer(struct b43_dmaring *ring, if (unlikely(!skb)) return -ENOMEM; dmaaddr = map_descbuffer(ring, skb->data, ring->rx_buffersize, 0); - if (dma_mapping_error(dmaaddr)) { + if (b43_dma_mapping_error(ring, dmaaddr, ring->rx_buffersize)) { /* ugh. try to realloc in zone_dma */ gfp_flags |= GFP_DMA; @@ -580,7 +612,7 @@ static int setup_rx_descbuffer(struct b43_dmaring *ring, ring->rx_buffersize, 0); } - if (dma_mapping_error(dmaaddr)) { + if (b43_dma_mapping_error(ring, dmaaddr, ring->rx_buffersize)) { dev_kfree_skb_any(skb); return -EIO; } @@ -645,7 +677,7 @@ static int dmacontroller_setup(struct b43_dmaring *ring) u32 trans = ssb_dma_translation(ring->dev->dev); if (ring->tx) { - if (ring->dma64) { + if (ring->type == B43_DMA_64BIT) { u64 ringbase = (u64) (ring->dmabase); addrext = ((ringbase >> 32) & SSB_DMA_TRANSLATION_MASK) @@ -677,7 +709,7 @@ static int dmacontroller_setup(struct b43_dmaring *ring) err = alloc_initial_descbuffers(ring); if (err) goto out; - if (ring->dma64) { + if (ring->type == B43_DMA_64BIT) { u64 ringbase = (u64) (ring->dmabase); addrext = ((ringbase >> 32) & SSB_DMA_TRANSLATION_MASK) @@ -722,16 +754,16 @@ static void dmacontroller_cleanup(struct b43_dmaring *ring) { if (ring->tx) { b43_dmacontroller_tx_reset(ring->dev, ring->mmio_base, - ring->dma64); - if (ring->dma64) { + ring->type); + if (ring->type == B43_DMA_64BIT) { b43_dma_write(ring, B43_DMA64_TXRINGLO, 0); b43_dma_write(ring, B43_DMA64_TXRINGHI, 0); } else b43_dma_write(ring, B43_DMA32_TXRING, 0); } else { b43_dmacontroller_rx_reset(ring->dev, ring->mmio_base, - ring->dma64); - if (ring->dma64) { + ring->type); + if (ring->type == B43_DMA_64BIT) { b43_dma_write(ring, B43_DMA64_RXRINGLO, 0); b43_dma_write(ring, B43_DMA64_RXRINGHI, 0); } else @@ -786,7 +818,8 @@ static u64 supported_dma_mask(struct b43_wldev *dev) static struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev, int controller_index, - int for_tx, int dma64) + int for_tx, + enum b43_dmatype type) { struct b43_dmaring *ring; int err; @@ -796,6 +829,7 @@ struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev, ring = kzalloc(sizeof(*ring), GFP_KERNEL); if (!ring) goto out; + ring->type = type; nr_slots = B43_RXRING_SLOTS; if (for_tx) @@ -818,7 +852,7 @@ struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev, b43_txhdr_size(dev), DMA_TO_DEVICE); - if (dma_mapping_error(dma_test)) { + if (b43_dma_mapping_error(ring, dma_test, b43_txhdr_size(dev))) { /* ugh realloc */ kfree(ring->txhdr_cache); ring->txhdr_cache = kcalloc(nr_slots, @@ -832,7 +866,8 @@ struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev, b43_txhdr_size(dev), DMA_TO_DEVICE); - if (dma_mapping_error(dma_test)) + if (b43_dma_mapping_error(ring, dma_test, + b43_txhdr_size(dev))) goto err_kfree_txhdr_cache; } @@ -843,10 +878,9 @@ struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev, ring->dev = dev; ring->nr_slots = nr_slots; - ring->mmio_base = b43_dmacontroller_base(dma64, controller_index); + ring->mmio_base = b43_dmacontroller_base(type, controller_index); ring->index = controller_index; - ring->dma64 = !!dma64; - if (dma64) + if (type == B43_DMA_64BIT) ring->ops = &dma64_ops; else ring->ops = &dma32_ops; @@ -896,8 +930,8 @@ static void b43_destroy_dmaring(struct b43_dmaring *ring) if (!ring) return; - b43dbg(ring->dev->wl, "DMA-%s 0x%04X (%s) max used slots: %d/%d\n", - (ring->dma64) ? "64" : "32", + b43dbg(ring->dev->wl, "DMA-%u 0x%04X (%s) max used slots: %d/%d\n", + (unsigned int)(ring->type), ring->mmio_base, (ring->tx) ? "TX" : "RX", ring->max_used_slots, ring->nr_slots); /* Device IRQs are disabled prior entering this function, @@ -941,12 +975,22 @@ int b43_dma_init(struct b43_wldev *dev) struct b43_dmaring *ring; int err; u64 dmamask; - int dma64 = 0; + enum b43_dmatype type; dmamask = supported_dma_mask(dev); - if (dmamask == DMA_64BIT_MASK) - dma64 = 1; - + switch (dmamask) { + default: + B43_WARN_ON(1); + case DMA_30BIT_MASK: + type = B43_DMA_30BIT; + break; + case DMA_32BIT_MASK: + type = B43_DMA_32BIT; + break; + case DMA_64BIT_MASK: + type = B43_DMA_64BIT; + break; + } err = ssb_dma_set_mask(dev->dev, dmamask); if (err) { b43err(dev->wl, "The machine/kernel does not support " @@ -958,52 +1002,51 @@ int b43_dma_init(struct b43_wldev *dev) err = -ENOMEM; /* setup TX DMA channels. */ - ring = b43_setup_dmaring(dev, 0, 1, dma64); + ring = b43_setup_dmaring(dev, 0, 1, type); if (!ring) goto out; dma->tx_ring0 = ring; - ring = b43_setup_dmaring(dev, 1, 1, dma64); + ring = b43_setup_dmaring(dev, 1, 1, type); if (!ring) goto err_destroy_tx0; dma->tx_ring1 = ring; - ring = b43_setup_dmaring(dev, 2, 1, dma64); + ring = b43_setup_dmaring(dev, 2, 1, type); if (!ring) goto err_destroy_tx1; dma->tx_ring2 = ring; - ring = b43_setup_dmaring(dev, 3, 1, dma64); + ring = b43_setup_dmaring(dev, 3, 1, type); if (!ring) goto err_destroy_tx2; dma->tx_ring3 = ring; - ring = b43_setup_dmaring(dev, 4, 1, dma64); + ring = b43_setup_dmaring(dev, 4, 1, type); if (!ring) goto err_destroy_tx3; dma->tx_ring4 = ring; - ring = b43_setup_dmaring(dev, 5, 1, dma64); + ring = b43_setup_dmaring(dev, 5, 1, type); if (!ring) goto err_destroy_tx4; dma->tx_ring5 = ring; /* setup RX DMA channels. */ - ring = b43_setup_dmaring(dev, 0, 0, dma64); + ring = b43_setup_dmaring(dev, 0, 0, type); if (!ring) goto err_destroy_tx5; dma->rx_ring0 = ring; if (dev->dev->id.revision < 5) { - ring = b43_setup_dmaring(dev, 3, 0, dma64); + ring = b43_setup_dmaring(dev, 3, 0, type); if (!ring) goto err_destroy_rx0; dma->rx_ring3 = ring; } - b43dbg(dev->wl, "%d-bit DMA initialized\n", - (dmamask == DMA_64BIT_MASK) ? 64 : - (dmamask == DMA_32BIT_MASK) ? 32 : 30); + b43dbg(dev->wl, "%u-bit DMA initialized\n", + (unsigned int)type); err = 0; out: return err; @@ -1146,7 +1189,7 @@ static int dma_tx_fragment(struct b43_dmaring *ring, meta_hdr->dmaaddr = map_descbuffer(ring, (unsigned char *)header, hdrsize, 1); - if (dma_mapping_error(meta_hdr->dmaaddr)) { + if (b43_dma_mapping_error(ring, meta_hdr->dmaaddr, hdrsize)) { ring->current_slot = old_top_slot; ring->used_slots = old_used_slots; return -EIO; @@ -1165,7 +1208,7 @@ static int dma_tx_fragment(struct b43_dmaring *ring, meta->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1); /* create a bounce buffer in zone_dma on mapping failure. */ - if (dma_mapping_error(meta->dmaaddr)) { + if (b43_dma_mapping_error(ring, meta->dmaaddr, skb->len)) { bounce_skb = __dev_alloc_skb(skb->len, GFP_ATOMIC | GFP_DMA); if (!bounce_skb) { ring->current_slot = old_top_slot; @@ -1179,7 +1222,7 @@ static int dma_tx_fragment(struct b43_dmaring *ring, skb = bounce_skb; meta->skb = skb; meta->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1); - if (dma_mapping_error(meta->dmaaddr)) { + if (b43_dma_mapping_error(ring, meta->dmaaddr, skb->len)) { ring->current_slot = old_top_slot; ring->used_slots = old_used_slots; err = -EIO; diff --git a/drivers/net/wireless/b43/dma.h b/drivers/net/wireless/b43/dma.h index 58db03ac536..c0d6b69e650 100644 --- a/drivers/net/wireless/b43/dma.h +++ b/drivers/net/wireless/b43/dma.h @@ -203,6 +203,12 @@ struct b43_dma_ops { void (*set_current_rxslot) (struct b43_dmaring * ring, int slot); }; +enum b43_dmatype { + B43_DMA_30BIT = 30, + B43_DMA_32BIT = 32, + B43_DMA_64BIT = 64, +}; + struct b43_dmaring { /* Lowlevel DMA ops. */ const struct b43_dma_ops *ops; @@ -235,8 +241,8 @@ struct b43_dmaring { int index; /* Boolean. Is this a TX ring? */ bool tx; - /* Boolean. 64bit DMA if true, 32bit DMA otherwise. */ - bool dma64; + /* The type of DMA engine used. */ + enum b43_dmatype type; /* Boolean. Is this ring stopped at ieee80211 level? */ bool stopped; /* Lock, only used for TX. */ @@ -255,8 +261,7 @@ static inline u32 b43_dma_read(struct b43_dmaring *ring, u16 offset) return b43_read32(ring->dev, ring->mmio_base + offset); } -static inline - void b43_dma_write(struct b43_dmaring *ring, u16 offset, u32 value) +static inline void b43_dma_write(struct b43_dmaring *ring, u16 offset, u32 value) { b43_write32(ring->dev, ring->mmio_base + offset, value); } @@ -264,13 +269,6 @@ static inline int b43_dma_init(struct b43_wldev *dev); void b43_dma_free(struct b43_wldev *dev); -int b43_dmacontroller_rx_reset(struct b43_wldev *dev, - u16 dmacontroller_mmio_base, int dma64); -int b43_dmacontroller_tx_reset(struct b43_wldev *dev, - u16 dmacontroller_mmio_base, int dma64); - -u16 b43_dmacontroller_base(int dma64bit, int dmacontroller_idx); - void b43_dma_tx_suspend(struct b43_wldev *dev); void b43_dma_tx_resume(struct b43_wldev *dev); diff --git a/drivers/net/wireless/b43legacy/dma.c b/drivers/net/wireless/b43legacy/dma.c index 83161d9af81..6e08405e802 100644 --- a/drivers/net/wireless/b43legacy/dma.c +++ b/drivers/net/wireless/b43legacy/dma.c @@ -1164,7 +1164,7 @@ static int dma_tx_fragment(struct b43legacy_dmaring *ring, { const struct b43legacy_dma_ops *ops = ring->ops; u8 *header; - int slot; + int slot, old_top_slot, old_used_slots; int err; struct b43legacy_dmadesc_generic *desc; struct b43legacy_dmadesc_meta *meta; @@ -1174,6 +1174,9 @@ static int dma_tx_fragment(struct b43legacy_dmaring *ring, #define SLOTS_PER_PACKET 2 B43legacy_WARN_ON(skb_shinfo(skb)->nr_frags != 0); + old_top_slot = ring->current_slot; + old_used_slots = ring->used_slots; + /* Get a slot for the header. */ slot = request_slot(ring); desc = ops->idx2desc(ring, slot, &meta_hdr); @@ -1181,9 +1184,14 @@ static int dma_tx_fragment(struct b43legacy_dmaring *ring, header = &(ring->txhdr_cache[slot * sizeof( struct b43legacy_txhdr_fw3)]); - b43legacy_generate_txhdr(ring->dev, header, + err = b43legacy_generate_txhdr(ring->dev, header, skb->data, skb->len, ctl, generate_cookie(ring, slot)); + if (unlikely(err)) { + ring->current_slot = old_top_slot; + ring->used_slots = old_used_slots; + return err; + } meta_hdr->dmaaddr = map_descbuffer(ring, (unsigned char *)header, sizeof(struct b43legacy_txhdr_fw3), 1); @@ -1206,6 +1214,8 @@ static int dma_tx_fragment(struct b43legacy_dmaring *ring, if (dma_mapping_error(meta->dmaaddr)) { bounce_skb = __dev_alloc_skb(skb->len, GFP_ATOMIC | GFP_DMA); if (!bounce_skb) { + ring->current_slot = old_top_slot; + ring->used_slots = old_used_slots; err = -ENOMEM; goto out_unmap_hdr; } @@ -1216,6 +1226,8 @@ static int dma_tx_fragment(struct b43legacy_dmaring *ring, meta->skb = skb; meta->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1); if (dma_mapping_error(meta->dmaaddr)) { + ring->current_slot = old_top_slot; + ring->used_slots = old_used_slots; err = -EIO; goto out_free_bounce; } @@ -1282,6 +1294,13 @@ int b43legacy_dma_tx(struct b43legacy_wldev *dev, B43legacy_BUG_ON(ring->stopped); err = dma_tx_fragment(ring, skb, ctl); + if (unlikely(err == -ENOKEY)) { + /* Drop this packet, as we don't have the encryption key + * anymore and must not transmit it unencrypted. */ + dev_kfree_skb_any(skb); + err = 0; + goto out_unlock; + } if (unlikely(err)) { b43legacyerr(dev->wl, "DMA tx mapping failure\n"); goto out_unlock; diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c index aa20d5d56e2..53f7f2e9761 100644 --- a/drivers/net/wireless/b43legacy/main.c +++ b/drivers/net/wireless/b43legacy/main.c @@ -3160,8 +3160,6 @@ static int b43legacy_wireless_core_init(struct b43legacy_wldev *dev) b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0414, 0x01F4); ssb_bus_powerup(bus, 1); /* Enable dynamic PCTL */ - memset(wl->bssid, 0, ETH_ALEN); - memset(wl->mac_addr, 0, ETH_ALEN); b43legacy_upload_card_macaddress(dev); b43legacy_security_init(dev); b43legacy_rng_init(wl); @@ -3263,6 +3261,13 @@ static int b43legacy_op_start(struct ieee80211_hw *hw) * LEDs that are registered later depend on it. */ b43legacy_rfkill_init(dev); + /* Kill all old instance specific information to make sure + * the card won't use it in the short timeframe between start + * and mac80211 reconfiguring it. */ + memset(wl->bssid, 0, ETH_ALEN); + memset(wl->mac_addr, 0, ETH_ALEN); + wl->filter_flags = 0; + mutex_lock(&wl->mutex); if (b43legacy_status(dev) < B43legacy_STAT_INITIALIZED) { diff --git a/drivers/net/wireless/b43legacy/pio.c b/drivers/net/wireless/b43legacy/pio.c index e4f4c5c39e3..bcdd54eb2ed 100644 --- a/drivers/net/wireless/b43legacy/pio.c +++ b/drivers/net/wireless/b43legacy/pio.c @@ -181,7 +181,7 @@ union txhdr_union { struct b43legacy_txhdr_fw3 txhdr_fw3; }; -static void pio_tx_write_fragment(struct b43legacy_pioqueue *queue, +static int pio_tx_write_fragment(struct b43legacy_pioqueue *queue, struct sk_buff *skb, struct b43legacy_pio_txpacket *packet, size_t txhdr_size) @@ -189,14 +189,17 @@ static void pio_tx_write_fragment(struct b43legacy_pioqueue *queue, union txhdr_union txhdr_data; u8 *txhdr = NULL; unsigned int octets; + int err; txhdr = (u8 *)(&txhdr_data.txhdr_fw3); B43legacy_WARN_ON(skb_shinfo(skb)->nr_frags != 0); - b43legacy_generate_txhdr(queue->dev, + err = b43legacy_generate_txhdr(queue->dev, txhdr, skb->data, skb->len, &packet->txstat.control, generate_cookie(queue, packet)); + if (err) + return err; tx_start(queue); octets = skb->len + txhdr_size; @@ -204,6 +207,8 @@ static void pio_tx_write_fragment(struct b43legacy_pioqueue *queue, octets--; tx_data(queue, txhdr, (u8 *)skb->data, octets); tx_complete(queue, skb); + + return 0; } static void free_txpacket(struct b43legacy_pio_txpacket *packet, @@ -226,6 +231,7 @@ static int pio_tx_packet(struct b43legacy_pio_txpacket *packet) struct b43legacy_pioqueue *queue = packet->queue; struct sk_buff *skb = packet->skb; u16 octets; + int err; octets = (u16)skb->len + sizeof(struct b43legacy_txhdr_fw3); if (queue->tx_devq_size < octets) { @@ -247,8 +253,14 @@ static int pio_tx_packet(struct b43legacy_pio_txpacket *packet) if (queue->tx_devq_used + octets > queue->tx_devq_size) return -EBUSY; /* Now poke the device. */ - pio_tx_write_fragment(queue, skb, packet, + err = pio_tx_write_fragment(queue, skb, packet, sizeof(struct b43legacy_txhdr_fw3)); + if (unlikely(err == -ENOKEY)) { + /* Drop this packet, as we don't have the encryption key + * anymore and must not transmit it unencrypted. */ + free_txpacket(packet, 1); + return 0; + } /* Account for the packet size. * (We must not overflow the device TX queue) @@ -486,6 +498,9 @@ void b43legacy_pio_handle_txstatus(struct b43legacy_wldev *dev, queue = parse_cookie(dev, status->cookie, &packet); B43legacy_WARN_ON(!queue); + if (!packet->skb) + return; + queue->tx_devq_packets--; queue->tx_devq_used -= (packet->skb->len + sizeof(struct b43legacy_txhdr_fw3)); diff --git a/drivers/net/wireless/b43legacy/xmit.c b/drivers/net/wireless/b43legacy/xmit.c index e20c552442d..d84408a82db 100644 --- a/drivers/net/wireless/b43legacy/xmit.c +++ b/drivers/net/wireless/b43legacy/xmit.c @@ -181,7 +181,7 @@ static u8 b43legacy_calc_fallback_rate(u8 bitrate) return 0; } -static void generate_txhdr_fw3(struct b43legacy_wldev *dev, +static int generate_txhdr_fw3(struct b43legacy_wldev *dev, struct b43legacy_txhdr_fw3 *txhdr, const unsigned char *fragment_data, unsigned int fragment_len, @@ -252,6 +252,13 @@ static void generate_txhdr_fw3(struct b43legacy_wldev *dev, iv_len = min((size_t)txctl->iv_len, ARRAY_SIZE(txhdr->iv)); memcpy(txhdr->iv, ((u8 *)wlhdr) + wlhdr_len, iv_len); + } else { + /* This key is invalid. This might only happen + * in a short timeframe after machine resume before + * we were able to reconfigure keys. + * Drop this packet completely. Do not transmit it + * unencrypted to avoid leaking information. */ + return -ENOKEY; } } b43legacy_generate_plcp_hdr((struct b43legacy_plcp_hdr4 *) @@ -345,16 +352,18 @@ static void generate_txhdr_fw3(struct b43legacy_wldev *dev, /* Apply the bitfields */ txhdr->mac_ctl = cpu_to_le32(mac_ctl); txhdr->phy_ctl = cpu_to_le16(phy_ctl); + + return 0; } -void b43legacy_generate_txhdr(struct b43legacy_wldev *dev, +int b43legacy_generate_txhdr(struct b43legacy_wldev *dev, u8 *txhdr, const unsigned char *fragment_data, unsigned int fragment_len, const struct ieee80211_tx_control *txctl, u16 cookie) { - generate_txhdr_fw3(dev, (struct b43legacy_txhdr_fw3 *)txhdr, + return generate_txhdr_fw3(dev, (struct b43legacy_txhdr_fw3 *)txhdr, fragment_data, fragment_len, txctl, cookie); } diff --git a/drivers/net/wireless/b43legacy/xmit.h b/drivers/net/wireless/b43legacy/xmit.h index 8a155d0a5d1..bab47928a0c 100644 --- a/drivers/net/wireless/b43legacy/xmit.h +++ b/drivers/net/wireless/b43legacy/xmit.h @@ -76,7 +76,7 @@ struct b43legacy_txhdr_fw3 { -void b43legacy_generate_txhdr(struct b43legacy_wldev *dev, +int b43legacy_generate_txhdr(struct b43legacy_wldev *dev, u8 *txhdr, const unsigned char *fragment_data, unsigned int fragment_len, diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index f55c75712b5..5ee1ad69898 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -4207,13 +4207,13 @@ static u8 ratio2dB[100] = { * Conversion assumes that levels are voltages (20*log), not powers (10*log). */ int iwl3945_calc_db_from_ratio(int sig_ratio) { - /* Anything above 1000:1 just report as 60 dB */ - if (sig_ratio > 1000) + /* 1000:1 or higher just report as 60 dB */ + if (sig_ratio >= 1000) return 60; - /* Above 100:1, divide by 10 and use table, + /* 100:1 or higher, divide by 10 and use table, * add 20 dB to make up for divide by 10 */ - if (sig_ratio > 100) + if (sig_ratio >= 100) return (20 + (int)ratio2dB[sig_ratio/10]); /* We shouldn't see this */ diff --git a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c index e9743d3efaf..238628d3a85 100644 --- a/drivers/parport/parport_pc.c +++ b/drivers/parport/parport_pc.c @@ -1540,6 +1540,38 @@ static void __devinit detect_and_report_smsc (void) smsc_check(0x3f0,0x44); smsc_check(0x370,0x44); } + +static void __devinit detect_and_report_it87(void) +{ + u16 dev; + u8 r; + if (verbose_probing) + printk(KERN_DEBUG "IT8705 Super-IO detection, now testing port 2E ...\n"); + if (!request_region(0x2e, 1, __FUNCTION__)) + return; + outb(0x87, 0x2e); + outb(0x01, 0x2e); + outb(0x55, 0x2e); + outb(0x55, 0x2e); + outb(0x20, 0x2e); + dev = inb(0x2f) << 8; + outb(0x21, 0x2e); + dev |= inb(0x2f); + if (dev == 0x8712 || dev == 0x8705 || dev == 0x8715 || + dev == 0x8716 || dev == 0x8718 || dev == 0x8726) { + printk(KERN_INFO "IT%04X SuperIO detected.\n", dev); + outb(0x07, 0x2E); /* Parallel Port */ + outb(0x03, 0x2F); + outb(0xF0, 0x2E); /* BOOT 0x80 off */ + r = inb(0x2f); + outb(0xF0, 0x2E); + outb(r | 8, 0x2F); + outb(0x02, 0x2E); /* Lock */ + outb(0x02, 0x2F); + + release_region(0x2e, 1); + } +} #endif /* CONFIG_PARPORT_PC_SUPERIO */ static int get_superio_dma (struct parport *p) @@ -2767,6 +2799,7 @@ enum parport_pc_pci_cards { netmos_9755, netmos_9805, netmos_9815, + quatech_sppxp100, }; @@ -2843,6 +2876,7 @@ static struct parport_pc_pci { /* netmos_9755 */ { 2, { { 0, 1 }, { 2, 3 },} }, /* untested */ /* netmos_9805 */ { 1, { { 0, -1 }, } }, /* untested */ /* netmos_9815 */ { 2, { { 0, -1 }, { 2, -1 }, } }, /* untested */ + /* quatech_sppxp100 */ { 1, { { 0, 1 }, } }, }; static const struct pci_device_id parport_pc_pci_tbl[] = { @@ -2926,6 +2960,9 @@ static const struct pci_device_id parport_pc_pci_tbl[] = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9805 }, { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9815, PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9815 }, + /* Quatech SPPXP-100 Parallel port PCI ExpressCard */ + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_SPPXP_100, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, quatech_sppxp100 }, { 0, } /* terminate list */ }; MODULE_DEVICE_TABLE(pci,parport_pc_pci_tbl); @@ -3159,24 +3196,25 @@ static void __init parport_pc_find_ports (int autoirq, int autodma) int count = 0, err; #ifdef CONFIG_PARPORT_PC_SUPERIO - detect_and_report_winbond (); - detect_and_report_smsc (); + detect_and_report_it87(); + detect_and_report_winbond(); + detect_and_report_smsc(); #endif /* Onboard SuperIO chipsets that show themselves on the PCI bus. */ - count += parport_pc_init_superio (autoirq, autodma); + count += parport_pc_init_superio(autoirq, autodma); /* PnP ports, skip detection if SuperIO already found them */ if (!count) { - err = pnp_register_driver (&parport_pc_pnp_driver); + err = pnp_register_driver(&parport_pc_pnp_driver); if (!err) pnp_registered_parport = 1; } /* ISA ports and whatever (see asm/parport.h). */ - parport_pc_find_nonpci_ports (autoirq, autodma); + parport_pc_find_nonpci_ports(autoirq, autodma); - err = pci_register_driver (&parport_pc_pci_driver); + err = pci_register_driver(&parport_pc_pci_driver); if (!err) pci_registered_parport = 1; } diff --git a/drivers/parport/parport_serial.c b/drivers/parport/parport_serial.c index bd6ad8b3816..e2e95b36a60 100644 --- a/drivers/parport/parport_serial.c +++ b/drivers/parport/parport_serial.c @@ -77,7 +77,7 @@ static struct parport_pc_pci cards[] __devinitdata = { /* titan_110l */ { 1, { { 3, -1 }, } }, /* titan_210l */ { 1, { { 3, -1 }, } }, /* netmos_9xx5_combo */ { 1, { { 2, -1 }, }, netmos_parallel_init }, - /* netmos_9855 */ { 1, { { 0, -1 }, }, netmos_parallel_init }, + /* netmos_9855 */ { 1, { { 2, -1 }, }, netmos_parallel_init }, /* avlab_1s1p */ { 1, { { 1, 2}, } }, /* avlab_1s2p */ { 2, { { 1, 2}, { 3, 4 },} }, /* avlab_2s1p */ { 1, { { 2, 3}, } }, @@ -185,7 +185,7 @@ static struct pciserial_board pci_parport_serial_boards[] __devinitdata = { .uart_offset = 8, }, [netmos_9855] = { - .flags = FL_BASE2 | FL_BASE_BARS, + .flags = FL_BASE4 | FL_BASE_BARS, .num_ports = 1, .base_baud = 115200, .uart_offset = 8, diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c index 91b2dc956be..8ed26480371 100644 --- a/drivers/pci/dmar.c +++ b/drivers/pci/dmar.c @@ -26,6 +26,7 @@ #include <linux/pci.h> #include <linux/dmar.h> #include "iova.h" +#include "intel-iommu.h" #undef PREFIX #define PREFIX "DMAR:" diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 4e01df99681..31fa6c92aa5 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -1088,7 +1088,7 @@ static void dmar_init_reserved_ranges(void) int i; u64 addr, size; - init_iova_domain(&reserved_iova_list); + init_iova_domain(&reserved_iova_list, DMA_32BIT_PFN); /* IOAPIC ranges shouldn't be accessed by DMA */ iova = reserve_iova(&reserved_iova_list, IOVA_PFN(IOAPIC_RANGE_START), @@ -1142,7 +1142,7 @@ static int domain_init(struct dmar_domain *domain, int guest_width) int adjust_width, agaw; unsigned long sagaw; - init_iova_domain(&domain->iovad); + init_iova_domain(&domain->iovad, DMA_32BIT_PFN); spin_lock_init(&domain->mapping_lock); domain_reserve_special_ranges(domain); diff --git a/drivers/pci/intel-iommu.h b/drivers/pci/intel-iommu.h index 459ad1f9dc5..0e4862675ad 100644 --- a/drivers/pci/intel-iommu.h +++ b/drivers/pci/intel-iommu.h @@ -23,10 +23,24 @@ #include <linux/types.h> #include <linux/msi.h> +#include <linux/sysdev.h> #include "iova.h" #include <linux/io.h> /* + * We need a fixed PAGE_SIZE of 4K irrespective of + * arch PAGE_SIZE for IOMMU page tables. + */ +#define PAGE_SHIFT_4K (12) +#define PAGE_SIZE_4K (1UL << PAGE_SHIFT_4K) +#define PAGE_MASK_4K (((u64)-1) << PAGE_SHIFT_4K) +#define PAGE_ALIGN_4K(addr) (((addr) + PAGE_SIZE_4K - 1) & PAGE_MASK_4K) + +#define IOVA_PFN(addr) ((addr) >> PAGE_SHIFT_4K) +#define DMA_32BIT_PFN IOVA_PFN(DMA_32BIT_MASK) +#define DMA_64BIT_PFN IOVA_PFN(DMA_64BIT_MASK) + +/* * Intel IOMMU register specification per version 1.0 public spec. */ diff --git a/drivers/pci/iova.c b/drivers/pci/iova.c index a84571c2936..8de7ab6c6d0 100644 --- a/drivers/pci/iova.c +++ b/drivers/pci/iova.c @@ -9,19 +9,19 @@ #include "iova.h" void -init_iova_domain(struct iova_domain *iovad) +init_iova_domain(struct iova_domain *iovad, unsigned long pfn_32bit) { spin_lock_init(&iovad->iova_alloc_lock); spin_lock_init(&iovad->iova_rbtree_lock); iovad->rbroot = RB_ROOT; iovad->cached32_node = NULL; - + iovad->dma_32bit_pfn = pfn_32bit; } static struct rb_node * __get_cached_rbnode(struct iova_domain *iovad, unsigned long *limit_pfn) { - if ((*limit_pfn != DMA_32BIT_PFN) || + if ((*limit_pfn != iovad->dma_32bit_pfn) || (iovad->cached32_node == NULL)) return rb_last(&iovad->rbroot); else { @@ -37,7 +37,7 @@ static void __cached_rbnode_insert_update(struct iova_domain *iovad, unsigned long limit_pfn, struct iova *new) { - if (limit_pfn != DMA_32BIT_PFN) + if (limit_pfn != iovad->dma_32bit_pfn) return; iovad->cached32_node = &new->node; } diff --git a/drivers/pci/iova.h b/drivers/pci/iova.h index ae3028d5a94..d521b5b7319 100644 --- a/drivers/pci/iova.h +++ b/drivers/pci/iova.h @@ -15,22 +15,9 @@ #include <linux/rbtree.h> #include <linux/dma-mapping.h> -/* - * We need a fixed PAGE_SIZE of 4K irrespective of - * arch PAGE_SIZE for IOMMU page tables. - */ -#define PAGE_SHIFT_4K (12) -#define PAGE_SIZE_4K (1UL << PAGE_SHIFT_4K) -#define PAGE_MASK_4K (((u64)-1) << PAGE_SHIFT_4K) -#define PAGE_ALIGN_4K(addr) (((addr) + PAGE_SIZE_4K - 1) & PAGE_MASK_4K) - /* IO virtual address start page frame number */ #define IOVA_START_PFN (1) -#define IOVA_PFN(addr) ((addr) >> PAGE_SHIFT_4K) -#define DMA_32BIT_PFN IOVA_PFN(DMA_32BIT_MASK) -#define DMA_64BIT_PFN IOVA_PFN(DMA_64BIT_MASK) - /* iova structure */ struct iova { struct rb_node node; @@ -44,6 +31,7 @@ struct iova_domain { spinlock_t iova_rbtree_lock; /* Lock to protect update of rbtree */ struct rb_root rbroot; /* iova domain rbtree root */ struct rb_node *cached32_node; /* Save last alloced node */ + unsigned long dma_32bit_pfn; }; struct iova *alloc_iova_mem(void); @@ -56,7 +44,7 @@ struct iova *alloc_iova(struct iova_domain *iovad, unsigned long size, struct iova *reserve_iova(struct iova_domain *iovad, unsigned long pfn_lo, unsigned long pfn_hi); void copy_reserved_iova(struct iova_domain *from, struct iova_domain *to); -void init_iova_domain(struct iova_domain *iovad); +void init_iova_domain(struct iova_domain *iovad, unsigned long pfn_32bit); struct iova *find_iova(struct iova_domain *iovad, unsigned long pfn); void put_iova_domain(struct iova_domain *iovad); diff --git a/drivers/pnp/driver.c b/drivers/pnp/driver.c index a262762c5b8..12a1645a2e4 100644 --- a/drivers/pnp/driver.c +++ b/drivers/pnp/driver.c @@ -161,8 +161,7 @@ static int pnp_bus_suspend(struct device *dev, pm_message_t state) return error; } - if (!(pnp_drv->flags & PNP_DRIVER_RES_DO_NOT_CHANGE) && - pnp_can_disable(pnp_dev)) { + if (pnp_can_disable(pnp_dev)) { error = pnp_stop_dev(pnp_dev); if (error) return error; @@ -185,14 +184,17 @@ static int pnp_bus_resume(struct device *dev) if (pnp_dev->protocol && pnp_dev->protocol->resume) pnp_dev->protocol->resume(pnp_dev); - if (!(pnp_drv->flags & PNP_DRIVER_RES_DO_NOT_CHANGE)) { + if (pnp_can_write(pnp_dev)) { error = pnp_start_dev(pnp_dev); if (error) return error; } - if (pnp_drv->resume) - return pnp_drv->resume(pnp_dev); + if (pnp_drv->resume) { + error = pnp_drv->resume(pnp_dev); + if (error) + return error; + } return 0; } diff --git a/drivers/pnp/interface.c b/drivers/pnp/interface.c index 31548044fdd..982658477a5 100644 --- a/drivers/pnp/interface.c +++ b/drivers/pnp/interface.c @@ -10,9 +10,12 @@ #include <linux/errno.h> #include <linux/list.h> #include <linux/types.h> +#include <linux/pnp.h> #include <linux/stat.h> #include <linux/ctype.h> #include <linux/slab.h> +#include <linux/mutex.h> + #include <asm/uaccess.h> #include "base.h" @@ -315,8 +318,6 @@ static ssize_t pnp_show_current_resources(struct device *dmdev, return ret; } -extern struct semaphore pnp_res_mutex; - static ssize_t pnp_set_current_resources(struct device *dmdev, struct device_attribute *attr, const char *ubuf, size_t count) @@ -361,10 +362,10 @@ pnp_set_current_resources(struct device *dmdev, struct device_attribute *attr, goto done; } if (!strnicmp(buf, "get", 3)) { - down(&pnp_res_mutex); + mutex_lock(&pnp_res_mutex); if (pnp_can_read(dev)) dev->protocol->get(dev, &dev->res); - up(&pnp_res_mutex); + mutex_unlock(&pnp_res_mutex); goto done; } if (!strnicmp(buf, "set", 3)) { @@ -373,7 +374,7 @@ pnp_set_current_resources(struct device *dmdev, struct device_attribute *attr, goto done; buf += 3; pnp_init_resource_table(&dev->res); - down(&pnp_res_mutex); + mutex_lock(&pnp_res_mutex); while (1) { while (isspace(*buf)) ++buf; @@ -455,7 +456,7 @@ pnp_set_current_resources(struct device *dmdev, struct device_attribute *attr, } break; } - up(&pnp_res_mutex); + mutex_unlock(&pnp_res_mutex); goto done; } diff --git a/drivers/pnp/manager.c b/drivers/pnp/manager.c index c6b3d4e63cc..c28caf272c1 100644 --- a/drivers/pnp/manager.c +++ b/drivers/pnp/manager.c @@ -12,9 +12,10 @@ #include <linux/pnp.h> #include <linux/slab.h> #include <linux/bitmap.h> +#include <linux/mutex.h> #include "base.h" -DECLARE_MUTEX(pnp_res_mutex); +DEFINE_MUTEX(pnp_res_mutex); static int pnp_assign_port(struct pnp_dev *dev, struct pnp_port *rule, int idx) { @@ -297,7 +298,7 @@ static int pnp_assign_resources(struct pnp_dev *dev, int depnum) if (!pnp_can_configure(dev)) return -ENODEV; - down(&pnp_res_mutex); + mutex_lock(&pnp_res_mutex); pnp_clean_resource_table(&dev->res); /* start with a fresh slate */ if (dev->independent) { port = dev->independent->port; @@ -366,12 +367,12 @@ static int pnp_assign_resources(struct pnp_dev *dev, int depnum) } else if (dev->dependent) goto fail; - up(&pnp_res_mutex); + mutex_unlock(&pnp_res_mutex); return 1; fail: pnp_clean_resource_table(&dev->res); - up(&pnp_res_mutex); + mutex_unlock(&pnp_res_mutex); return 0; } @@ -396,7 +397,7 @@ int pnp_manual_config_dev(struct pnp_dev *dev, struct pnp_resource_table *res, return -ENOMEM; *bak = dev->res; - down(&pnp_res_mutex); + mutex_lock(&pnp_res_mutex); dev->res = *res; if (!(mode & PNP_CONFIG_FORCE)) { for (i = 0; i < PNP_MAX_PORT; i++) { @@ -416,14 +417,14 @@ int pnp_manual_config_dev(struct pnp_dev *dev, struct pnp_resource_table *res, goto fail; } } - up(&pnp_res_mutex); + mutex_unlock(&pnp_res_mutex); kfree(bak); return 0; fail: dev->res = *bak; - up(&pnp_res_mutex); + mutex_unlock(&pnp_res_mutex); kfree(bak); return -EINVAL; } @@ -513,7 +514,7 @@ int pnp_activate_dev(struct pnp_dev *dev) int error; if (dev->active) - return 0; /* the device is already active */ + return 0; /* ensure resources are allocated */ if (pnp_auto_config_dev(dev)) @@ -524,7 +525,7 @@ int pnp_activate_dev(struct pnp_dev *dev) return error; dev->active = 1; - return 1; + return 0; } /** @@ -538,7 +539,7 @@ int pnp_disable_dev(struct pnp_dev *dev) int error; if (!dev->active) - return 0; /* the device is already disabled */ + return 0; error = pnp_stop_dev(dev); if (error) @@ -547,11 +548,11 @@ int pnp_disable_dev(struct pnp_dev *dev) dev->active = 0; /* release the resources so that other devices can use them */ - down(&pnp_res_mutex); + mutex_lock(&pnp_res_mutex); pnp_clean_resource_table(&dev->res); - up(&pnp_res_mutex); + mutex_unlock(&pnp_res_mutex); - return 1; + return 0; } /** diff --git a/drivers/pnp/pnpacpi/rsparser.c b/drivers/pnp/pnpacpi/rsparser.c index 6b9840cce0f..6aa231ef642 100644 --- a/drivers/pnp/pnpacpi/rsparser.c +++ b/drivers/pnp/pnpacpi/rsparser.c @@ -391,8 +391,8 @@ acpi_status pnpacpi_parse_allocated_resource(acpi_handle handle, pnpacpi_allocated_resource, res); } -static void pnpacpi_parse_dma_option(struct pnp_option *option, - struct acpi_resource_dma *p) +static __init void pnpacpi_parse_dma_option(struct pnp_option *option, + struct acpi_resource_dma *p) { int i; struct pnp_dma *dma; @@ -411,8 +411,8 @@ static void pnpacpi_parse_dma_option(struct pnp_option *option, pnp_register_dma_resource(option, dma); } -static void pnpacpi_parse_irq_option(struct pnp_option *option, - struct acpi_resource_irq *p) +static __init void pnpacpi_parse_irq_option(struct pnp_option *option, + struct acpi_resource_irq *p) { int i; struct pnp_irq *irq; @@ -431,8 +431,8 @@ static void pnpacpi_parse_irq_option(struct pnp_option *option, pnp_register_irq_resource(option, irq); } -static void pnpacpi_parse_ext_irq_option(struct pnp_option *option, - struct acpi_resource_extended_irq *p) +static __init void pnpacpi_parse_ext_irq_option(struct pnp_option *option, + struct acpi_resource_extended_irq *p) { int i; struct pnp_irq *irq; @@ -451,8 +451,8 @@ static void pnpacpi_parse_ext_irq_option(struct pnp_option *option, pnp_register_irq_resource(option, irq); } -static void pnpacpi_parse_port_option(struct pnp_option *option, - struct acpi_resource_io *io) +static __init void pnpacpi_parse_port_option(struct pnp_option *option, + struct acpi_resource_io *io) { struct pnp_port *port; @@ -470,8 +470,8 @@ static void pnpacpi_parse_port_option(struct pnp_option *option, pnp_register_port_resource(option, port); } -static void pnpacpi_parse_fixed_port_option(struct pnp_option *option, - struct acpi_resource_fixed_io *io) +static __init void pnpacpi_parse_fixed_port_option(struct pnp_option *option, + struct acpi_resource_fixed_io *io) { struct pnp_port *port; @@ -487,8 +487,8 @@ static void pnpacpi_parse_fixed_port_option(struct pnp_option *option, pnp_register_port_resource(option, port); } -static void pnpacpi_parse_mem24_option(struct pnp_option *option, - struct acpi_resource_memory24 *p) +static __init void pnpacpi_parse_mem24_option(struct pnp_option *option, + struct acpi_resource_memory24 *p) { struct pnp_mem *mem; @@ -508,8 +508,8 @@ static void pnpacpi_parse_mem24_option(struct pnp_option *option, pnp_register_mem_resource(option, mem); } -static void pnpacpi_parse_mem32_option(struct pnp_option *option, - struct acpi_resource_memory32 *p) +static __init void pnpacpi_parse_mem32_option(struct pnp_option *option, + struct acpi_resource_memory32 *p) { struct pnp_mem *mem; @@ -529,8 +529,8 @@ static void pnpacpi_parse_mem32_option(struct pnp_option *option, pnp_register_mem_resource(option, mem); } -static void pnpacpi_parse_fixed_mem32_option(struct pnp_option *option, - struct acpi_resource_fixed_memory32 *p) +static __init void pnpacpi_parse_fixed_mem32_option(struct pnp_option *option, + struct acpi_resource_fixed_memory32 *p) { struct pnp_mem *mem; @@ -549,8 +549,8 @@ static void pnpacpi_parse_fixed_mem32_option(struct pnp_option *option, pnp_register_mem_resource(option, mem); } -static void pnpacpi_parse_address_option(struct pnp_option *option, - struct acpi_resource *r) +static __init void pnpacpi_parse_address_option(struct pnp_option *option, + struct acpi_resource *r) { struct acpi_resource_address64 addr, *p = &addr; acpi_status status; @@ -596,8 +596,8 @@ struct acpipnp_parse_option_s { struct pnp_dev *dev; }; -static acpi_status pnpacpi_option_resource(struct acpi_resource *res, - void *data) +static __init acpi_status pnpacpi_option_resource(struct acpi_resource *res, + void *data) { int priority = 0; struct acpipnp_parse_option_s *parse_data = data; @@ -696,8 +696,8 @@ static acpi_status pnpacpi_option_resource(struct acpi_resource *res, return AE_OK; } -acpi_status pnpacpi_parse_resource_option_data(acpi_handle handle, - struct pnp_dev * dev) +acpi_status __init pnpacpi_parse_resource_option_data(acpi_handle handle, + struct pnp_dev *dev) { acpi_status status; struct acpipnp_parse_option_s parse_data; diff --git a/drivers/pnp/pnpbios/core.c b/drivers/pnp/pnpbios/core.c index e33e03f7108..f7e67197a56 100644 --- a/drivers/pnp/pnpbios/core.c +++ b/drivers/pnp/pnpbios/core.c @@ -315,7 +315,7 @@ struct pnp_protocol pnpbios_protocol = { .disable = pnpbios_disable_resources, }; -static int insert_device(struct pnp_bios_node *node) +static int __init insert_device(struct pnp_bios_node *node) { struct list_head *pos; struct pnp_dev *dev; diff --git a/drivers/pnp/pnpbios/rsparser.c b/drivers/pnp/pnpbios/rsparser.c index 3fabf11b002..caade353141 100644 --- a/drivers/pnp/pnpbios/rsparser.c +++ b/drivers/pnp/pnpbios/rsparser.c @@ -262,8 +262,8 @@ len_err: * Resource Configuration Options */ -static void pnpbios_parse_mem_option(unsigned char *p, int size, - struct pnp_option *option) +static __init void pnpbios_parse_mem_option(unsigned char *p, int size, + struct pnp_option *option) { struct pnp_mem *mem; @@ -278,8 +278,8 @@ static void pnpbios_parse_mem_option(unsigned char *p, int size, pnp_register_mem_resource(option, mem); } -static void pnpbios_parse_mem32_option(unsigned char *p, int size, - struct pnp_option *option) +static __init void pnpbios_parse_mem32_option(unsigned char *p, int size, + struct pnp_option *option) { struct pnp_mem *mem; @@ -294,8 +294,8 @@ static void pnpbios_parse_mem32_option(unsigned char *p, int size, pnp_register_mem_resource(option, mem); } -static void pnpbios_parse_fixed_mem32_option(unsigned char *p, int size, - struct pnp_option *option) +static __init void pnpbios_parse_fixed_mem32_option(unsigned char *p, int size, + struct pnp_option *option) { struct pnp_mem *mem; @@ -309,7 +309,7 @@ static void pnpbios_parse_fixed_mem32_option(unsigned char *p, int size, pnp_register_mem_resource(option, mem); } -static void pnpbios_parse_irq_option(unsigned char *p, int size, +static __init void pnpbios_parse_irq_option(unsigned char *p, int size, struct pnp_option *option) { struct pnp_irq *irq; @@ -327,7 +327,7 @@ static void pnpbios_parse_irq_option(unsigned char *p, int size, pnp_register_irq_resource(option, irq); } -static void pnpbios_parse_dma_option(unsigned char *p, int size, +static __init void pnpbios_parse_dma_option(unsigned char *p, int size, struct pnp_option *option) { struct pnp_dma *dma; @@ -340,8 +340,8 @@ static void pnpbios_parse_dma_option(unsigned char *p, int size, pnp_register_dma_resource(option, dma); } -static void pnpbios_parse_port_option(unsigned char *p, int size, - struct pnp_option *option) +static __init void pnpbios_parse_port_option(unsigned char *p, int size, + struct pnp_option *option) { struct pnp_port *port; @@ -356,8 +356,8 @@ static void pnpbios_parse_port_option(unsigned char *p, int size, pnp_register_port_resource(option, port); } -static void pnpbios_parse_fixed_port_option(unsigned char *p, int size, - struct pnp_option *option) +static __init void pnpbios_parse_fixed_port_option(unsigned char *p, int size, + struct pnp_option *option) { struct pnp_port *port; @@ -371,9 +371,9 @@ static void pnpbios_parse_fixed_port_option(unsigned char *p, int size, pnp_register_port_resource(option, port); } -static unsigned char *pnpbios_parse_resource_option_data(unsigned char *p, - unsigned char *end, - struct pnp_dev *dev) +static __init unsigned char * +pnpbios_parse_resource_option_data(unsigned char *p, unsigned char *end, + struct pnp_dev *dev) { unsigned int len, tag; int priority = 0; @@ -781,7 +781,8 @@ len_err: * Core Parsing Functions */ -int pnpbios_parse_data_stream(struct pnp_dev *dev, struct pnp_bios_node *node) +int __init pnpbios_parse_data_stream(struct pnp_dev *dev, + struct pnp_bios_node *node) { unsigned char *p = (char *)node->data; unsigned char *end = (char *)(node->data + node->size); diff --git a/drivers/pnp/quirks.c b/drivers/pnp/quirks.c index e903b8c2b1f..4065139753b 100644 --- a/drivers/pnp/quirks.c +++ b/drivers/pnp/quirks.c @@ -17,6 +17,7 @@ #include <linux/slab.h> #include <linux/pnp.h> #include <linux/io.h> +#include <linux/dmi.h> #include <linux/kallsyms.h> #include "base.h" @@ -108,6 +109,46 @@ static void quirk_sb16audio_resources(struct pnp_dev *dev) "pnp: SB audio device quirk - increasing port range\n"); } +static void quirk_supermicro_h8dce_system(struct pnp_dev *dev) +{ + int i; + static struct dmi_system_id supermicro_h8dce[] = { + { + .ident = "Supermicro H8DCE", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Supermicro"), + DMI_MATCH(DMI_PRODUCT_NAME, "H8DCE"), + }, + }, + { } + }; + + if (!dmi_check_system(supermicro_h8dce)) + return; + + /* + * On the Supermicro H8DCE, there's a system device with resources + * that overlap BAR 6 of the built-in SATA PCI adapter. If the PNP + * system device claims them, the sata_nv driver won't be able to. + * More details at: + * https://bugzilla.redhat.com/show_bug.cgi?id=280641 + * https://bugzilla.redhat.com/show_bug.cgi?id=313491 + * http://lkml.org/lkml/2008/1/9/449 + * http://thread.gmane.org/gmane.linux.acpi.devel/27312 + */ + for (i = 0; i < PNP_MAX_MEM; i++) { + if (pnp_mem_valid(dev, i) && pnp_mem_len(dev, i) && + (pnp_mem_start(dev, i) & 0xdfef0000) == 0xdfef0000) { + dev_warn(&dev->dev, "disabling 0x%llx-0x%llx to prevent" + " conflict with sata_nv PCI device\n", + (unsigned long long) pnp_mem_start(dev, i), + (unsigned long long) (pnp_mem_start(dev, i) + + pnp_mem_len(dev, i) - 1)); + pnp_mem_flags(dev, i) = 0; + } + } +} + /* * PnP Quirks * Cards or devices that need some tweaking due to incomplete resource info @@ -128,6 +169,8 @@ static struct pnp_fixup pnp_fixups[] = { {"CTL0043", quirk_sb16audio_resources}, {"CTL0044", quirk_sb16audio_resources}, {"CTL0045", quirk_sb16audio_resources}, + {"PNP0c01", quirk_supermicro_h8dce_system}, + {"PNP0c02", quirk_supermicro_h8dce_system}, {""} }; diff --git a/drivers/ps3/ps3av.c b/drivers/ps3/ps3av.c index 87b3493d88e..6f2f90ebb02 100644 --- a/drivers/ps3/ps3av.c +++ b/drivers/ps3/ps3av.c @@ -78,23 +78,21 @@ static const struct avset_video_mode { u32 aspect; u32 x; u32 y; - u32 interlace; - u32 freq; } video_mode_table[] = { { 0, }, /* auto */ - {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_480I, A_N, 720, 480, 1, 60}, - {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_480P, A_N, 720, 480, 0, 60}, - {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_720P_60HZ, A_N, 1280, 720, 0, 60}, - {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080I_60HZ, A_W, 1920, 1080, 1, 60}, - {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080P_60HZ, A_W, 1920, 1080, 0, 60}, - {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_576I, A_N, 720, 576, 1, 50}, - {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_576P, A_N, 720, 576, 0, 50}, - {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_720P_50HZ, A_N, 1280, 720, 0, 50}, - {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080I_50HZ, A_W, 1920, 1080, 1, 50}, - {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080P_50HZ, A_W, 1920, 1080, 0, 50}, - { RGB8, XRGB, PS3AV_CMD_VIDEO_VID_WXGA, A_W, 1280, 768, 0, 60}, - { RGB8, XRGB, PS3AV_CMD_VIDEO_VID_SXGA, A_N, 1280, 1024, 0, 60}, - { RGB8, XRGB, PS3AV_CMD_VIDEO_VID_WUXGA, A_W, 1920, 1200, 0, 60}, + {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_480I, A_N, 720, 480}, + {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_480P, A_N, 720, 480}, + {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_720P_60HZ, A_N, 1280, 720}, + {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080I_60HZ, A_W, 1920, 1080}, + {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080P_60HZ, A_W, 1920, 1080}, + {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_576I, A_N, 720, 576}, + {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_576P, A_N, 720, 576}, + {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_720P_50HZ, A_N, 1280, 720}, + {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080I_50HZ, A_W, 1920, 1080}, + {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080P_50HZ, A_W, 1920, 1080}, + { RGB8, XRGB, PS3AV_CMD_VIDEO_VID_WXGA, A_W, 1280, 768}, + { RGB8, XRGB, PS3AV_CMD_VIDEO_VID_SXGA, A_N, 1280, 1024}, + { RGB8, XRGB, PS3AV_CMD_VIDEO_VID_WUXGA, A_W, 1920, 1200}, }; /* supported CIDs */ @@ -544,7 +542,7 @@ static void ps3av_set_videomode_packet(u32 id) static void ps3av_set_videomode_cont(u32 id, u32 old_id) { - static int vesa = 0; + static int vesa; int res; /* video signal off */ @@ -554,9 +552,9 @@ static void ps3av_set_videomode_cont(u32 id, u32 old_id) * AV backend needs non-VESA mode setting at least one time * when VESA mode is used. */ - if (vesa == 0 && (id & PS3AV_MODE_MASK) >= 11) { + if (vesa == 0 && (id & PS3AV_MODE_MASK) >= PS3AV_MODE_WXGA) { /* vesa mode */ - ps3av_set_videomode_packet(2); /* 480P */ + ps3av_set_videomode_packet(PS3AV_MODE_480P); } vesa = 1; @@ -596,20 +594,21 @@ static const struct { unsigned mask : 19; unsigned id : 4; } ps3av_preferred_modes[] = { - { .mask = PS3AV_RESBIT_WUXGA << SHIFT_VESA, .id = 13 }, - { .mask = PS3AV_RESBIT_1920x1080P << SHIFT_60, .id = 5 }, - { .mask = PS3AV_RESBIT_1920x1080P << SHIFT_50, .id = 10 }, - { .mask = PS3AV_RESBIT_1920x1080I << SHIFT_60, .id = 4 }, - { .mask = PS3AV_RESBIT_1920x1080I << SHIFT_50, .id = 9 }, - { .mask = PS3AV_RESBIT_SXGA << SHIFT_VESA, .id = 12 }, - { .mask = PS3AV_RESBIT_WXGA << SHIFT_VESA, .id = 11 }, - { .mask = PS3AV_RESBIT_1280x720P << SHIFT_60, .id = 3 }, - { .mask = PS3AV_RESBIT_1280x720P << SHIFT_50, .id = 8 }, - { .mask = PS3AV_RESBIT_720x480P << SHIFT_60, .id = 2 }, - { .mask = PS3AV_RESBIT_720x576P << SHIFT_50, .id = 7 }, + { PS3AV_RESBIT_WUXGA << SHIFT_VESA, PS3AV_MODE_WUXGA }, + { PS3AV_RESBIT_1920x1080P << SHIFT_60, PS3AV_MODE_1080P60 }, + { PS3AV_RESBIT_1920x1080P << SHIFT_50, PS3AV_MODE_1080P50 }, + { PS3AV_RESBIT_1920x1080I << SHIFT_60, PS3AV_MODE_1080I60 }, + { PS3AV_RESBIT_1920x1080I << SHIFT_50, PS3AV_MODE_1080I50 }, + { PS3AV_RESBIT_SXGA << SHIFT_VESA, PS3AV_MODE_SXGA }, + { PS3AV_RESBIT_WXGA << SHIFT_VESA, PS3AV_MODE_WXGA }, + { PS3AV_RESBIT_1280x720P << SHIFT_60, PS3AV_MODE_720P60 }, + { PS3AV_RESBIT_1280x720P << SHIFT_50, PS3AV_MODE_720P50 }, + { PS3AV_RESBIT_720x480P << SHIFT_60, PS3AV_MODE_480P }, + { PS3AV_RESBIT_720x576P << SHIFT_50, PS3AV_MODE_576P }, }; -static int ps3av_resbit2id(u32 res_50, u32 res_60, u32 res_vesa) +static enum ps3av_mode_num ps3av_resbit2id(u32 res_50, u32 res_60, + u32 res_vesa) { unsigned int i; u32 res_all; @@ -638,9 +637,9 @@ static int ps3av_resbit2id(u32 res_50, u32 res_60, u32 res_vesa) return 0; } -static int ps3av_hdmi_get_id(struct ps3av_info_monitor *info) +static enum ps3av_mode_num ps3av_hdmi_get_id(struct ps3av_info_monitor *info) { - int id; + enum ps3av_mode_num id; if (safe_mode) return PS3AV_DEFAULT_HDMI_MODE_ID_REG_60; @@ -854,7 +853,7 @@ int ps3av_set_video_mode(u32 id) /* auto mode */ option = id & ~PS3AV_MODE_MASK; - if ((id & PS3AV_MODE_MASK) == 0) { + if ((id & PS3AV_MODE_MASK) == PS3AV_MODE_AUTO) { id = ps3av_auto_videomode(&ps3av->av_hw_conf); if (id < 1) { printk(KERN_ERR "%s: invalid id :%d\n", __func__, id); @@ -889,36 +888,6 @@ int ps3av_get_mode(void) EXPORT_SYMBOL_GPL(ps3av_get_mode); -int ps3av_get_scanmode(int id) -{ - int size; - - id = id & PS3AV_MODE_MASK; - size = ARRAY_SIZE(video_mode_table); - if (id > size - 1 || id < 0) { - printk(KERN_ERR "%s: invalid mode %d\n", __func__, id); - return -EINVAL; - } - return video_mode_table[id].interlace; -} - -EXPORT_SYMBOL_GPL(ps3av_get_scanmode); - -int ps3av_get_refresh_rate(int id) -{ - int size; - - id = id & PS3AV_MODE_MASK; - size = ARRAY_SIZE(video_mode_table); - if (id > size - 1 || id < 0) { - printk(KERN_ERR "%s: invalid mode %d\n", __func__, id); - return -EINVAL; - } - return video_mode_table[id].freq; -} - -EXPORT_SYMBOL_GPL(ps3av_get_refresh_rate); - /* get resolution by video_mode */ int ps3av_video_mode2res(u32 id, u32 *xres, u32 *yres) { @@ -990,7 +959,7 @@ static int ps3av_probe(struct ps3_system_bus_device *dev) return -ENOMEM; mutex_init(&ps3av->mutex); - ps3av->ps3av_mode = 0; + ps3av->ps3av_mode = PS3AV_MODE_AUTO; ps3av->dev = dev; INIT_WORK(&ps3av->work, ps3avd); diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 45e4b964817..6402d699072 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -20,6 +20,10 @@ menuconfig RTC_CLASS if RTC_CLASS +if GEN_RTC || RTC +comment "Conflicting RTC option has been selected, check GEN_RTC and RTC" +endif + config RTC_HCTOSYS bool "Set system time from RTC on startup and resume" depends on RTC_CLASS = y @@ -49,7 +53,7 @@ config RTC_HCTOSYS_DEVICE If the clock you specify here is not battery backed, it may still be useful to reinitialize system time when resuming from system - sleep states. Do not specify an RTC here unless it stays powered + sleep states. Do not specify an RTC here unless it stays powered during all this system's supported sleep states. config RTC_DEBUG @@ -142,7 +146,7 @@ config RTC_DRV_DS1307 will be called rtc-ds1307. config RTC_DRV_DS1374 - tristate "Maxim/Dallas Semiconductor DS1374 Real Time Clock" + tristate "Dallas/Maxim DS1374" depends on RTC_CLASS && I2C help If you say yes here you get support for Dallas Semiconductor @@ -162,7 +166,7 @@ config RTC_DRV_DS1672 will be called rtc-ds1672. config RTC_DRV_MAX6900 - tristate "Maxim 6900" + tristate "Maxim MAX6900" help If you say yes here you will get support for the Maxim MAX6900 I2C RTC chip. @@ -180,10 +184,10 @@ config RTC_DRV_RS5C372 will be called rtc-rs5c372. config RTC_DRV_ISL1208 - tristate "Intersil 1208" + tristate "Intersil ISL1208" help If you say yes here you get support for the - Intersil 1208 RTC chip. + Intersil ISL1208 RTC chip. This driver can also be built as a module. If so, the module will be called rtc-isl1208. @@ -220,7 +224,7 @@ config RTC_DRV_PCF8583 will be called rtc-pcf8583. config RTC_DRV_M41T80 - tristate "ST M41T80 series RTC" + tristate "ST M41T80/81/82/83/84/85/87" help If you say Y here you will get support for the ST M41T80 RTC chips series. Currently following chips are @@ -252,23 +256,32 @@ comment "SPI RTC drivers" if SPI_MASTER -config RTC_DRV_RS5C348 - tristate "Ricoh RS5C348A/B" +config RTC_DRV_MAX6902 + tristate "Maxim MAX6902" help - If you say yes here you get support for the - Ricoh RS5C348A and RS5C348B RTC chips. + If you say yes here you will get support for the + Maxim MAX6902 SPI RTC chip. This driver can also be built as a module. If so, the module - will be called rtc-rs5c348. + will be called rtc-max6902. -config RTC_DRV_MAX6902 - tristate "Maxim 6902" +config RTC_DRV_R9701 + tristate "Epson RTC-9701JE" help If you say yes here you will get support for the - Maxim MAX6902 SPI RTC chip. + Epson RTC-9701JE SPI RTC chip. This driver can also be built as a module. If so, the module - will be called rtc-max6902. + will be called rtc-r9701. + +config RTC_DRV_RS5C348 + tristate "Ricoh RS5C348A/B" + help + If you say yes here you get support for the + Ricoh RS5C348A and RS5C348B RTC chips. + + This driver can also be built as a module. If so, the module + will be called rtc-rs5c348. endif # SPI_MASTER @@ -302,34 +315,50 @@ config RTC_DRV_DS1216 help If you say yes here you get support for the Dallas DS1216 RTC chips. -config RTC_DRV_DS1553 - tristate "Dallas DS1553" +config RTC_DRV_DS1302 + tristate "Dallas DS1302" + depends on SH_SECUREEDGE5410 + help + If you say yes here you get support for the Dallas DS1302 RTC chips. + +config RTC_DRV_DS1511 + tristate "Dallas DS1511" + depends on RTC_CLASS help If you say yes here you get support for the - Dallas DS1553 timekeeping chip. + Dallas DS1511 timekeeping/watchdog chip. This driver can also be built as a module. If so, the module - will be called rtc-ds1553. + will be called rtc-ds1511. -config RTC_DRV_STK17TA8 - tristate "Simtek STK17TA8" - depends on RTC_CLASS +config RTC_DRV_DS1553 + tristate "Maxim/Dallas DS1553" help If you say yes here you get support for the - Simtek STK17TA8 timekeeping chip. + Maxim/Dallas DS1553 timekeeping chip. This driver can also be built as a module. If so, the module - will be called rtc-stk17ta8. + will be called rtc-ds1553. config RTC_DRV_DS1742 - tristate "Dallas DS1742/1743" + tristate "Maxim/Dallas DS1742/1743" help If you say yes here you get support for the - Dallas DS1742/1743 timekeeping chip. + Maxim/Dallas DS1742/1743 timekeeping chip. This driver can also be built as a module. If so, the module will be called rtc-ds1742. +config RTC_DRV_STK17TA8 + tristate "Simtek STK17TA8" + depends on RTC_CLASS + help + If you say yes here you get support for the + Simtek STK17TA8 timekeeping chip. + + This driver can also be built as a module. If so, the module + will be called rtc-stk17ta8. + config RTC_DRV_M48T86 tristate "ST M48T86/Dallas DS12887" help @@ -440,10 +469,47 @@ config RTC_DRV_AT32AP700X AT32AP700x family processors. config RTC_DRV_AT91RM9200 - tristate "AT91RM9200" - depends on ARCH_AT91RM9200 - help - Driver for the Atmel AT91RM9200's internal RTC (Realtime Clock). + tristate "AT91RM9200 or AT91SAM9RL" + depends on ARCH_AT91RM9200 || ARCH_AT91SAM9RL + help + Driver for the internal RTC (Realtime Clock) module found on + Atmel AT91RM9200's and AT91SAM9RL chips. On SAM9RL chips + this is powered by the backup power supply. + +config RTC_DRV_AT91SAM9 + tristate "AT91SAM9x" + depends on ARCH_AT91 && !(ARCH_AT91RM9200 || ARCH_AT91X40) + help + RTC driver for the Atmel AT91SAM9x internal RTT (Real Time Timer). + These timers are powered by the backup power supply (such as a + small coin cell battery), but do not need to be used as RTCs. + + (On AT91SAM9rl chips you probably want to use the dedicated RTC + module and leave the RTT available for other uses.) + +config RTC_DRV_AT91SAM9_RTT + int + range 0 1 + default 0 + prompt "RTT module Number" if ARCH_AT91SAM9263 + depends on RTC_DRV_AT91SAM9 + help + More than one RTT module is available. You can choose which + one will be used as an RTC. The default of zero is normally + OK to use, though some systems use that for non-RTC purposes. + +config RTC_DRV_AT91SAM9_GPBR + int + range 0 3 if !ARCH_AT91SAM9263 + range 0 15 if ARCH_AT91SAM9263 + default 0 + prompt "Backup Register Number" + depends on RTC_DRV_AT91SAM9 + help + The RTC driver needs to use one of the General Purpose Backup + Registers (GPBRs) as well as the RTT. You can choose which one + will be used. The default of zero is normally OK to use, but + on some systems other software needs to use that register. config RTC_DRV_BFIN tristate "Blackfin On-Chip RTC" diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 465db4dd50b..ec703f34ab8 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -19,11 +19,14 @@ rtc-core-$(CONFIG_RTC_INTF_SYSFS) += rtc-sysfs.o obj-$(CONFIG_RTC_DRV_AT32AP700X)+= rtc-at32ap700x.o obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o +obj-$(CONFIG_RTC_DRV_AT91SAM9) += rtc-at91sam9.o obj-$(CONFIG_RTC_DRV_BFIN) += rtc-bfin.o obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o obj-$(CONFIG_RTC_DRV_DS1216) += rtc-ds1216.o +obj-$(CONFIG_RTC_DRV_DS1302) += rtc-ds1302.o obj-$(CONFIG_RTC_DRV_DS1307) += rtc-ds1307.o obj-$(CONFIG_RTC_DRV_DS1374) += rtc-ds1374.o +obj-$(CONFIG_RTC_DRV_DS1511) += rtc-ds1511.o obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o obj-$(CONFIG_RTC_DRV_DS1742) += rtc-ds1742.o @@ -38,6 +41,7 @@ obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o obj-$(CONFIG_RTC_DRV_PCF8583) += rtc-pcf8583.o obj-$(CONFIG_RTC_DRV_PL031) += rtc-pl031.o +obj-$(CONFIG_RTC_DRV_R9701) += rtc-r9701.o obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o obj-$(CONFIG_RTC_DRV_RS5C348) += rtc-rs5c348.o obj-$(CONFIG_RTC_DRV_RS5C372) += rtc-rs5c372.o diff --git a/drivers/rtc/rtc-at91sam9.c b/drivers/rtc/rtc-at91sam9.c new file mode 100644 index 00000000000..bbf10ecf416 --- /dev/null +++ b/drivers/rtc/rtc-at91sam9.c @@ -0,0 +1,520 @@ +/* + * "RTT as Real Time Clock" driver for AT91SAM9 SoC family + * + * (C) 2007 Michel Benoit + * + * Based on rtc-at91rm9200.c by Rick Bronson + * + * 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 <linux/module.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/time.h> +#include <linux/rtc.h> +#include <linux/interrupt.h> +#include <linux/ioctl.h> + +#include <asm/mach/time.h> +#include <asm/arch/board.h> +#include <asm/arch/at91_rtt.h> + + +/* + * This driver uses two configurable hardware resources that live in the + * AT91SAM9 backup power domain (intended to be powered at all times) + * to implement the Real Time Clock interfaces + * + * - A "Real-time Timer" (RTT) counts up in seconds from a base time. + * We can't assign the counter value (CRTV) ... but we can reset it. + * + * - One of the "General Purpose Backup Registers" (GPBRs) holds the + * base time, normally an offset from the beginning of the POSIX + * epoch (1970-Jan-1 00:00:00 UTC). Some systems also include the + * local timezone's offset. + * + * The RTC's value is the RTT counter plus that offset. The RTC's alarm + * is likewise a base (ALMV) plus that offset. + * + * Not all RTTs will be used as RTCs; some systems have multiple RTTs to + * choose from, or a "real" RTC module. All systems have multiple GPBR + * registers available, likewise usable for more than "RTC" support. + */ + +/* + * We store ALARM_DISABLED in ALMV to record that no alarm is set. + * It's also the reset value for that field. + */ +#define ALARM_DISABLED ((u32)~0) + + +struct sam9_rtc { + void __iomem *rtt; + struct rtc_device *rtcdev; + u32 imr; +}; + +#define rtt_readl(rtc, field) \ + __raw_readl((rtc)->rtt + AT91_RTT_ ## field) +#define rtt_writel(rtc, field, val) \ + __raw_writel((val), (rtc)->rtt + AT91_RTT_ ## field) + +#define gpbr_readl(rtc) \ + at91_sys_read(AT91_GPBR + 4 * CONFIG_RTC_DRV_AT91SAM9_GPBR) +#define gpbr_writel(rtc, val) \ + at91_sys_write(AT91_GPBR + 4 * CONFIG_RTC_DRV_AT91SAM9_GPBR, (val)) + +/* + * Read current time and date in RTC + */ +static int at91_rtc_readtime(struct device *dev, struct rtc_time *tm) +{ + struct sam9_rtc *rtc = dev_get_drvdata(dev); + u32 secs, secs2; + u32 offset; + + /* read current time offset */ + offset = gpbr_readl(rtc); + if (offset == 0) + return -EILSEQ; + + /* reread the counter to help sync the two clock domains */ + secs = rtt_readl(rtc, VR); + secs2 = rtt_readl(rtc, VR); + if (secs != secs2) + secs = rtt_readl(rtc, VR); + + rtc_time_to_tm(offset + secs, tm); + + dev_dbg(dev, "%s: %4d-%02d-%02d %02d:%02d:%02d\n", "readtime", + 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + + return 0; +} + +/* + * Set current time and date in RTC + */ +static int at91_rtc_settime(struct device *dev, struct rtc_time *tm) +{ + struct sam9_rtc *rtc = dev_get_drvdata(dev); + int err; + u32 offset, alarm, mr; + unsigned long secs; + + dev_dbg(dev, "%s: %4d-%02d-%02d %02d:%02d:%02d\n", "settime", + 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + + err = rtc_tm_to_time(tm, &secs); + if (err != 0) + return err; + + mr = rtt_readl(rtc, MR); + + /* disable interrupts */ + rtt_writel(rtc, MR, mr & ~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN)); + + /* read current time offset */ + offset = gpbr_readl(rtc); + + /* store the new base time in a battery backup register */ + secs += 1; + gpbr_writel(rtc, secs); + + /* adjust the alarm time for the new base */ + alarm = rtt_readl(rtc, AR); + if (alarm != ALARM_DISABLED) { + if (offset > secs) { + /* time jumped backwards, increase time until alarm */ + alarm += (offset - secs); + } else if ((alarm + offset) > secs) { + /* time jumped forwards, decrease time until alarm */ + alarm -= (secs - offset); + } else { + /* time jumped past the alarm, disable alarm */ + alarm = ALARM_DISABLED; + mr &= ~AT91_RTT_ALMIEN; + } + rtt_writel(rtc, AR, alarm); + } + + /* reset the timer, and re-enable interrupts */ + rtt_writel(rtc, MR, mr | AT91_RTT_RTTRST); + + return 0; +} + +static int at91_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct sam9_rtc *rtc = dev_get_drvdata(dev); + struct rtc_time *tm = &alrm->time; + u32 alarm = rtt_readl(rtc, AR); + u32 offset; + + offset = gpbr_readl(rtc); + if (offset == 0) + return -EILSEQ; + + memset(alrm, 0, sizeof(alrm)); + if (alarm != ALARM_DISABLED && offset != 0) { + rtc_time_to_tm(offset + alarm, tm); + + dev_dbg(dev, "%s: %4d-%02d-%02d %02d:%02d:%02d\n", "readalarm", + 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + + if (rtt_readl(rtc, MR) & AT91_RTT_ALMIEN) + alrm->enabled = 1; + } + + return 0; +} + +static int at91_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct sam9_rtc *rtc = dev_get_drvdata(dev); + struct rtc_time *tm = &alrm->time; + unsigned long secs; + u32 offset; + u32 mr; + int err; + + err = rtc_tm_to_time(tm, &secs); + if (err != 0) + return err; + + offset = gpbr_readl(rtc); + if (offset == 0) { + /* time is not set */ + return -EILSEQ; + } + mr = rtt_readl(rtc, MR); + rtt_writel(rtc, MR, mr & ~AT91_RTT_ALMIEN); + + /* alarm in the past? finish and leave disabled */ + if (secs <= offset) { + rtt_writel(rtc, AR, ALARM_DISABLED); + return 0; + } + + /* else set alarm and maybe enable it */ + rtt_writel(rtc, AR, secs - offset); + if (alrm->enabled) + rtt_writel(rtc, MR, mr | AT91_RTT_ALMIEN); + + dev_dbg(dev, "%s: %4d-%02d-%02d %02d:%02d:%02d\n", "setalarm", + tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, + tm->tm_min, tm->tm_sec); + + return 0; +} + +/* + * Handle commands from user-space + */ +static int at91_rtc_ioctl(struct device *dev, unsigned int cmd, + unsigned long arg) +{ + struct sam9_rtc *rtc = dev_get_drvdata(dev); + int ret = 0; + u32 mr = rtt_readl(rtc, MR); + + dev_dbg(dev, "ioctl: cmd=%08x, arg=%08lx, mr %08x\n", cmd, arg, mr); + + switch (cmd) { + case RTC_AIE_OFF: /* alarm off */ + rtt_writel(rtc, MR, mr & ~AT91_RTT_ALMIEN); + break; + case RTC_AIE_ON: /* alarm on */ + rtt_writel(rtc, MR, mr | AT91_RTT_ALMIEN); + break; + case RTC_UIE_OFF: /* update off */ + rtt_writel(rtc, MR, mr & ~AT91_RTT_RTTINCIEN); + break; + case RTC_UIE_ON: /* update on */ + rtt_writel(rtc, MR, mr | AT91_RTT_RTTINCIEN); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + +/* + * Provide additional RTC information in /proc/driver/rtc + */ +static int at91_rtc_proc(struct device *dev, struct seq_file *seq) +{ + struct sam9_rtc *rtc = dev_get_drvdata(dev); + u32 mr = mr = rtt_readl(rtc, MR); + + seq_printf(seq, "update_IRQ\t: %s\n", + (mr & AT91_RTT_RTTINCIEN) ? "yes" : "no"); + return 0; +} + +/* + * IRQ handler for the RTC + */ +static irqreturn_t at91_rtc_interrupt(int irq, void *_rtc) +{ + struct sam9_rtc *rtc = _rtc; + u32 sr, mr; + unsigned long events = 0; + + /* Shared interrupt may be for another device. Note: reading + * SR clears it, so we must only read it in this irq handler! + */ + mr = rtt_readl(rtc, MR) & (AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN); + sr = rtt_readl(rtc, SR) & mr; + if (!sr) + return IRQ_NONE; + + /* alarm status */ + if (sr & AT91_RTT_ALMS) + events |= (RTC_AF | RTC_IRQF); + + /* timer update/increment */ + if (sr & AT91_RTT_RTTINC) + events |= (RTC_UF | RTC_IRQF); + + rtc_update_irq(rtc->rtcdev, 1, events); + + pr_debug("%s: num=%ld, events=0x%02lx\n", __FUNCTION__, + events >> 8, events & 0x000000FF); + + return IRQ_HANDLED; +} + +static const struct rtc_class_ops at91_rtc_ops = { + .ioctl = at91_rtc_ioctl, + .read_time = at91_rtc_readtime, + .set_time = at91_rtc_settime, + .read_alarm = at91_rtc_readalarm, + .set_alarm = at91_rtc_setalarm, + .proc = at91_rtc_proc, +}; + +/* + * Initialize and install RTC driver + */ +static int __init at91_rtc_probe(struct platform_device *pdev) +{ + struct resource *r; + struct sam9_rtc *rtc; + int ret; + u32 mr; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) + return -ENODEV; + + rtc = kzalloc(sizeof *rtc, GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + platform_set_drvdata(pdev, rtc); + rtc->rtt = (void __force __iomem *) (AT91_VA_BASE_SYS - AT91_BASE_SYS); + rtc->rtt += r->start; + + mr = rtt_readl(rtc, MR); + + /* unless RTT is counting at 1 Hz, re-initialize it */ + if ((mr & AT91_RTT_RTPRES) != AT91_SLOW_CLOCK) { + mr = AT91_RTT_RTTRST | (AT91_SLOW_CLOCK & AT91_RTT_RTPRES); + gpbr_writel(rtc, 0); + } + + /* disable all interrupts (same as on shutdown path) */ + mr &= ~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN); + rtt_writel(rtc, MR, mr); + + rtc->rtcdev = rtc_device_register(pdev->name, &pdev->dev, + &at91_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc->rtcdev)) { + ret = PTR_ERR(rtc->rtcdev); + goto fail; + } + + /* register irq handler after we know what name we'll use */ + ret = request_irq(AT91_ID_SYS, at91_rtc_interrupt, + IRQF_DISABLED | IRQF_SHARED, + rtc->rtcdev->dev.bus_id, rtc); + if (ret) { + dev_dbg(&pdev->dev, "can't share IRQ %d?\n", AT91_ID_SYS); + rtc_device_unregister(rtc->rtcdev); + goto fail; + } + + /* NOTE: sam9260 rev A silicon has a ROM bug which resets the + * RTT on at least some reboots. If you have that chip, you must + * initialize the time from some external source like a GPS, wall + * clock, discrete RTC, etc + */ + + if (gpbr_readl(rtc) == 0) + dev_warn(&pdev->dev, "%s: SET TIME!\n", + rtc->rtcdev->dev.bus_id); + + return 0; + +fail: + platform_set_drvdata(pdev, NULL); + kfree(rtc); + return ret; +} + +/* + * Disable and remove the RTC driver + */ +static int __exit at91_rtc_remove(struct platform_device *pdev) +{ + struct sam9_rtc *rtc = platform_get_drvdata(pdev); + u32 mr = rtt_readl(rtc, MR); + + /* disable all interrupts */ + rtt_writel(rtc, MR, mr & ~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN)); + free_irq(AT91_ID_SYS, rtc); + + rtc_device_unregister(rtc->rtcdev); + + platform_set_drvdata(pdev, NULL); + kfree(rtc); + return 0; +} + +static void at91_rtc_shutdown(struct platform_device *pdev) +{ + struct sam9_rtc *rtc = platform_get_drvdata(pdev); + u32 mr = rtt_readl(rtc, MR); + + rtc->imr = mr & (AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN); + rtt_writel(rtc, MR, mr & ~rtc->imr); +} + +#ifdef CONFIG_PM + +/* AT91SAM9 RTC Power management control */ + +static int at91_rtc_suspend(struct platform_device *pdev, + pm_message_t state) +{ + struct sam9_rtc *rtc = platform_get_drvdata(pdev); + u32 mr = rtt_readl(rtc, MR); + + /* + * This IRQ is shared with DBGU and other hardware which isn't + * necessarily a wakeup event source. + */ + rtc->imr = mr & (AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN); + if (rtc->imr) { + if (device_may_wakeup(&pdev->dev) && (mr & AT91_RTT_ALMIEN)) { + enable_irq_wake(AT91_ID_SYS); + /* don't let RTTINC cause wakeups */ + if (mr & AT91_RTT_RTTINCIEN) + rtt_writel(rtc, MR, mr & ~AT91_RTT_RTTINCIEN); + } else + rtt_writel(rtc, MR, mr & ~rtc->imr); + } + + return 0; +} + +static int at91_rtc_resume(struct platform_device *pdev) +{ + struct sam9_rtc *rtc = platform_get_drvdata(pdev); + u32 mr; + + if (rtc->imr) { + if (device_may_wakeup(&pdev->dev)) + disable_irq_wake(AT91_ID_SYS); + mr = rtt_readl(rtc, MR); + rtt_writel(rtc, MR, mr | rtc->imr); + } + + return 0; +} +#else +#define at91_rtc_suspend NULL +#define at91_rtc_resume NULL +#endif + +static struct platform_driver at91_rtc_driver = { + .driver.name = "rtc-at91sam9", + .driver.owner = THIS_MODULE, + .remove = __exit_p(at91_rtc_remove), + .shutdown = at91_rtc_shutdown, + .suspend = at91_rtc_suspend, + .resume = at91_rtc_resume, +}; + +/* Chips can have more than one RTT module, and they can be used for more + * than just RTCs. So we can't just register as "the" RTT driver. + * + * A normal approach in such cases is to create a library to allocate and + * free the modules. Here we just use bus_find_device() as like such a + * library, binding directly ... no runtime "library" footprint is needed. + */ +static int __init at91_rtc_match(struct device *dev, void *v) +{ + struct platform_device *pdev = to_platform_device(dev); + int ret; + + /* continue searching if this isn't the RTT we need */ + if (strcmp("at91_rtt", pdev->name) != 0 + || pdev->id != CONFIG_RTC_DRV_AT91SAM9_RTT) + goto fail; + + /* else we found it ... but fail unless we can bind to the RTC driver */ + if (dev->driver) { + dev_dbg(dev, "busy, can't use as RTC!\n"); + goto fail; + } + dev->driver = &at91_rtc_driver.driver; + if (device_attach(dev) == 0) { + dev_dbg(dev, "can't attach RTC!\n"); + goto fail; + } + ret = at91_rtc_probe(pdev); + if (ret == 0) + return true; + + dev_dbg(dev, "RTC probe err %d!\n", ret); +fail: + return false; +} + +static int __init at91_rtc_init(void) +{ + int status; + struct device *rtc; + + status = platform_driver_register(&at91_rtc_driver); + if (status) + return status; + rtc = bus_find_device(&platform_bus_type, NULL, + NULL, at91_rtc_match); + if (!rtc) + platform_driver_unregister(&at91_rtc_driver); + return rtc ? 0 : -ENODEV; +} +module_init(at91_rtc_init); + +static void __exit at91_rtc_exit(void) +{ + platform_driver_unregister(&at91_rtc_driver); +} +module_exit(at91_rtc_exit); + + +MODULE_AUTHOR("Michel Benoit"); +MODULE_DESCRIPTION("RTC driver for Atmel AT91SAM9x"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-bfin.c b/drivers/rtc/rtc-bfin.c index 1aa709dda0d..d90ba860d21 100644 --- a/drivers/rtc/rtc-bfin.c +++ b/drivers/rtc/rtc-bfin.c @@ -1,6 +1,6 @@ /* * Blackfin On-Chip Real Time Clock Driver - * Supports BF53[123]/BF53[467]/BF54[2489] + * Supports BF52[257]/BF53[123]/BF53[467]/BF54[24789] * * Copyright 2004-2007 Analog Devices Inc. * @@ -32,26 +32,25 @@ * writes to clear status registers complete immediately. */ -#include <linux/module.h> -#include <linux/kernel.h> #include <linux/bcd.h> -#include <linux/rtc.h> +#include <linux/completion.h> +#include <linux/delay.h> #include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> #include <linux/platform_device.h> +#include <linux/rtc.h> #include <linux/seq_file.h> -#include <linux/interrupt.h> -#include <linux/spinlock.h> -#include <linux/delay.h> #include <asm/blackfin.h> -#define stamp(fmt, args...) pr_debug("%s:%i: " fmt "\n", __FUNCTION__, __LINE__, ## args) -#define stampit() stamp("here i am") +#define dev_dbg_stamp(dev) dev_dbg(dev, "%s:%i: here i am\n", __func__, __LINE__) struct bfin_rtc { struct rtc_device *rtc_dev; struct rtc_time rtc_alarm; - spinlock_t lock; + u16 rtc_wrote_regs; }; /* Bit values for the ISTAT / ICTL registers */ @@ -72,7 +71,7 @@ struct bfin_rtc { #define SEC_BITS_OFF 0 /* Some helper functions to convert between the common RTC notion of time - * and the internal Blackfin notion that is stored in 32bits. + * and the internal Blackfin notion that is encoded in 32bits. */ static inline u32 rtc_time_to_bfin(unsigned long now) { @@ -97,7 +96,10 @@ static inline void rtc_bfin_to_tm(u32 rtc_bfin, struct rtc_time *tm) rtc_time_to_tm(rtc_bfin_to_time(rtc_bfin), tm); } -/* Wait for the previous write to a RTC register to complete. +/** + * bfin_rtc_sync_pending - make sure pending writes have complete + * + * Wait for the previous write to a RTC register to complete. * Unfortunately, we can't sleep here as that introduces a race condition when * turning on interrupt events. Consider this: * - process sets alarm @@ -112,188 +114,202 @@ static inline void rtc_bfin_to_tm(u32 rtc_bfin, struct rtc_time *tm) * If anyone can point out the obvious solution here, i'm listening :). This * shouldn't be an issue on an SMP or preempt system as this function should * only be called with the rtc lock held. + * + * Other options: + * - disable PREN so the sync happens at 32.768kHZ ... but this changes the + * inc rate for all RTC registers from 1HZ to 32.768kHZ ... + * - use the write complete IRQ */ -static void rtc_bfin_sync_pending(void) +/* +static void bfin_rtc_sync_pending_polled(void) { - stampit(); - while (!(bfin_read_RTC_ISTAT() & RTC_ISTAT_WRITE_COMPLETE)) { + while (!(bfin_read_RTC_ISTAT() & RTC_ISTAT_WRITE_COMPLETE)) if (!(bfin_read_RTC_ISTAT() & RTC_ISTAT_WRITE_PENDING)) break; - } bfin_write_RTC_ISTAT(RTC_ISTAT_WRITE_COMPLETE); } +*/ +static DECLARE_COMPLETION(bfin_write_complete); +static void bfin_rtc_sync_pending(struct device *dev) +{ + dev_dbg_stamp(dev); + while (bfin_read_RTC_ISTAT() & RTC_ISTAT_WRITE_PENDING) + wait_for_completion_timeout(&bfin_write_complete, HZ * 5); + dev_dbg_stamp(dev); +} -static void rtc_bfin_reset(struct bfin_rtc *rtc) +/** + * bfin_rtc_reset - set RTC to sane/known state + * + * Initialize the RTC. Enable pre-scaler to scale RTC clock + * to 1Hz and clear interrupt/status registers. + */ +static void bfin_rtc_reset(struct device *dev) { - /* Initialize the RTC. Enable pre-scaler to scale RTC clock - * to 1Hz and clear interrupt/status registers. */ - spin_lock_irq(&rtc->lock); - rtc_bfin_sync_pending(); + struct bfin_rtc *rtc = dev_get_drvdata(dev); + dev_dbg_stamp(dev); + bfin_rtc_sync_pending(dev); bfin_write_RTC_PREN(0x1); - bfin_write_RTC_ICTL(0); + bfin_write_RTC_ICTL(RTC_ISTAT_WRITE_COMPLETE); bfin_write_RTC_SWCNT(0); bfin_write_RTC_ALARM(0); bfin_write_RTC_ISTAT(0xFFFF); - spin_unlock_irq(&rtc->lock); + rtc->rtc_wrote_regs = 0; } +/** + * bfin_rtc_interrupt - handle interrupt from RTC + * + * Since we handle all RTC events here, we have to make sure the requested + * interrupt is enabled (in RTC_ICTL) as the event status register (RTC_ISTAT) + * always gets updated regardless of the interrupt being enabled. So when one + * even we care about (e.g. stopwatch) goes off, we don't want to turn around + * and say that other events have happened as well (e.g. second). We do not + * have to worry about pending writes to the RTC_ICTL register as interrupts + * only fire if they are enabled in the RTC_ICTL register. + */ static irqreturn_t bfin_rtc_interrupt(int irq, void *dev_id) { - struct platform_device *pdev = to_platform_device(dev_id); - struct bfin_rtc *rtc = platform_get_drvdata(pdev); + struct device *dev = dev_id; + struct bfin_rtc *rtc = dev_get_drvdata(dev); unsigned long events = 0; - u16 rtc_istat; - - stampit(); + bool write_complete = false; + u16 rtc_istat, rtc_ictl; - spin_lock_irq(&rtc->lock); + dev_dbg_stamp(dev); rtc_istat = bfin_read_RTC_ISTAT(); + rtc_ictl = bfin_read_RTC_ICTL(); - if (rtc_istat & (RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY)) { - bfin_write_RTC_ISTAT(RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY); - events |= RTC_AF | RTC_IRQF; + if (rtc_istat & RTC_ISTAT_WRITE_COMPLETE) { + bfin_write_RTC_ISTAT(RTC_ISTAT_WRITE_COMPLETE); + write_complete = true; + complete(&bfin_write_complete); } - if (rtc_istat & RTC_ISTAT_STOPWATCH) { - bfin_write_RTC_ISTAT(RTC_ISTAT_STOPWATCH); - events |= RTC_PF | RTC_IRQF; - bfin_write_RTC_SWCNT(rtc->rtc_dev->irq_freq); + if (rtc_ictl & (RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY)) { + if (rtc_istat & (RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY)) { + bfin_write_RTC_ISTAT(RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY); + events |= RTC_AF | RTC_IRQF; + } } - if (rtc_istat & RTC_ISTAT_SEC) { - bfin_write_RTC_ISTAT(RTC_ISTAT_SEC); - events |= RTC_UF | RTC_IRQF; + if (rtc_ictl & RTC_ISTAT_STOPWATCH) { + if (rtc_istat & RTC_ISTAT_STOPWATCH) { + bfin_write_RTC_ISTAT(RTC_ISTAT_STOPWATCH); + events |= RTC_PF | RTC_IRQF; + bfin_write_RTC_SWCNT(rtc->rtc_dev->irq_freq); + } } - rtc_update_irq(rtc->rtc_dev, 1, events); + if (rtc_ictl & RTC_ISTAT_SEC) { + if (rtc_istat & RTC_ISTAT_SEC) { + bfin_write_RTC_ISTAT(RTC_ISTAT_SEC); + events |= RTC_UF | RTC_IRQF; + } + } - spin_unlock_irq(&rtc->lock); + if (events) + rtc_update_irq(rtc->rtc_dev, 1, events); - return IRQ_HANDLED; + if (write_complete || events) + return IRQ_HANDLED; + else + return IRQ_NONE; } static int bfin_rtc_open(struct device *dev) { - struct bfin_rtc *rtc = dev_get_drvdata(dev); int ret; - stampit(); + dev_dbg_stamp(dev); - ret = request_irq(IRQ_RTC, bfin_rtc_interrupt, IRQF_DISABLED, "rtc-bfin", dev); - if (unlikely(ret)) { - dev_err(dev, "request RTC IRQ failed with %d\n", ret); - return ret; - } - - rtc_bfin_reset(rtc); + ret = request_irq(IRQ_RTC, bfin_rtc_interrupt, IRQF_SHARED, to_platform_device(dev)->name, dev); + if (!ret) + bfin_rtc_reset(dev); return ret; } static void bfin_rtc_release(struct device *dev) { - struct bfin_rtc *rtc = dev_get_drvdata(dev); - stampit(); - rtc_bfin_reset(rtc); + dev_dbg_stamp(dev); + bfin_rtc_reset(dev); free_irq(IRQ_RTC, dev); } +static void bfin_rtc_int_set(struct bfin_rtc *rtc, u16 rtc_int) +{ + bfin_write_RTC_ISTAT(rtc_int); + bfin_write_RTC_ICTL(bfin_read_RTC_ICTL() | rtc_int); +} +static void bfin_rtc_int_clear(struct bfin_rtc *rtc, u16 rtc_int) +{ + bfin_write_RTC_ICTL(bfin_read_RTC_ICTL() & rtc_int); +} +static void bfin_rtc_int_set_alarm(struct bfin_rtc *rtc) +{ + /* Blackfin has different bits for whether the alarm is + * more than 24 hours away. + */ + bfin_rtc_int_set(rtc, (rtc->rtc_alarm.tm_yday == -1 ? RTC_ISTAT_ALARM : RTC_ISTAT_ALARM_DAY)); +} static int bfin_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) { struct bfin_rtc *rtc = dev_get_drvdata(dev); + int ret = 0; + + dev_dbg_stamp(dev); - stampit(); + bfin_rtc_sync_pending(dev); switch (cmd) { case RTC_PIE_ON: - stampit(); - spin_lock_irq(&rtc->lock); - rtc_bfin_sync_pending(); - bfin_write_RTC_ISTAT(RTC_ISTAT_STOPWATCH); + dev_dbg_stamp(dev); + bfin_rtc_int_set(rtc, RTC_ISTAT_STOPWATCH); bfin_write_RTC_SWCNT(rtc->rtc_dev->irq_freq); - bfin_write_RTC_ICTL(bfin_read_RTC_ICTL() | RTC_ISTAT_STOPWATCH); - spin_unlock_irq(&rtc->lock); - return 0; + break; case RTC_PIE_OFF: - stampit(); - spin_lock_irq(&rtc->lock); - rtc_bfin_sync_pending(); - bfin_write_RTC_SWCNT(0); - bfin_write_RTC_ICTL(bfin_read_RTC_ICTL() & ~RTC_ISTAT_STOPWATCH); - spin_unlock_irq(&rtc->lock); - return 0; + dev_dbg_stamp(dev); + bfin_rtc_int_clear(rtc, ~RTC_ISTAT_STOPWATCH); + break; case RTC_UIE_ON: - stampit(); - spin_lock_irq(&rtc->lock); - rtc_bfin_sync_pending(); - bfin_write_RTC_ISTAT(RTC_ISTAT_SEC); - bfin_write_RTC_ICTL(bfin_read_RTC_ICTL() | RTC_ISTAT_SEC); - spin_unlock_irq(&rtc->lock); - return 0; + dev_dbg_stamp(dev); + bfin_rtc_int_set(rtc, RTC_ISTAT_SEC); + break; case RTC_UIE_OFF: - stampit(); - spin_lock_irq(&rtc->lock); - rtc_bfin_sync_pending(); - bfin_write_RTC_ICTL(bfin_read_RTC_ICTL() & ~RTC_ISTAT_SEC); - spin_unlock_irq(&rtc->lock); - return 0; - - case RTC_AIE_ON: { - unsigned long rtc_alarm; - u16 which_alarm; - int ret = 0; - - stampit(); - - spin_lock_irq(&rtc->lock); - - rtc_bfin_sync_pending(); - if (rtc->rtc_alarm.tm_yday == -1) { - struct rtc_time now; - rtc_bfin_to_tm(bfin_read_RTC_STAT(), &now); - now.tm_sec = rtc->rtc_alarm.tm_sec; - now.tm_min = rtc->rtc_alarm.tm_min; - now.tm_hour = rtc->rtc_alarm.tm_hour; - ret = rtc_tm_to_time(&now, &rtc_alarm); - which_alarm = RTC_ISTAT_ALARM; - } else { - ret = rtc_tm_to_time(&rtc->rtc_alarm, &rtc_alarm); - which_alarm = RTC_ISTAT_ALARM_DAY; - } - if (ret == 0) { - bfin_write_RTC_ISTAT(which_alarm); - bfin_write_RTC_ALARM(rtc_time_to_bfin(rtc_alarm)); - bfin_write_RTC_ICTL(bfin_read_RTC_ICTL() | which_alarm); - } - - spin_unlock_irq(&rtc->lock); - - return ret; - } + dev_dbg_stamp(dev); + bfin_rtc_int_clear(rtc, ~RTC_ISTAT_SEC); + break; + + case RTC_AIE_ON: + dev_dbg_stamp(dev); + bfin_rtc_int_set_alarm(rtc); + break; case RTC_AIE_OFF: - stampit(); - spin_lock_irq(&rtc->lock); - rtc_bfin_sync_pending(); - bfin_write_RTC_ICTL(bfin_read_RTC_ICTL() & ~(RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY)); - spin_unlock_irq(&rtc->lock); - return 0; + dev_dbg_stamp(dev); + bfin_rtc_int_clear(rtc, ~(RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY)); + break; + + default: + dev_dbg_stamp(dev); + ret = -ENOIOCTLCMD; } - return -ENOIOCTLCMD; + return ret; } static int bfin_rtc_read_time(struct device *dev, struct rtc_time *tm) { struct bfin_rtc *rtc = dev_get_drvdata(dev); - stampit(); + dev_dbg_stamp(dev); + + if (rtc->rtc_wrote_regs & 0x1) + bfin_rtc_sync_pending(dev); - spin_lock_irq(&rtc->lock); - rtc_bfin_sync_pending(); rtc_bfin_to_tm(bfin_read_RTC_STAT(), tm); - spin_unlock_irq(&rtc->lock); return 0; } @@ -304,64 +320,79 @@ static int bfin_rtc_set_time(struct device *dev, struct rtc_time *tm) int ret; unsigned long now; - stampit(); - - spin_lock_irq(&rtc->lock); + dev_dbg_stamp(dev); ret = rtc_tm_to_time(tm, &now); if (ret == 0) { - rtc_bfin_sync_pending(); + if (rtc->rtc_wrote_regs & 0x1) + bfin_rtc_sync_pending(dev); bfin_write_RTC_STAT(rtc_time_to_bfin(now)); + rtc->rtc_wrote_regs = 0x1; } - spin_unlock_irq(&rtc->lock); - return ret; } static int bfin_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) { struct bfin_rtc *rtc = dev_get_drvdata(dev); - stampit(); - memcpy(&alrm->time, &rtc->rtc_alarm, sizeof(struct rtc_time)); - alrm->pending = !!(bfin_read_RTC_ICTL() & (RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY)); + dev_dbg_stamp(dev); + alrm->time = rtc->rtc_alarm; + bfin_rtc_sync_pending(dev); + alrm->enabled = !!(bfin_read_RTC_ICTL() & (RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY)); return 0; } static int bfin_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) { struct bfin_rtc *rtc = dev_get_drvdata(dev); - stampit(); - memcpy(&rtc->rtc_alarm, &alrm->time, sizeof(struct rtc_time)); + unsigned long rtc_alarm; + + dev_dbg_stamp(dev); + + if (rtc_tm_to_time(&alrm->time, &rtc_alarm)) + return -EINVAL; + + rtc->rtc_alarm = alrm->time; + + bfin_rtc_sync_pending(dev); + bfin_write_RTC_ALARM(rtc_time_to_bfin(rtc_alarm)); + if (alrm->enabled) + bfin_rtc_int_set_alarm(rtc); + return 0; } static int bfin_rtc_proc(struct device *dev, struct seq_file *seq) { -#define yesno(x) (x ? "yes" : "no") +#define yesno(x) ((x) ? "yes" : "no") u16 ictl = bfin_read_RTC_ICTL(); - stampit(); - seq_printf(seq, "alarm_IRQ\t: %s\n", yesno(ictl & RTC_ISTAT_ALARM)); - seq_printf(seq, "wkalarm_IRQ\t: %s\n", yesno(ictl & RTC_ISTAT_ALARM_DAY)); - seq_printf(seq, "seconds_IRQ\t: %s\n", yesno(ictl & RTC_ISTAT_SEC)); - seq_printf(seq, "periodic_IRQ\t: %s\n", yesno(ictl & RTC_ISTAT_STOPWATCH)); -#ifdef DEBUG - seq_printf(seq, "RTC_STAT\t: 0x%08X\n", bfin_read_RTC_STAT()); - seq_printf(seq, "RTC_ICTL\t: 0x%04X\n", bfin_read_RTC_ICTL()); - seq_printf(seq, "RTC_ISTAT\t: 0x%04X\n", bfin_read_RTC_ISTAT()); - seq_printf(seq, "RTC_SWCNT\t: 0x%04X\n", bfin_read_RTC_SWCNT()); - seq_printf(seq, "RTC_ALARM\t: 0x%08X\n", bfin_read_RTC_ALARM()); - seq_printf(seq, "RTC_PREN\t: 0x%04X\n", bfin_read_RTC_PREN()); -#endif + dev_dbg_stamp(dev); + seq_printf(seq, + "alarm_IRQ\t: %s\n" + "wkalarm_IRQ\t: %s\n" + "seconds_IRQ\t: %s\n" + "periodic_IRQ\t: %s\n", + yesno(ictl & RTC_ISTAT_ALARM), + yesno(ictl & RTC_ISTAT_ALARM_DAY), + yesno(ictl & RTC_ISTAT_SEC), + yesno(ictl & RTC_ISTAT_STOPWATCH)); return 0; +#undef yesno } +/** + * bfin_irq_set_freq - make sure hardware supports requested freq + * @dev: pointer to RTC device structure + * @freq: requested frequency rate + * + * The Blackfin RTC can only generate periodic events at 1 per + * second (1 Hz), so reject any attempt at changing it. + */ static int bfin_irq_set_freq(struct device *dev, int freq) { - struct bfin_rtc *rtc = dev_get_drvdata(dev); - stampit(); - rtc->rtc_dev->irq_freq = freq; - return 0; + dev_dbg_stamp(dev); + return -ENOTTY; } static struct rtc_class_ops bfin_rtc_ops = { @@ -381,27 +412,24 @@ static int __devinit bfin_rtc_probe(struct platform_device *pdev) struct bfin_rtc *rtc; int ret = 0; - stampit(); + dev_dbg_stamp(&pdev->dev); rtc = kzalloc(sizeof(*rtc), GFP_KERNEL); if (unlikely(!rtc)) return -ENOMEM; - spin_lock_init(&rtc->lock); - rtc->rtc_dev = rtc_device_register(pdev->name, &pdev->dev, &bfin_rtc_ops, THIS_MODULE); if (unlikely(IS_ERR(rtc))) { ret = PTR_ERR(rtc->rtc_dev); goto err; } - rtc->rtc_dev->irq_freq = 0; - rtc->rtc_dev->max_user_freq = (2 << 16); /* stopwatch is an unsigned 16 bit reg */ + rtc->rtc_dev->irq_freq = 1; platform_set_drvdata(pdev, rtc); return 0; -err: + err: kfree(rtc); return ret; } @@ -428,7 +456,6 @@ static struct platform_driver bfin_rtc_driver = { static int __init bfin_rtc_init(void) { - stampit(); return platform_driver_register(&bfin_rtc_driver); } diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index 29cf1457ca1..e059f94c79e 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -36,9 +36,24 @@ #include <linux/platform_device.h> #include <linux/mod_devicetable.h> +#ifdef CONFIG_HPET_EMULATE_RTC +#include <asm/hpet.h> +#endif + /* this is for "generic access to PC-style RTC" using CMOS_READ/CMOS_WRITE */ #include <asm-generic/rtc.h> +#ifndef CONFIG_HPET_EMULATE_RTC +#define is_hpet_enabled() 0 +#define hpet_set_alarm_time(hrs, min, sec) do { } while (0) +#define hpet_set_periodic_freq(arg) 0 +#define hpet_mask_rtc_irq_bit(arg) do { } while (0) +#define hpet_set_rtc_irq_bit(arg) do { } while (0) +#define hpet_rtc_timer_init() do { } while (0) +#define hpet_register_irq_handler(h) 0 +#define hpet_unregister_irq_handler(h) do { } while (0) +extern irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id); +#endif struct cmos_rtc { struct rtc_device *rtc; @@ -199,6 +214,7 @@ static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t) sec = t->time.tm_sec; sec = (sec < 60) ? BIN2BCD(sec) : 0xff; + hpet_set_alarm_time(t->time.tm_hour, t->time.tm_min, t->time.tm_sec); spin_lock_irq(&rtc_lock); /* next rtc irq must not be from previous alarm setting */ @@ -252,7 +268,8 @@ static int cmos_irq_set_freq(struct device *dev, int freq) f = 16 - f; spin_lock_irqsave(&rtc_lock, flags); - CMOS_WRITE(RTC_REF_CLCK_32KHZ | f, RTC_FREQ_SELECT); + if (!hpet_set_periodic_freq(freq)) + CMOS_WRITE(RTC_REF_CLCK_32KHZ | f, RTC_FREQ_SELECT); spin_unlock_irqrestore(&rtc_lock, flags); return 0; @@ -314,28 +331,37 @@ cmos_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) switch (cmd) { case RTC_AIE_OFF: /* alarm off */ rtc_control &= ~RTC_AIE; + hpet_mask_rtc_irq_bit(RTC_AIE); break; case RTC_AIE_ON: /* alarm on */ rtc_control |= RTC_AIE; + hpet_set_rtc_irq_bit(RTC_AIE); break; case RTC_UIE_OFF: /* update off */ rtc_control &= ~RTC_UIE; + hpet_mask_rtc_irq_bit(RTC_UIE); break; case RTC_UIE_ON: /* update on */ rtc_control |= RTC_UIE; + hpet_set_rtc_irq_bit(RTC_UIE); break; case RTC_PIE_OFF: /* periodic off */ rtc_control &= ~RTC_PIE; + hpet_mask_rtc_irq_bit(RTC_PIE); break; case RTC_PIE_ON: /* periodic on */ rtc_control |= RTC_PIE; + hpet_set_rtc_irq_bit(RTC_PIE); break; } - CMOS_WRITE(rtc_control, RTC_CONTROL); + if (!is_hpet_enabled()) + CMOS_WRITE(rtc_control, RTC_CONTROL); + rtc_intr = CMOS_READ(RTC_INTR_FLAGS); rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF; if (is_intr(rtc_intr)) rtc_update_irq(cmos->rtc, 1, rtc_intr); + spin_unlock_irqrestore(&rtc_lock, flags); return 0; } @@ -393,15 +419,111 @@ static const struct rtc_class_ops cmos_rtc_ops = { /*----------------------------------------------------------------*/ +/* + * All these chips have at least 64 bytes of address space, shared by + * RTC registers and NVRAM. Most of those bytes of NVRAM are used + * by boot firmware. Modern chips have 128 or 256 bytes. + */ + +#define NVRAM_OFFSET (RTC_REG_D + 1) + +static ssize_t +cmos_nvram_read(struct kobject *kobj, struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + int retval; + + if (unlikely(off >= attr->size)) + return 0; + if ((off + count) > attr->size) + count = attr->size - off; + + spin_lock_irq(&rtc_lock); + for (retval = 0, off += NVRAM_OFFSET; count--; retval++, off++) + *buf++ = CMOS_READ(off); + spin_unlock_irq(&rtc_lock); + + return retval; +} + +static ssize_t +cmos_nvram_write(struct kobject *kobj, struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct cmos_rtc *cmos; + int retval; + + cmos = dev_get_drvdata(container_of(kobj, struct device, kobj)); + if (unlikely(off >= attr->size)) + return -EFBIG; + if ((off + count) > attr->size) + count = attr->size - off; + + /* NOTE: on at least PCs and Ataris, the boot firmware uses a + * checksum on part of the NVRAM data. That's currently ignored + * here. If userspace is smart enough to know what fields of + * NVRAM to update, updating checksums is also part of its job. + */ + spin_lock_irq(&rtc_lock); + for (retval = 0, off += NVRAM_OFFSET; count--; retval++, off++) { + /* don't trash RTC registers */ + if (off == cmos->day_alrm + || off == cmos->mon_alrm + || off == cmos->century) + buf++; + else + CMOS_WRITE(*buf++, off); + } + spin_unlock_irq(&rtc_lock); + + return retval; +} + +static struct bin_attribute nvram = { + .attr = { + .name = "nvram", + .mode = S_IRUGO | S_IWUSR, + .owner = THIS_MODULE, + }, + + .read = cmos_nvram_read, + .write = cmos_nvram_write, + /* size gets set up later */ +}; + +/*----------------------------------------------------------------*/ + static struct cmos_rtc cmos_rtc; static irqreturn_t cmos_interrupt(int irq, void *p) { u8 irqstat; + u8 rtc_control; spin_lock(&rtc_lock); - irqstat = CMOS_READ(RTC_INTR_FLAGS); - irqstat &= (CMOS_READ(RTC_CONTROL) & RTC_IRQMASK) | RTC_IRQF; + /* + * In this case it is HPET RTC interrupt handler + * calling us, with the interrupt information + * passed as arg1, instead of irq. + */ + if (is_hpet_enabled()) + irqstat = (unsigned long)irq & 0xF0; + else { + irqstat = CMOS_READ(RTC_INTR_FLAGS); + rtc_control = CMOS_READ(RTC_CONTROL); + irqstat &= (rtc_control & RTC_IRQMASK) | RTC_IRQF; + } + + /* All Linux RTC alarms should be treated as if they were oneshot. + * Similar code may be needed in system wakeup paths, in case the + * alarm woke the system. + */ + if (irqstat & RTC_AIE) { + rtc_control = CMOS_READ(RTC_CONTROL); + rtc_control &= ~RTC_AIE; + CMOS_WRITE(rtc_control, RTC_CONTROL); + CMOS_READ(RTC_INTR_FLAGS); + } spin_unlock(&rtc_lock); if (is_intr(irqstat)) { @@ -412,11 +534,9 @@ static irqreturn_t cmos_interrupt(int irq, void *p) } #ifdef CONFIG_PNP -#define is_pnp() 1 #define INITSECTION #else -#define is_pnp() 0 #define INITSECTION __init #endif @@ -426,6 +546,7 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) struct cmos_rtc_board_info *info = dev->platform_data; int retval = 0; unsigned char rtc_control; + unsigned address_space; /* there can be only one ... */ if (cmos_rtc.dev) @@ -450,15 +571,36 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) cmos_rtc.irq = rtc_irq; cmos_rtc.iomem = ports; + /* Heuristic to deduce NVRAM size ... do what the legacy NVRAM + * driver did, but don't reject unknown configs. Old hardware + * won't address 128 bytes, and for now we ignore the way newer + * chips can address 256 bytes (using two more i/o ports). + */ +#if defined(CONFIG_ATARI) + address_space = 64; +#elif defined(__i386__) || defined(__x86_64__) || defined(__arm__) + address_space = 128; +#else +#warning Assuming 128 bytes of RTC+NVRAM address space, not 64 bytes. + address_space = 128; +#endif + /* For ACPI systems extension info comes from the FADT. On others, * board specific setup provides it as appropriate. Systems where * the alarm IRQ isn't automatically a wakeup IRQ (like ACPI, and * some almost-clones) can provide hooks to make that behave. + * + * Note that ACPI doesn't preclude putting these registers into + * "extended" areas of the chip, including some that we won't yet + * expect CMOS_READ and friends to handle. */ if (info) { - cmos_rtc.day_alrm = info->rtc_day_alarm; - cmos_rtc.mon_alrm = info->rtc_mon_alarm; - cmos_rtc.century = info->rtc_century; + if (info->rtc_day_alarm && info->rtc_day_alarm < 128) + cmos_rtc.day_alrm = info->rtc_day_alarm; + if (info->rtc_mon_alarm && info->rtc_mon_alarm < 128) + cmos_rtc.mon_alrm = info->rtc_mon_alarm; + if (info->rtc_century && info->rtc_century < 128) + cmos_rtc.century = info->rtc_century; if (info->wake_on && info->wake_off) { cmos_rtc.wake_on = info->wake_on; @@ -485,8 +627,9 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) * doesn't use 32KHz here ... for portability we might need to * do something about other clock frequencies. */ - CMOS_WRITE(RTC_REF_CLCK_32KHZ | 0x06, RTC_FREQ_SELECT); cmos_rtc.rtc->irq_freq = 1024; + if (!hpet_set_periodic_freq(cmos_rtc.rtc->irq_freq)) + CMOS_WRITE(RTC_REF_CLCK_32KHZ | 0x06, RTC_FREQ_SELECT); /* disable irqs. * @@ -509,19 +652,39 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) goto cleanup1; } - if (is_valid_irq(rtc_irq)) - retval = request_irq(rtc_irq, cmos_interrupt, IRQF_DISABLED, - cmos_rtc.rtc->dev.bus_id, + if (is_valid_irq(rtc_irq)) { + irq_handler_t rtc_cmos_int_handler; + + if (is_hpet_enabled()) { + int err; + + rtc_cmos_int_handler = hpet_rtc_interrupt; + err = hpet_register_irq_handler(cmos_interrupt); + if (err != 0) { + printk(KERN_WARNING "hpet_register_irq_handler " + " failed in rtc_init()."); + goto cleanup1; + } + } else + rtc_cmos_int_handler = cmos_interrupt; + + retval = request_irq(rtc_irq, rtc_cmos_int_handler, + IRQF_DISABLED, cmos_rtc.rtc->dev.bus_id, cmos_rtc.rtc); - if (retval < 0) { - dev_dbg(dev, "IRQ %d is already in use\n", rtc_irq); - goto cleanup1; + if (retval < 0) { + dev_dbg(dev, "IRQ %d is already in use\n", rtc_irq); + goto cleanup1; + } } + hpet_rtc_timer_init(); - /* REVISIT optionally make 50 or 114 bytes NVRAM available, - * like rtc-ds1553, rtc-ds1742 ... this will often include - * registers for century, and day/month alarm. - */ + /* export at least the first block of NVRAM */ + nvram.size = address_space - NVRAM_OFFSET; + retval = sysfs_create_bin_file(&dev->kobj, &nvram); + if (retval < 0) { + dev_dbg(dev, "can't create nvram file? %d\n", retval); + goto cleanup2; + } pr_info("%s: alarms up to one %s%s\n", cmos_rtc.rtc->dev.bus_id, @@ -536,6 +699,9 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) return 0; +cleanup2: + if (is_valid_irq(rtc_irq)) + free_irq(rtc_irq, cmos_rtc.rtc); cleanup1: cmos_rtc.dev = NULL; rtc_device_unregister(cmos_rtc.rtc); @@ -563,8 +729,12 @@ static void __exit cmos_do_remove(struct device *dev) cmos_do_shutdown(); - if (is_valid_irq(cmos->irq)) + sysfs_remove_bin_file(&dev->kobj, &nvram); + + if (is_valid_irq(cmos->irq)) { free_irq(cmos->irq, cmos->rtc); + hpet_unregister_irq_handler(cmos_interrupt); + } rtc_device_unregister(cmos->rtc); cmos->rtc = NULL; @@ -659,9 +829,12 @@ static int cmos_resume(struct device *dev) /*----------------------------------------------------------------*/ -/* The "CMOS" RTC normally lives on the platform_bus. On ACPI systems, - * the device node will always be created as a PNPACPI device. Plus - * pre-ACPI PCs probably list it in the PNPBIOS tables. +/* On non-x86 systems, a "CMOS" RTC lives most naturally on platform_bus. + * ACPI systems always list these as PNPACPI devices, and pre-ACPI PCs + * probably list them in similar PNPBIOS tables; so PNP is more common. + * + * We don't use legacy "poke at the hardware" probing. Ancient PCs that + * predate even PNPBIOS should set up platform_bus devices. */ #ifdef CONFIG_PNP diff --git a/drivers/rtc/rtc-dev.c b/drivers/rtc/rtc-dev.c index 025c60a17a4..90dfa0df747 100644 --- a/drivers/rtc/rtc-dev.c +++ b/drivers/rtc/rtc-dev.c @@ -246,6 +246,15 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file, /* if the driver does not provide the ioctl interface * or if that particular ioctl was not implemented * (-ENOIOCTLCMD), we will try to emulate here. + * + * Drivers *SHOULD NOT* provide ioctl implementations + * for these requests. Instead, provide methods to + * support the following code, so that the RTC's main + * features are accessible without using ioctls. + * + * RTC and alarm times will be in UTC, by preference, + * but dual-booting with MS-Windows implies RTCs must + * use the local wall clock time. */ switch (cmd) { diff --git a/drivers/rtc/rtc-ds1302.c b/drivers/rtc/rtc-ds1302.c new file mode 100644 index 00000000000..7b002ceeaa7 --- /dev/null +++ b/drivers/rtc/rtc-ds1302.c @@ -0,0 +1,262 @@ +/* + * Dallas DS1302 RTC Support + * + * Copyright (C) 2002 David McCullough + * Copyright (C) 2003 - 2007 Paul Mundt + * + * This file is subject to the terms and conditions of the GNU General Public + * License version 2. See the file "COPYING" in the main directory of + * this archive for more details. + */ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/time.h> +#include <linux/rtc.h> +#include <linux/spinlock.h> +#include <linux/io.h> +#include <linux/bcd.h> +#include <asm/rtc.h> + +#define DRV_NAME "rtc-ds1302" +#define DRV_VERSION "0.1.0" + +#define RTC_CMD_READ 0x81 /* Read command */ +#define RTC_CMD_WRITE 0x80 /* Write command */ + +#define RTC_ADDR_RAM0 0x20 /* Address of RAM0 */ +#define RTC_ADDR_TCR 0x08 /* Address of trickle charge register */ +#define RTC_ADDR_YEAR 0x06 /* Address of year register */ +#define RTC_ADDR_DAY 0x05 /* Address of day of week register */ +#define RTC_ADDR_MON 0x04 /* Address of month register */ +#define RTC_ADDR_DATE 0x03 /* Address of day of month register */ +#define RTC_ADDR_HOUR 0x02 /* Address of hour register */ +#define RTC_ADDR_MIN 0x01 /* Address of minute register */ +#define RTC_ADDR_SEC 0x00 /* Address of second register */ + +#define RTC_RESET 0x1000 +#define RTC_IODATA 0x0800 +#define RTC_SCLK 0x0400 + +#ifdef CONFIG_SH_SECUREEDGE5410 +#include <asm/snapgear.h> +#define set_dp(x) SECUREEDGE_WRITE_IOPORT(x, 0x1c00) +#define get_dp() SECUREEDGE_READ_IOPORT() +#else +#error "Add support for your platform" +#endif + +struct ds1302_rtc { + struct rtc_device *rtc_dev; + spinlock_t lock; +}; + +static void ds1302_sendbits(unsigned int val) +{ + int i; + + for (i = 8; (i); i--, val >>= 1) { + set_dp((get_dp() & ~RTC_IODATA) | ((val & 0x1) ? + RTC_IODATA : 0)); + set_dp(get_dp() | RTC_SCLK); /* clock high */ + set_dp(get_dp() & ~RTC_SCLK); /* clock low */ + } +} + +static unsigned int ds1302_recvbits(void) +{ + unsigned int val; + int i; + + for (i = 0, val = 0; (i < 8); i++) { + val |= (((get_dp() & RTC_IODATA) ? 1 : 0) << i); + set_dp(get_dp() | RTC_SCLK); /* clock high */ + set_dp(get_dp() & ~RTC_SCLK); /* clock low */ + } + + return val; +} + +static unsigned int ds1302_readbyte(unsigned int addr) +{ + unsigned int val; + + set_dp(get_dp() & ~(RTC_RESET | RTC_IODATA | RTC_SCLK)); + + set_dp(get_dp() | RTC_RESET); + ds1302_sendbits(((addr & 0x3f) << 1) | RTC_CMD_READ); + val = ds1302_recvbits(); + set_dp(get_dp() & ~RTC_RESET); + + return val; +} + +static void ds1302_writebyte(unsigned int addr, unsigned int val) +{ + set_dp(get_dp() & ~(RTC_RESET | RTC_IODATA | RTC_SCLK)); + set_dp(get_dp() | RTC_RESET); + ds1302_sendbits(((addr & 0x3f) << 1) | RTC_CMD_WRITE); + ds1302_sendbits(val); + set_dp(get_dp() & ~RTC_RESET); +} + +static int ds1302_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct ds1302_rtc *rtc = dev_get_drvdata(dev); + + spin_lock_irq(&rtc->lock); + + tm->tm_sec = BCD2BIN(ds1302_readbyte(RTC_ADDR_SEC)); + tm->tm_min = BCD2BIN(ds1302_readbyte(RTC_ADDR_MIN)); + tm->tm_hour = BCD2BIN(ds1302_readbyte(RTC_ADDR_HOUR)); + tm->tm_wday = BCD2BIN(ds1302_readbyte(RTC_ADDR_DAY)); + tm->tm_mday = BCD2BIN(ds1302_readbyte(RTC_ADDR_DATE)); + tm->tm_mon = BCD2BIN(ds1302_readbyte(RTC_ADDR_MON)) - 1; + tm->tm_year = BCD2BIN(ds1302_readbyte(RTC_ADDR_YEAR)); + + if (tm->tm_year < 70) + tm->tm_year += 100; + + spin_unlock_irq(&rtc->lock); + + dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, " + "mday=%d, mon=%d, year=%d, wday=%d\n", + __FUNCTION__, + tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon + 1, tm->tm_year, tm->tm_wday); + + if (rtc_valid_tm(tm) < 0) + dev_err(dev, "invalid date\n"); + + return 0; +} + +static int ds1302_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct ds1302_rtc *rtc = dev_get_drvdata(dev); + + spin_lock_irq(&rtc->lock); + + /* Stop RTC */ + ds1302_writebyte(RTC_ADDR_SEC, ds1302_readbyte(RTC_ADDR_SEC) | 0x80); + + ds1302_writebyte(RTC_ADDR_SEC, BIN2BCD(tm->tm_sec)); + ds1302_writebyte(RTC_ADDR_MIN, BIN2BCD(tm->tm_min)); + ds1302_writebyte(RTC_ADDR_HOUR, BIN2BCD(tm->tm_hour)); + ds1302_writebyte(RTC_ADDR_DAY, BIN2BCD(tm->tm_wday)); + ds1302_writebyte(RTC_ADDR_DATE, BIN2BCD(tm->tm_mday)); + ds1302_writebyte(RTC_ADDR_MON, BIN2BCD(tm->tm_mon + 1)); + ds1302_writebyte(RTC_ADDR_YEAR, BIN2BCD(tm->tm_year % 100)); + + /* Start RTC */ + ds1302_writebyte(RTC_ADDR_SEC, ds1302_readbyte(RTC_ADDR_SEC) & ~0x80); + + spin_unlock_irq(&rtc->lock); + + return 0; +} + +static int ds1302_rtc_ioctl(struct device *dev, unsigned int cmd, + unsigned long arg) +{ + switch (cmd) { +#ifdef RTC_SET_CHARGE + case RTC_SET_CHARGE: + { + struct ds1302_rtc *rtc = dev_get_drvdata(dev); + int tcs_val; + + if (copy_from_user(&tcs_val, (int __user *)arg, sizeof(int))) + return -EFAULT; + + spin_lock_irq(&rtc->lock); + ds1302_writebyte(RTC_ADDR_TCR, (0xa0 | tcs_val * 0xf)); + spin_unlock_irq(&rtc->lock); + return 0; + } +#endif + } + + return -ENOIOCTLCMD; +} + +static struct rtc_class_ops ds1302_rtc_ops = { + .read_time = ds1302_rtc_read_time, + .set_time = ds1302_rtc_set_time, + .ioctl = ds1302_rtc_ioctl, +}; + +static int __devinit ds1302_rtc_probe(struct platform_device *pdev) +{ + struct ds1302_rtc *rtc; + int ret; + + /* Reset */ + set_dp(get_dp() & ~(RTC_RESET | RTC_IODATA | RTC_SCLK)); + + /* Write a magic value to the DS1302 RAM, and see if it sticks. */ + ds1302_writebyte(RTC_ADDR_RAM0, 0x42); + if (ds1302_readbyte(RTC_ADDR_RAM0) != 0x42) + return -ENODEV; + + rtc = kzalloc(sizeof(struct ds1302_rtc), GFP_KERNEL); + if (unlikely(!rtc)) + return -ENOMEM; + + spin_lock_init(&rtc->lock); + rtc->rtc_dev = rtc_device_register("ds1302", &pdev->dev, + &ds1302_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc->rtc_dev)) { + ret = PTR_ERR(rtc->rtc_dev); + goto out; + } + + platform_set_drvdata(pdev, rtc); + + return 0; +out: + kfree(rtc); + return ret; +} + +static int __devexit ds1302_rtc_remove(struct platform_device *pdev) +{ + struct ds1302_rtc *rtc = platform_get_drvdata(pdev); + + if (likely(rtc->rtc_dev)) + rtc_device_unregister(rtc->rtc_dev); + + platform_set_drvdata(pdev, NULL); + + kfree(rtc); + + return 0; +} + +static struct platform_driver ds1302_platform_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, + .probe = ds1302_rtc_probe, + .remove = __devexit_p(ds1302_rtc_remove), +}; + +static int __init ds1302_rtc_init(void) +{ + return platform_driver_register(&ds1302_platform_driver); +} + +static void __exit ds1302_rtc_exit(void) +{ + platform_driver_unregister(&ds1302_platform_driver); +} + +module_init(ds1302_rtc_init); +module_exit(ds1302_rtc_exit); + +MODULE_DESCRIPTION("Dallas DS1302 RTC driver"); +MODULE_VERSION(DRV_VERSION); +MODULE_AUTHOR("Paul Mundt, David McCullough"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index bc1c7fe94ad..f389a28720d 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -256,7 +256,7 @@ ds1307_nvram_read(struct kobject *kobj, struct bin_attribute *attr, struct i2c_msg msg[2]; int result; - client = to_i2c_client(container_of(kobj, struct device, kobj)); + client = kobj_to_i2c_client(kobj); ds1307 = i2c_get_clientdata(client); if (unlikely(off >= NVRAM_SIZE)) @@ -294,7 +294,7 @@ ds1307_nvram_write(struct kobject *kobj, struct bin_attribute *attr, u8 buffer[NVRAM_SIZE + 1]; int ret; - client = to_i2c_client(container_of(kobj, struct device, kobj)); + client = kobj_to_i2c_client(kobj); if (unlikely(off >= NVRAM_SIZE)) return -EFBIG; @@ -412,11 +412,6 @@ read_rtc: */ tmp = ds1307->regs[DS1307_REG_SECS]; switch (ds1307->type) { - case ds_1340: - /* FIXME read register with DS1340_BIT_OSF, use that to - * trigger the "set time" warning (*after* restarting the - * oscillator!) instead of this weaker ds1307/m41t00 test. - */ case ds_1307: case m41t00: /* clock halted? turn it on, so clock can tick. */ @@ -440,6 +435,24 @@ read_rtc: goto read_rtc; } break; + case ds_1340: + /* clock halted? turn it on, so clock can tick. */ + if (tmp & DS1340_BIT_nEOSC) + i2c_smbus_write_byte_data(client, DS1307_REG_SECS, 0); + + tmp = i2c_smbus_read_byte_data(client, DS1340_REG_FLAG); + if (tmp < 0) { + pr_debug("read error %d\n", tmp); + err = -EIO; + goto exit_free; + } + + /* oscillator fault? clear flag, and warn */ + if (tmp & DS1340_BIT_OSF) { + i2c_smbus_write_byte_data(client, DS1340_REG_FLAG, 0); + dev_warn(&client->dev, "SET TIME!\n"); + } + break; case ds_1337: case ds_1339: break; diff --git a/drivers/rtc/rtc-ds1511.c b/drivers/rtc/rtc-ds1511.c new file mode 100644 index 00000000000..d74b8086fa3 --- /dev/null +++ b/drivers/rtc/rtc-ds1511.c @@ -0,0 +1,656 @@ +/* + * An rtc driver for the Dallas DS1511 + * + * Copyright (C) 2006 Atsushi Nemoto <anemo@mba.ocn.ne.jp> + * Copyright (C) 2007 Andrew Sharp <andy.sharp@onstor.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. + * + * Real time clock driver for the Dallas 1511 chip, which also + * contains a watchdog timer. There is a tiny amount of code that + * platform code could use to mess with the watchdog device a little + * bit, but not a full watchdog driver. + */ + +#include <linux/bcd.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/rtc.h> +#include <linux/platform_device.h> +#include <linux/io.h> + +#define DRV_VERSION "0.6" + +enum ds1511reg { + DS1511_SEC = 0x0, + DS1511_MIN = 0x1, + DS1511_HOUR = 0x2, + DS1511_DOW = 0x3, + DS1511_DOM = 0x4, + DS1511_MONTH = 0x5, + DS1511_YEAR = 0x6, + DS1511_CENTURY = 0x7, + DS1511_AM1_SEC = 0x8, + DS1511_AM2_MIN = 0x9, + DS1511_AM3_HOUR = 0xa, + DS1511_AM4_DATE = 0xb, + DS1511_WD_MSEC = 0xc, + DS1511_WD_SEC = 0xd, + DS1511_CONTROL_A = 0xe, + DS1511_CONTROL_B = 0xf, + DS1511_RAMADDR_LSB = 0x10, + DS1511_RAMDATA = 0x13 +}; + +#define DS1511_BLF1 0x80 +#define DS1511_BLF2 0x40 +#define DS1511_PRS 0x20 +#define DS1511_PAB 0x10 +#define DS1511_TDF 0x08 +#define DS1511_KSF 0x04 +#define DS1511_WDF 0x02 +#define DS1511_IRQF 0x01 +#define DS1511_TE 0x80 +#define DS1511_CS 0x40 +#define DS1511_BME 0x20 +#define DS1511_TPE 0x10 +#define DS1511_TIE 0x08 +#define DS1511_KIE 0x04 +#define DS1511_WDE 0x02 +#define DS1511_WDS 0x01 +#define DS1511_RAM_MAX 0xff + +#define RTC_CMD DS1511_CONTROL_B +#define RTC_CMD1 DS1511_CONTROL_A + +#define RTC_ALARM_SEC DS1511_AM1_SEC +#define RTC_ALARM_MIN DS1511_AM2_MIN +#define RTC_ALARM_HOUR DS1511_AM3_HOUR +#define RTC_ALARM_DATE DS1511_AM4_DATE + +#define RTC_SEC DS1511_SEC +#define RTC_MIN DS1511_MIN +#define RTC_HOUR DS1511_HOUR +#define RTC_DOW DS1511_DOW +#define RTC_DOM DS1511_DOM +#define RTC_MON DS1511_MONTH +#define RTC_YEAR DS1511_YEAR +#define RTC_CENTURY DS1511_CENTURY + +#define RTC_TIE DS1511_TIE +#define RTC_TE DS1511_TE + +struct rtc_plat_data { + struct rtc_device *rtc; + void __iomem *ioaddr; /* virtual base address */ + unsigned long baseaddr; /* physical base address */ + int size; /* amount of memory mapped */ + int irq; + unsigned int irqen; + int alrm_sec; + int alrm_min; + int alrm_hour; + int alrm_mday; +}; + +static DEFINE_SPINLOCK(ds1511_lock); + +static __iomem char *ds1511_base; +static u32 reg_spacing = 1; + + static noinline void +rtc_write(uint8_t val, uint32_t reg) +{ + writeb(val, ds1511_base + (reg * reg_spacing)); +} + + static inline void +rtc_write_alarm(uint8_t val, enum ds1511reg reg) +{ + rtc_write((val | 0x80), reg); +} + + static noinline uint8_t +rtc_read(enum ds1511reg reg) +{ + return readb(ds1511_base + (reg * reg_spacing)); +} + + static inline void +rtc_disable_update(void) +{ + rtc_write((rtc_read(RTC_CMD) & ~RTC_TE), RTC_CMD); +} + + static void +rtc_enable_update(void) +{ + rtc_write((rtc_read(RTC_CMD) | RTC_TE), RTC_CMD); +} + +/* + * #define DS1511_WDOG_RESET_SUPPORT + * + * Uncomment this if you want to use these routines in + * some platform code. + */ +#ifdef DS1511_WDOG_RESET_SUPPORT +/* + * just enough code to set the watchdog timer so that it + * will reboot the system + */ + void +ds1511_wdog_set(unsigned long deciseconds) +{ + /* + * the wdog timer can take 99.99 seconds + */ + deciseconds %= 10000; + /* + * set the wdog values in the wdog registers + */ + rtc_write(BIN2BCD(deciseconds % 100), DS1511_WD_MSEC); + rtc_write(BIN2BCD(deciseconds / 100), DS1511_WD_SEC); + /* + * set wdog enable and wdog 'steering' bit to issue a reset + */ + rtc_write(DS1511_WDE | DS1511_WDS, RTC_CMD); +} + + void +ds1511_wdog_disable(void) +{ + /* + * clear wdog enable and wdog 'steering' bits + */ + rtc_write(rtc_read(RTC_CMD) & ~(DS1511_WDE | DS1511_WDS), RTC_CMD); + /* + * clear the wdog counter + */ + rtc_write(0, DS1511_WD_MSEC); + rtc_write(0, DS1511_WD_SEC); +} +#endif + +/* + * set the rtc chip's idea of the time. + * stupidly, some callers call with year unmolested; + * and some call with year = year - 1900. thanks. + */ + int +ds1511_rtc_set_time(struct device *dev, struct rtc_time *rtc_tm) +{ + u8 mon, day, dow, hrs, min, sec, yrs, cen; + unsigned int flags; + + /* + * won't have to change this for a while + */ + if (rtc_tm->tm_year < 1900) { + rtc_tm->tm_year += 1900; + } + + if (rtc_tm->tm_year < 1970) { + return -EINVAL; + } + yrs = rtc_tm->tm_year % 100; + cen = rtc_tm->tm_year / 100; + mon = rtc_tm->tm_mon + 1; /* tm_mon starts at zero */ + day = rtc_tm->tm_mday; + dow = rtc_tm->tm_wday & 0x7; /* automatic BCD */ + hrs = rtc_tm->tm_hour; + min = rtc_tm->tm_min; + sec = rtc_tm->tm_sec; + + if ((mon > 12) || (day == 0)) { + return -EINVAL; + } + + if (day > rtc_month_days(rtc_tm->tm_mon, rtc_tm->tm_year)) { + return -EINVAL; + } + + if ((hrs >= 24) || (min >= 60) || (sec >= 60)) { + return -EINVAL; + } + + /* + * each register is a different number of valid bits + */ + sec = BIN2BCD(sec) & 0x7f; + min = BIN2BCD(min) & 0x7f; + hrs = BIN2BCD(hrs) & 0x3f; + day = BIN2BCD(day) & 0x3f; + mon = BIN2BCD(mon) & 0x1f; + yrs = BIN2BCD(yrs) & 0xff; + cen = BIN2BCD(cen) & 0xff; + + spin_lock_irqsave(&ds1511_lock, flags); + rtc_disable_update(); + rtc_write(cen, RTC_CENTURY); + rtc_write(yrs, RTC_YEAR); + rtc_write((rtc_read(RTC_MON) & 0xe0) | mon, RTC_MON); + rtc_write(day, RTC_DOM); + rtc_write(hrs, RTC_HOUR); + rtc_write(min, RTC_MIN); + rtc_write(sec, RTC_SEC); + rtc_write(dow, RTC_DOW); + rtc_enable_update(); + spin_unlock_irqrestore(&ds1511_lock, flags); + + return 0; +} + + int +ds1511_rtc_read_time(struct device *dev, struct rtc_time *rtc_tm) +{ + unsigned int century; + unsigned int flags; + + spin_lock_irqsave(&ds1511_lock, flags); + rtc_disable_update(); + + rtc_tm->tm_sec = rtc_read(RTC_SEC) & 0x7f; + rtc_tm->tm_min = rtc_read(RTC_MIN) & 0x7f; + rtc_tm->tm_hour = rtc_read(RTC_HOUR) & 0x3f; + rtc_tm->tm_mday = rtc_read(RTC_DOM) & 0x3f; + rtc_tm->tm_wday = rtc_read(RTC_DOW) & 0x7; + rtc_tm->tm_mon = rtc_read(RTC_MON) & 0x1f; + rtc_tm->tm_year = rtc_read(RTC_YEAR) & 0x7f; + century = rtc_read(RTC_CENTURY); + + rtc_enable_update(); + spin_unlock_irqrestore(&ds1511_lock, flags); + + rtc_tm->tm_sec = BCD2BIN(rtc_tm->tm_sec); + rtc_tm->tm_min = BCD2BIN(rtc_tm->tm_min); + rtc_tm->tm_hour = BCD2BIN(rtc_tm->tm_hour); + rtc_tm->tm_mday = BCD2BIN(rtc_tm->tm_mday); + rtc_tm->tm_wday = BCD2BIN(rtc_tm->tm_wday); + rtc_tm->tm_mon = BCD2BIN(rtc_tm->tm_mon); + rtc_tm->tm_year = BCD2BIN(rtc_tm->tm_year); + century = BCD2BIN(century) * 100; + + /* + * Account for differences between how the RTC uses the values + * and how they are defined in a struct rtc_time; + */ + century += rtc_tm->tm_year; + rtc_tm->tm_year = century - 1900; + + rtc_tm->tm_mon--; + + if (rtc_valid_tm(rtc_tm) < 0) { + dev_err(dev, "retrieved date/time is not valid.\n"); + rtc_time_to_tm(0, rtc_tm); + } + return 0; +} + +/* + * write the alarm register settings + * + * we only have the use to interrupt every second, otherwise + * known as the update interrupt, or the interrupt if the whole + * date/hours/mins/secs matches. the ds1511 has many more + * permutations, but the kernel doesn't. + */ + static void +ds1511_rtc_update_alarm(struct rtc_plat_data *pdata) +{ + unsigned long flags; + + spin_lock_irqsave(&pdata->rtc->irq_lock, flags); + rtc_write(pdata->alrm_mday < 0 || (pdata->irqen & RTC_UF) ? + 0x80 : BIN2BCD(pdata->alrm_mday) & 0x3f, + RTC_ALARM_DATE); + rtc_write(pdata->alrm_hour < 0 || (pdata->irqen & RTC_UF) ? + 0x80 : BIN2BCD(pdata->alrm_hour) & 0x3f, + RTC_ALARM_HOUR); + rtc_write(pdata->alrm_min < 0 || (pdata->irqen & RTC_UF) ? + 0x80 : BIN2BCD(pdata->alrm_min) & 0x7f, + RTC_ALARM_MIN); + rtc_write(pdata->alrm_sec < 0 || (pdata->irqen & RTC_UF) ? + 0x80 : BIN2BCD(pdata->alrm_sec) & 0x7f, + RTC_ALARM_SEC); + rtc_write(rtc_read(RTC_CMD) | (pdata->irqen ? RTC_TIE : 0), RTC_CMD); + rtc_read(RTC_CMD1); /* clear interrupts */ + spin_unlock_irqrestore(&pdata->rtc->irq_lock, flags); +} + + static int +ds1511_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + + if (pdata->irq < 0) { + return -EINVAL; + } + pdata->alrm_mday = alrm->time.tm_mday; + pdata->alrm_hour = alrm->time.tm_hour; + pdata->alrm_min = alrm->time.tm_min; + pdata->alrm_sec = alrm->time.tm_sec; + if (alrm->enabled) { + pdata->irqen |= RTC_AF; + } + ds1511_rtc_update_alarm(pdata); + return 0; +} + + static int +ds1511_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + + if (pdata->irq < 0) { + return -EINVAL; + } + alrm->time.tm_mday = pdata->alrm_mday < 0 ? 0 : pdata->alrm_mday; + alrm->time.tm_hour = pdata->alrm_hour < 0 ? 0 : pdata->alrm_hour; + alrm->time.tm_min = pdata->alrm_min < 0 ? 0 : pdata->alrm_min; + alrm->time.tm_sec = pdata->alrm_sec < 0 ? 0 : pdata->alrm_sec; + alrm->enabled = (pdata->irqen & RTC_AF) ? 1 : 0; + return 0; +} + + static irqreturn_t +ds1511_interrupt(int irq, void *dev_id) +{ + struct platform_device *pdev = dev_id; + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + unsigned long events = RTC_IRQF; + + /* + * read and clear interrupt + */ + if (!(rtc_read(RTC_CMD1) & DS1511_IRQF)) { + return IRQ_NONE; + } + if (rtc_read(RTC_ALARM_SEC) & 0x80) { + events |= RTC_UF; + } else { + events |= RTC_AF; + } + rtc_update_irq(pdata->rtc, 1, events); + return IRQ_HANDLED; +} + + static void +ds1511_rtc_release(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + + if (pdata->irq >= 0) { + pdata->irqen = 0; + ds1511_rtc_update_alarm(pdata); + } +} + + static int +ds1511_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + + if (pdata->irq < 0) { + return -ENOIOCTLCMD; /* fall back into rtc-dev's emulation */ + } + switch (cmd) { + case RTC_AIE_OFF: + pdata->irqen &= ~RTC_AF; + ds1511_rtc_update_alarm(pdata); + break; + case RTC_AIE_ON: + pdata->irqen |= RTC_AF; + ds1511_rtc_update_alarm(pdata); + break; + case RTC_UIE_OFF: + pdata->irqen &= ~RTC_UF; + ds1511_rtc_update_alarm(pdata); + break; + case RTC_UIE_ON: + pdata->irqen |= RTC_UF; + ds1511_rtc_update_alarm(pdata); + break; + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static const struct rtc_class_ops ds1511_rtc_ops = { + .read_time = ds1511_rtc_read_time, + .set_time = ds1511_rtc_set_time, + .read_alarm = ds1511_rtc_read_alarm, + .set_alarm = ds1511_rtc_set_alarm, + .release = ds1511_rtc_release, + .ioctl = ds1511_rtc_ioctl, +}; + + static ssize_t +ds1511_nvram_read(struct kobject *kobj, struct bin_attribute *ba, + char *buf, loff_t pos, size_t size) +{ + ssize_t count; + + /* + * if count is more than one, turn on "burst" mode + * turn it off when you're done + */ + if (size > 1) { + rtc_write((rtc_read(RTC_CMD) | DS1511_BME), RTC_CMD); + } + if (pos > DS1511_RAM_MAX) { + pos = DS1511_RAM_MAX; + } + if (size + pos > DS1511_RAM_MAX + 1) { + size = DS1511_RAM_MAX - pos + 1; + } + rtc_write(pos, DS1511_RAMADDR_LSB); + for (count = 0; size > 0; count++, size--) { + *buf++ = rtc_read(DS1511_RAMDATA); + } + if (count > 1) { + rtc_write((rtc_read(RTC_CMD) & ~DS1511_BME), RTC_CMD); + } + return count; +} + + static ssize_t +ds1511_nvram_write(struct kobject *kobj, struct bin_attribute *bin_attr, + char *buf, loff_t pos, size_t size) +{ + ssize_t count; + + /* + * if count is more than one, turn on "burst" mode + * turn it off when you're done + */ + if (size > 1) { + rtc_write((rtc_read(RTC_CMD) | DS1511_BME), RTC_CMD); + } + if (pos > DS1511_RAM_MAX) { + pos = DS1511_RAM_MAX; + } + if (size + pos > DS1511_RAM_MAX + 1) { + size = DS1511_RAM_MAX - pos + 1; + } + rtc_write(pos, DS1511_RAMADDR_LSB); + for (count = 0; size > 0; count++, size--) { + rtc_write(*buf++, DS1511_RAMDATA); + } + if (count > 1) { + rtc_write((rtc_read(RTC_CMD) & ~DS1511_BME), RTC_CMD); + } + return count; +} + +static struct bin_attribute ds1511_nvram_attr = { + .attr = { + .name = "nvram", + .mode = S_IRUGO | S_IWUGO, + .owner = THIS_MODULE, + }, + .size = DS1511_RAM_MAX, + .read = ds1511_nvram_read, + .write = ds1511_nvram_write, +}; + + static int __devinit +ds1511_rtc_probe(struct platform_device *pdev) +{ + struct rtc_device *rtc; + struct resource *res; + struct rtc_plat_data *pdata = NULL; + int ret = 0; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + return -ENODEV; + } + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + return -ENOMEM; + } + pdata->irq = -1; + pdata->size = res->end - res->start + 1; + if (!request_mem_region(res->start, pdata->size, pdev->name)) { + ret = -EBUSY; + goto out; + } + pdata->baseaddr = res->start; + pdata->size = pdata->size; + ds1511_base = ioremap(pdata->baseaddr, pdata->size); + if (!ds1511_base) { + ret = -ENOMEM; + goto out; + } + pdata->ioaddr = ds1511_base; + pdata->irq = platform_get_irq(pdev, 0); + + /* + * turn on the clock and the crystal, etc. + */ + rtc_write(0, RTC_CMD); + rtc_write(0, RTC_CMD1); + /* + * clear the wdog counter + */ + rtc_write(0, DS1511_WD_MSEC); + rtc_write(0, DS1511_WD_SEC); + /* + * start the clock + */ + rtc_enable_update(); + + /* + * check for a dying bat-tree + */ + if (rtc_read(RTC_CMD1) & DS1511_BLF1) { + dev_warn(&pdev->dev, "voltage-low detected.\n"); + } + + /* + * if the platform has an interrupt in mind for this device, + * then by all means, set it + */ + if (pdata->irq >= 0) { + rtc_read(RTC_CMD1); + if (request_irq(pdata->irq, ds1511_interrupt, + IRQF_DISABLED | IRQF_SHARED, pdev->name, pdev) < 0) { + + dev_warn(&pdev->dev, "interrupt not available.\n"); + pdata->irq = -1; + } + } + + rtc = rtc_device_register(pdev->name, &pdev->dev, &ds1511_rtc_ops, + THIS_MODULE); + if (IS_ERR(rtc)) { + ret = PTR_ERR(rtc); + goto out; + } + pdata->rtc = rtc; + platform_set_drvdata(pdev, pdata); + ret = sysfs_create_bin_file(&pdev->dev.kobj, &ds1511_nvram_attr); + if (ret) { + goto out; + } + return 0; + out: + if (pdata->rtc) { + rtc_device_unregister(pdata->rtc); + } + if (pdata->irq >= 0) { + free_irq(pdata->irq, pdev); + } + if (ds1511_base) { + iounmap(ds1511_base); + ds1511_base = NULL; + } + if (pdata->baseaddr) { + release_mem_region(pdata->baseaddr, pdata->size); + } + + kfree(pdata); + return ret; +} + + static int __devexit +ds1511_rtc_remove(struct platform_device *pdev) +{ + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + + sysfs_remove_bin_file(&pdev->dev.kobj, &ds1511_nvram_attr); + rtc_device_unregister(pdata->rtc); + pdata->rtc = NULL; + if (pdata->irq >= 0) { + /* + * disable the alarm interrupt + */ + rtc_write(rtc_read(RTC_CMD) & ~RTC_TIE, RTC_CMD); + rtc_read(RTC_CMD1); + free_irq(pdata->irq, pdev); + } + iounmap(pdata->ioaddr); + ds1511_base = NULL; + release_mem_region(pdata->baseaddr, pdata->size); + kfree(pdata); + return 0; +} + +static struct platform_driver ds1511_rtc_driver = { + .probe = ds1511_rtc_probe, + .remove = __devexit_p(ds1511_rtc_remove), + .driver = { + .name = "ds1511", + .owner = THIS_MODULE, + }, +}; + + static int __init +ds1511_rtc_init(void) +{ + return platform_driver_register(&ds1511_rtc_driver); +} + + static void __exit +ds1511_rtc_exit(void) +{ + return platform_driver_unregister(&ds1511_rtc_driver); +} + +module_init(ds1511_rtc_init); +module_exit(ds1511_rtc_exit); + +MODULE_AUTHOR("Andrew Sharp <andy.sharp@onstor.com>"); +MODULE_DESCRIPTION("Dallas DS1511 RTC driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); diff --git a/drivers/rtc/rtc-pcf8583.c b/drivers/rtc/rtc-pcf8583.c index c973ba94c42..8b399700750 100644 --- a/drivers/rtc/rtc-pcf8583.c +++ b/drivers/rtc/rtc-pcf8583.c @@ -163,27 +163,17 @@ static int pcf8583_read_mem(struct i2c_client *client, struct rtc_mem *mem) static int pcf8583_write_mem(struct i2c_client *client, struct rtc_mem *mem) { - unsigned char addr[1]; - struct i2c_msg msgs[2] = { - { - .addr = client->addr, - .flags = 0, - .len = 1, - .buf = addr, - }, { - .addr = client->addr, - .flags = I2C_M_NOSTART, - .len = mem->nr, - .buf = mem->data, - } - }; + unsigned char buf[9]; + int ret; - if (mem->loc < 8) + if (mem->loc < 8 || mem->nr > 8) return -EINVAL; - addr[0] = mem->loc; + buf[0] = mem->loc; + memcpy(buf + 1, mem->data, mem->nr); - return i2c_transfer(client->adapter, msgs, 2) == 2 ? 0 : -EIO; + ret = i2c_master_send(client, buf, mem->nr + 1); + return ret == mem->nr + 1 ? 0 : -EIO; } static int pcf8583_rtc_read_time(struct device *dev, struct rtc_time *tm) diff --git a/drivers/rtc/rtc-r9701.c b/drivers/rtc/rtc-r9701.c new file mode 100644 index 00000000000..a64626a82d0 --- /dev/null +++ b/drivers/rtc/rtc-r9701.c @@ -0,0 +1,178 @@ +/* + * Driver for Epson RTC-9701JE + * + * Copyright (C) 2008 Magnus Damm + * + * Based on rtc-max6902.c + * + * Copyright (C) 2006 8D Technologies inc. + * Copyright (C) 2004 Compulab Ltd. + * + * 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 <linux/module.h> +#include <linux/version.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/device.h> +#include <linux/init.h> +#include <linux/rtc.h> +#include <linux/spi/spi.h> +#include <linux/bcd.h> +#include <linux/delay.h> +#include <linux/bitops.h> + +#define RSECCNT 0x00 /* Second Counter */ +#define RMINCNT 0x01 /* Minute Counter */ +#define RHRCNT 0x02 /* Hour Counter */ +#define RWKCNT 0x03 /* Week Counter */ +#define RDAYCNT 0x04 /* Day Counter */ +#define RMONCNT 0x05 /* Month Counter */ +#define RYRCNT 0x06 /* Year Counter */ +#define R100CNT 0x07 /* Y100 Counter */ +#define RMINAR 0x08 /* Minute Alarm */ +#define RHRAR 0x09 /* Hour Alarm */ +#define RWKAR 0x0a /* Week/Day Alarm */ +#define RTIMCNT 0x0c /* Interval Timer */ +#define REXT 0x0d /* Extension Register */ +#define RFLAG 0x0e /* RTC Flag Register */ +#define RCR 0x0f /* RTC Control Register */ + +static int write_reg(struct device *dev, int address, unsigned char data) +{ + struct spi_device *spi = to_spi_device(dev); + unsigned char buf[2]; + + buf[0] = address & 0x7f; + buf[1] = data; + + return spi_write(spi, buf, ARRAY_SIZE(buf)); +} + +static int read_regs(struct device *dev, unsigned char *regs, int no_regs) +{ + struct spi_device *spi = to_spi_device(dev); + u8 txbuf[1], rxbuf[1]; + int k, ret; + + ret = 0; + + for (k = 0; ret == 0 && k < no_regs; k++) { + txbuf[0] = 0x80 | regs[k]; + ret = spi_write_then_read(spi, txbuf, 1, rxbuf, 1); + regs[k] = rxbuf[0]; + } + + return ret; +} + +static int r9701_get_datetime(struct device *dev, struct rtc_time *dt) +{ + unsigned long time; + int ret; + unsigned char buf[] = { RSECCNT, RMINCNT, RHRCNT, + RDAYCNT, RMONCNT, RYRCNT }; + + ret = read_regs(dev, buf, ARRAY_SIZE(buf)); + if (ret) + return ret; + + memset(dt, 0, sizeof(*dt)); + + dt->tm_sec = BCD2BIN(buf[0]); /* RSECCNT */ + dt->tm_min = BCD2BIN(buf[1]); /* RMINCNT */ + dt->tm_hour = BCD2BIN(buf[2]); /* RHRCNT */ + + dt->tm_mday = BCD2BIN(buf[3]); /* RDAYCNT */ + dt->tm_mon = BCD2BIN(buf[4]) - 1; /* RMONCNT */ + dt->tm_year = BCD2BIN(buf[5]) + 100; /* RYRCNT */ + + /* the rtc device may contain illegal values on power up + * according to the data sheet. make sure they are valid. + */ + + return rtc_valid_tm(dt); +} + +static int r9701_set_datetime(struct device *dev, struct rtc_time *dt) +{ + int ret, year; + + year = dt->tm_year + 1900; + if (year >= 2100 || year < 2000) + return -EINVAL; + + ret = write_reg(dev, RHRCNT, BIN2BCD(dt->tm_hour)); + ret = ret ? ret : write_reg(dev, RMINCNT, BIN2BCD(dt->tm_min)); + ret = ret ? ret : write_reg(dev, RSECCNT, BIN2BCD(dt->tm_sec)); + ret = ret ? ret : write_reg(dev, RDAYCNT, BIN2BCD(dt->tm_mday)); + ret = ret ? ret : write_reg(dev, RMONCNT, BIN2BCD(dt->tm_mon + 1)); + ret = ret ? ret : write_reg(dev, RYRCNT, BIN2BCD(dt->tm_year - 100)); + ret = ret ? ret : write_reg(dev, RWKCNT, 1 << dt->tm_wday); + + return ret; +} + +static const struct rtc_class_ops r9701_rtc_ops = { + .read_time = r9701_get_datetime, + .set_time = r9701_set_datetime, +}; + +static int __devinit r9701_probe(struct spi_device *spi) +{ + struct rtc_device *rtc; + unsigned char tmp; + int res; + + rtc = rtc_device_register("r9701", + &spi->dev, &r9701_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + dev_set_drvdata(&spi->dev, rtc); + + tmp = R100CNT; + res = read_regs(&spi->dev, &tmp, 1); + if (res || tmp != 0x20) { + rtc_device_unregister(rtc); + return res; + } + + return 0; +} + +static int __devexit r9701_remove(struct spi_device *spi) +{ + struct rtc_device *rtc = dev_get_drvdata(&spi->dev); + + rtc_device_unregister(rtc); + return 0; +} + +static struct spi_driver r9701_driver = { + .driver = { + .name = "rtc-r9701", + .owner = THIS_MODULE, + }, + .probe = r9701_probe, + .remove = __devexit_p(r9701_remove), +}; + +static __init int r9701_init(void) +{ + return spi_register_driver(&r9701_driver); +} +module_init(r9701_init); + +static __exit void r9701_exit(void) +{ + spi_unregister_driver(&r9701_driver); +} +module_exit(r9701_exit); + +MODULE_DESCRIPTION("r9701 spi RTC driver"); +MODULE_AUTHOR("Magnus Damm <damm@opensource.se>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index e2041b4d0c8..86766f1f249 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -20,6 +20,7 @@ #include <linux/rtc.h> #include <linux/bcd.h> #include <linux/clk.h> +#include <linux/log2.h> #include <asm/hardware.h> #include <asm/uaccess.h> @@ -309,9 +310,7 @@ static int s3c_rtc_ioctl(struct device *dev, break; case RTC_IRQP_SET: - /* check for power of 2 */ - - if ((arg & (arg-1)) != 0 || arg < 1) { + if (!is_power_of_2(arg)) { ret = -EINVAL; goto exit; } diff --git a/drivers/rtc/rtc-sa1100.c b/drivers/rtc/rtc-sa1100.c index 2eb38520f0c..ee253cc45de 100644 --- a/drivers/rtc/rtc-sa1100.c +++ b/drivers/rtc/rtc-sa1100.c @@ -357,23 +357,15 @@ static int sa1100_rtc_remove(struct platform_device *pdev) #ifdef CONFIG_PM static int sa1100_rtc_suspend(struct platform_device *pdev, pm_message_t state) { - if (pdev->dev.power.power_state.event != state.event) { - if (state.event == PM_EVENT_SUSPEND && - device_may_wakeup(&pdev->dev)) - enable_irq_wake(IRQ_RTCAlrm); - - pdev->dev.power.power_state = state; - } + if (device_may_wakeup(&pdev->dev)) + enable_irq_wake(IRQ_RTCAlrm); return 0; } static int sa1100_rtc_resume(struct platform_device *pdev) { - if (pdev->dev.power.power_state.event != PM_EVENT_ON) { - if (device_may_wakeup(&pdev->dev)) - disable_irq_wake(IRQ_RTCAlrm); - pdev->dev.power.power_state = PMSG_ON; - } + if (device_may_wakeup(&pdev->dev)) + disable_irq_wake(IRQ_RTCAlrm); return 0; } #else diff --git a/drivers/rtc/rtc-sysfs.c b/drivers/rtc/rtc-sysfs.c index 2ae0e8304d3..4d27ccc4fc0 100644 --- a/drivers/rtc/rtc-sysfs.c +++ b/drivers/rtc/rtc-sysfs.c @@ -17,6 +17,13 @@ /* device attributes */ +/* + * NOTE: RTC times displayed in sysfs use the RTC's timezone. That's + * ideally UTC. However, PCs that also boot to MS-Windows normally use + * the local time and change to match daylight savings time. That affects + * attributes including date, time, since_epoch, and wakealarm. + */ + static ssize_t rtc_sysfs_show_name(struct device *dev, struct device_attribute *attr, char *buf) @@ -113,13 +120,13 @@ rtc_sysfs_show_wakealarm(struct device *dev, struct device_attribute *attr, unsigned long alarm; struct rtc_wkalrm alm; - /* Don't show disabled alarms; but the RTC could leave the - * alarm enabled after it's already triggered. Alarms are - * conceptually one-shot, even though some common hardware - * (PCs) doesn't actually work that way. + /* Don't show disabled alarms. For uniformity, RTC alarms are + * conceptually one-shot, even though some common RTCs (on PCs) + * don't actually work that way. * - * REVISIT maybe we should require RTC implementations to - * disable the RTC alarm after it triggers, for uniformity. + * NOTE: RTC implementations where the alarm doesn't match an + * exact YYYY-MM-DD HH:MM[:SS] date *must* disable their RTC + * alarms after they trigger, to ensure one-shot semantics. */ retval = rtc_read_alarm(to_rtc_device(dev), &alm); if (retval == 0 && alm.enabled) { diff --git a/drivers/s390/sysinfo.c b/drivers/s390/sysinfo.c index 19343f9675c..291ff6235fe 100644 --- a/drivers/s390/sysinfo.c +++ b/drivers/s390/sysinfo.c @@ -422,7 +422,7 @@ void s390_adjust_jiffies(void) /* * calibrate the delay loop */ -void __init calibrate_delay(void) +void __cpuinit calibrate_delay(void) { s390_adjust_jiffies(); /* Print the good old Bogomips line .. */ diff --git a/drivers/scsi/a2091.c b/drivers/scsi/a2091.c index 23f27c9c989..5ac3a3e8dfa 100644 --- a/drivers/scsi/a2091.c +++ b/drivers/scsi/a2091.c @@ -46,8 +46,7 @@ static int dma_setup(struct scsi_cmnd *cmd, int dir_in) struct Scsi_Host *instance = cmd->device->host; /* don't allow DMA if the physical address is bad */ - if (addr & A2091_XFER_MASK || - (!dir_in && mm_end_of_chunk (addr, cmd->SCp.this_residual))) + if (addr & A2091_XFER_MASK) { HDATA(instance)->dma_bounce_len = (cmd->SCp.this_residual + 511) & ~0x1ff; diff --git a/drivers/scsi/a3000.c b/drivers/scsi/a3000.c index d7255c8bf28..3aeec963940 100644 --- a/drivers/scsi/a3000.c +++ b/drivers/scsi/a3000.c @@ -54,8 +54,7 @@ static int dma_setup(struct scsi_cmnd *cmd, int dir_in) * end of a physical memory chunk, then allocate a bounce * buffer */ - if (addr & A3000_XFER_MASK || - (!dir_in && mm_end_of_chunk (addr, cmd->SCp.this_residual))) + if (addr & A3000_XFER_MASK) { HDATA(a3000_host)->dma_bounce_len = (cmd->SCp.this_residual + 511) & ~0x1ff; diff --git a/drivers/scsi/aic7xxx_old.c b/drivers/scsi/aic7xxx_old.c index 3bfd9296bbf..93984c9dfe1 100644 --- a/drivers/scsi/aic7xxx_old.c +++ b/drivers/scsi/aic7xxx_old.c @@ -6472,7 +6472,7 @@ do_aic7xxx_isr(int irq, void *dev_id) unsigned long cpu_flags; struct aic7xxx_host *p; - p = (struct aic7xxx_host *)dev_id; + p = dev_id; if(!p) return IRQ_NONE; spin_lock_irqsave(p->host->host_lock, cpu_flags); diff --git a/drivers/scsi/gvp11.c b/drivers/scsi/gvp11.c index 37741e9b5c3..91f85226d08 100644 --- a/drivers/scsi/gvp11.c +++ b/drivers/scsi/gvp11.c @@ -54,8 +54,7 @@ static int dma_setup(struct scsi_cmnd *cmd, int dir_in) static int scsi_alloc_out_of_range = 0; /* use bounce buffer if the physical address is bad */ - if (addr & HDATA(cmd->device->host)->dma_xfer_mask || - (!dir_in && mm_end_of_chunk (addr, cmd->SCp.this_residual))) + if (addr & HDATA(cmd->device->host)->dma_xfer_mask) { HDATA(cmd->device->host)->dma_bounce_len = (cmd->SCp.this_residual + 511) & ~0x1ff; diff --git a/drivers/scsi/ibmvscsi/ibmvstgt.c b/drivers/scsi/ibmvscsi/ibmvstgt.c index d63f11e95ab..bd62131b97a 100644 --- a/drivers/scsi/ibmvscsi/ibmvstgt.c +++ b/drivers/scsi/ibmvscsi/ibmvstgt.c @@ -539,9 +539,9 @@ out: srp_iu_put(iue); } -static irqreturn_t ibmvstgt_interrupt(int irq, void *data) +static irqreturn_t ibmvstgt_interrupt(int dummy, void *data) { - struct srp_target *target = (struct srp_target *) data; + struct srp_target *target = data; struct vio_port *vport = target_to_port(target); vio_disable_interrupts(vport->dma_dev); diff --git a/drivers/scsi/megaraid/megaraid_sas.c b/drivers/scsi/megaraid/megaraid_sas.c index 672c759ac24..77a62a1b12c 100644 --- a/drivers/scsi/megaraid/megaraid_sas.c +++ b/drivers/scsi/megaraid/megaraid_sas.c @@ -31,7 +31,6 @@ #include <linux/moduleparam.h> #include <linux/module.h> #include <linux/spinlock.h> -#include <linux/mutex.h> #include <linux/interrupt.h> #include <linux/delay.h> #include <linux/uio.h> diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 4fa7927997a..6a44fb1dc16 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -877,15 +877,15 @@ config SERIAL_SUNHV systems. Say Y if you want to be able to use this device. config SERIAL_IP22_ZILOG - tristate "IP22 Zilog8530 serial support" - depends on SGI_IP22 + tristate "SGI Zilog8530 serial support" + depends on SGI_HAS_ZILOG select SERIAL_CORE help - This driver supports the Zilog8530 serial ports found on SGI IP22 + This driver supports the Zilog8530 serial ports found on SGI systems. Say Y or M if you want to be able to these serial ports. config SERIAL_IP22_ZILOG_CONSOLE - bool "Console on IP22 Zilog8530 serial port" + bool "Console on SGI Zilog8530 serial port" depends on SERIAL_IP22_ZILOG=y select SERIAL_CORE_CONSOLE @@ -1318,4 +1318,19 @@ config SERIAL_QE This driver supports the QE serial ports on Freescale embedded PowerPC that contain a QUICC Engine. +config SERIAL_SC26XX + tristate "SC2681/SC2692 serial port support" + depends on SNI_RM + select SERIAL_CORE + help + This is a driver for the onboard serial ports of + older RM400 machines. + +config SERIAL_SC26XX_CONSOLE + bool "Console on SC2681/SC2692 serial port" + depends on SERIAL_SC26XX + select SERIAL_CORE_CONSOLE + help + Support for Console on SC2681/SC2692 serial ports. + endmenu diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 2dd41b4cc8d..640cfe44a56 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -55,6 +55,7 @@ obj-$(CONFIG_SERIAL_M32R_SIO) += m32r_sio.o obj-$(CONFIG_SERIAL_MPSC) += mpsc.o obj-$(CONFIG_SERIAL_SB1250_DUART) += sb1250-duart.o obj-$(CONFIG_ETRAX_SERIAL) += crisv10.o +obj-$(CONFIG_SERIAL_SC26XX) += sc26xx.o obj-$(CONFIG_SERIAL_JSM) += jsm/ obj-$(CONFIG_SERIAL_TXX9) += serial_txx9.o obj-$(CONFIG_SERIAL_VR41XX) += vr41xx_siu.o diff --git a/drivers/serial/cpm_uart/cpm_uart_core.c b/drivers/serial/cpm_uart/cpm_uart_core.c index b5e4478de0e..236af9d3385 100644 --- a/drivers/serial/cpm_uart/cpm_uart_core.c +++ b/drivers/serial/cpm_uart/cpm_uart_core.c @@ -380,7 +380,7 @@ static void cpm_uart_int_rx(struct uart_port *port) static irqreturn_t cpm_uart_int(int irq, void *data) { u8 events; - struct uart_port *port = (struct uart_port *)data; + struct uart_port *port = data; struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; smc_t __iomem *smcp = pinfo->smcp; scc_t __iomem *sccp = pinfo->sccp; diff --git a/drivers/serial/dz.c b/drivers/serial/dz.c index d31721f2744..bbae5a22021 100644 --- a/drivers/serial/dz.c +++ b/drivers/serial/dz.c @@ -324,7 +324,7 @@ static inline void check_modem_status(struct dz_port *dport) */ static irqreturn_t dz_interrupt(int irq, void *dev) { - struct dz_port *dport = (struct dz_port *)dev; + struct dz_port *dport = dev; unsigned short status; /* get the reason why we just got an irq */ diff --git a/drivers/serial/imx.c b/drivers/serial/imx.c index dc1967176fe..56af1f566a4 100644 --- a/drivers/serial/imx.c +++ b/drivers/serial/imx.c @@ -308,7 +308,7 @@ static void imx_start_tx(struct uart_port *port) static irqreturn_t imx_rtsint(int irq, void *dev_id) { - struct imx_port *sport = (struct imx_port *)dev_id; + struct imx_port *sport = dev_id; unsigned int val = readl(sport->port.membase + USR1) & USR1_RTSS; unsigned long flags; @@ -324,7 +324,7 @@ static irqreturn_t imx_rtsint(int irq, void *dev_id) static irqreturn_t imx_txint(int irq, void *dev_id) { - struct imx_port *sport = (struct imx_port *)dev_id; + struct imx_port *sport = dev_id; struct circ_buf *xmit = &sport->port.info->xmit; unsigned long flags; diff --git a/drivers/serial/sc26xx.c b/drivers/serial/sc26xx.c new file mode 100644 index 00000000000..a350b6d2a18 --- /dev/null +++ b/drivers/serial/sc26xx.c @@ -0,0 +1,755 @@ +/* + * SC268xx.c: Serial driver for Philiphs SC2681/SC2692 devices. + * + * Copyright (C) 2006,2007 Thomas Bogendörfer (tsbogend@alpha.franken.de) + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/major.h> +#include <linux/circ_buf.h> +#include <linux/serial.h> +#include <linux/sysrq.h> +#include <linux/console.h> +#include <linux/spinlock.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/irq.h> + +#if defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include <linux/serial_core.h> + +#define SC26XX_MAJOR 204 +#define SC26XX_MINOR_START 205 +#define SC26XX_NR 2 + +struct uart_sc26xx_port { + struct uart_port port[2]; + u8 dsr_mask[2]; + u8 cts_mask[2]; + u8 dcd_mask[2]; + u8 ri_mask[2]; + u8 dtr_mask[2]; + u8 rts_mask[2]; + u8 imr; +}; + +/* register common to both ports */ +#define RD_ISR 0x14 +#define RD_IPR 0x34 + +#define WR_ACR 0x10 +#define WR_IMR 0x14 +#define WR_OPCR 0x34 +#define WR_OPR_SET 0x38 +#define WR_OPR_CLR 0x3C + +/* access common register */ +#define READ_SC(p, r) readb((p)->membase + RD_##r) +#define WRITE_SC(p, r, v) writeb((v), (p)->membase + WR_##r) + +/* register per port */ +#define RD_PORT_MRx 0x00 +#define RD_PORT_SR 0x04 +#define RD_PORT_RHR 0x0c + +#define WR_PORT_MRx 0x00 +#define WR_PORT_CSR 0x04 +#define WR_PORT_CR 0x08 +#define WR_PORT_THR 0x0c + +/* SR bits */ +#define SR_BREAK (1 << 7) +#define SR_FRAME (1 << 6) +#define SR_PARITY (1 << 5) +#define SR_OVERRUN (1 << 4) +#define SR_TXRDY (1 << 2) +#define SR_RXRDY (1 << 0) + +#define CR_RES_MR (1 << 4) +#define CR_RES_RX (2 << 4) +#define CR_RES_TX (3 << 4) +#define CR_STRT_BRK (6 << 4) +#define CR_STOP_BRK (7 << 4) +#define CR_DIS_TX (1 << 3) +#define CR_ENA_TX (1 << 2) +#define CR_DIS_RX (1 << 1) +#define CR_ENA_RX (1 << 0) + +/* ISR bits */ +#define ISR_RXRDYB (1 << 5) +#define ISR_TXRDYB (1 << 4) +#define ISR_RXRDYA (1 << 1) +#define ISR_TXRDYA (1 << 0) + +/* IMR bits */ +#define IMR_RXRDY (1 << 1) +#define IMR_TXRDY (1 << 0) + +/* access port register */ +static inline u8 read_sc_port(struct uart_port *p, u8 reg) +{ + return readb(p->membase + p->line * 0x20 + reg); +} + +static inline void write_sc_port(struct uart_port *p, u8 reg, u8 val) +{ + writeb(val, p->membase + p->line * 0x20 + reg); +} + +#define READ_SC_PORT(p, r) read_sc_port(p, RD_PORT_##r) +#define WRITE_SC_PORT(p, r, v) write_sc_port(p, WR_PORT_##r, v) + +static void sc26xx_enable_irq(struct uart_port *port, int mask) +{ + struct uart_sc26xx_port *up; + int line = port->line; + + port -= line; + up = container_of(port, struct uart_sc26xx_port, port[0]); + + up->imr |= mask << (line * 4); + WRITE_SC(port, IMR, up->imr); +} + +static void sc26xx_disable_irq(struct uart_port *port, int mask) +{ + struct uart_sc26xx_port *up; + int line = port->line; + + port -= line; + up = container_of(port, struct uart_sc26xx_port, port[0]); + + up->imr &= ~(mask << (line * 4)); + WRITE_SC(port, IMR, up->imr); +} + +static struct tty_struct *receive_chars(struct uart_port *port) +{ + struct tty_struct *tty = NULL; + int limit = 10000; + unsigned char ch; + char flag; + u8 status; + + if (port->info != NULL) /* Unopened serial console */ + tty = port->info->tty; + + while (limit-- > 0) { + status = READ_SC_PORT(port, SR); + if (!(status & SR_RXRDY)) + break; + ch = READ_SC_PORT(port, RHR); + + flag = TTY_NORMAL; + port->icount.rx++; + + if (unlikely(status & (SR_BREAK | SR_FRAME | + SR_PARITY | SR_OVERRUN))) { + if (status & SR_BREAK) { + status &= ~(SR_PARITY | SR_FRAME); + port->icount.brk++; + if (uart_handle_break(port)) + continue; + } else if (status & SR_PARITY) + port->icount.parity++; + else if (status & SR_FRAME) + port->icount.frame++; + if (status & SR_OVERRUN) + port->icount.overrun++; + + status &= port->read_status_mask; + if (status & SR_BREAK) + flag = TTY_BREAK; + else if (status & SR_PARITY) + flag = TTY_PARITY; + else if (status & SR_FRAME) + flag = TTY_FRAME; + } + + if (uart_handle_sysrq_char(port, ch)) + continue; + + if (status & port->ignore_status_mask) + continue; + + tty_insert_flip_char(tty, ch, flag); + } + return tty; +} + +static void transmit_chars(struct uart_port *port) +{ + struct circ_buf *xmit; + + if (!port->info) + return; + + xmit = &port->info->xmit; + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + sc26xx_disable_irq(port, IMR_TXRDY); + return; + } + while (!uart_circ_empty(xmit)) { + if (!(READ_SC_PORT(port, SR) & SR_TXRDY)) + break; + + WRITE_SC_PORT(port, THR, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + } + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); +} + +static irqreturn_t sc26xx_interrupt(int irq, void *dev_id) +{ + struct uart_sc26xx_port *up = dev_id; + struct tty_struct *tty; + unsigned long flags; + u8 isr; + + spin_lock_irqsave(&up->port[0].lock, flags); + + tty = NULL; + isr = READ_SC(&up->port[0], ISR); + if (isr & ISR_TXRDYA) + transmit_chars(&up->port[0]); + if (isr & ISR_RXRDYA) + tty = receive_chars(&up->port[0]); + + spin_unlock(&up->port[0].lock); + + if (tty) + tty_flip_buffer_push(tty); + + spin_lock(&up->port[1].lock); + + tty = NULL; + if (isr & ISR_TXRDYB) + transmit_chars(&up->port[1]); + if (isr & ISR_RXRDYB) + tty = receive_chars(&up->port[1]); + + spin_unlock_irqrestore(&up->port[1].lock, flags); + + if (tty) + tty_flip_buffer_push(tty); + + return IRQ_HANDLED; +} + +/* port->lock is not held. */ +static unsigned int sc26xx_tx_empty(struct uart_port *port) +{ + return (READ_SC_PORT(port, SR) & SR_TXRDY) ? TIOCSER_TEMT : 0; +} + +/* port->lock held by caller. */ +static void sc26xx_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct uart_sc26xx_port *up; + int line = port->line; + + port -= line; + up = container_of(port, struct uart_sc26xx_port, port[0]); + + if (up->dtr_mask[line]) { + if (mctrl & TIOCM_DTR) + WRITE_SC(port, OPR_SET, up->dtr_mask[line]); + else + WRITE_SC(port, OPR_CLR, up->dtr_mask[line]); + } + if (up->rts_mask[line]) { + if (mctrl & TIOCM_RTS) + WRITE_SC(port, OPR_SET, up->rts_mask[line]); + else + WRITE_SC(port, OPR_CLR, up->rts_mask[line]); + } +} + +/* port->lock is held by caller and interrupts are disabled. */ +static unsigned int sc26xx_get_mctrl(struct uart_port *port) +{ + struct uart_sc26xx_port *up; + int line = port->line; + unsigned int mctrl = TIOCM_DSR | TIOCM_CTS | TIOCM_CAR; + u8 ipr; + + port -= line; + up = container_of(port, struct uart_sc26xx_port, port[0]); + ipr = READ_SC(port, IPR) ^ 0xff; + + if (up->dsr_mask[line]) { + mctrl &= ~TIOCM_DSR; + mctrl |= ipr & up->dsr_mask[line] ? TIOCM_DSR : 0; + } + if (up->cts_mask[line]) { + mctrl &= ~TIOCM_CTS; + mctrl |= ipr & up->cts_mask[line] ? TIOCM_CTS : 0; + } + if (up->dcd_mask[line]) { + mctrl &= ~TIOCM_CAR; + mctrl |= ipr & up->dcd_mask[line] ? TIOCM_CAR : 0; + } + if (up->ri_mask[line]) { + mctrl &= ~TIOCM_RNG; + mctrl |= ipr & up->ri_mask[line] ? TIOCM_RNG : 0; + } + return mctrl; +} + +/* port->lock held by caller. */ +static void sc26xx_stop_tx(struct uart_port *port) +{ + return; +} + +/* port->lock held by caller. */ +static void sc26xx_start_tx(struct uart_port *port) +{ + struct circ_buf *xmit = &port->info->xmit; + + while (!uart_circ_empty(xmit)) { + if (!(READ_SC_PORT(port, SR) & SR_TXRDY)) { + sc26xx_enable_irq(port, IMR_TXRDY); + break; + } + WRITE_SC_PORT(port, THR, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + } +} + +/* port->lock held by caller. */ +static void sc26xx_stop_rx(struct uart_port *port) +{ +} + +/* port->lock held by caller. */ +static void sc26xx_enable_ms(struct uart_port *port) +{ +} + +/* port->lock is not held. */ +static void sc26xx_break_ctl(struct uart_port *port, int break_state) +{ + if (break_state == -1) + WRITE_SC_PORT(port, CR, CR_STRT_BRK); + else + WRITE_SC_PORT(port, CR, CR_STOP_BRK); +} + +/* port->lock is not held. */ +static int sc26xx_startup(struct uart_port *port) +{ + sc26xx_disable_irq(port, IMR_TXRDY | IMR_RXRDY); + WRITE_SC(port, OPCR, 0); + + /* reset tx and rx */ + WRITE_SC_PORT(port, CR, CR_RES_RX); + WRITE_SC_PORT(port, CR, CR_RES_TX); + + /* start rx/tx */ + WRITE_SC_PORT(port, CR, CR_ENA_TX | CR_ENA_RX); + + /* enable irqs */ + sc26xx_enable_irq(port, IMR_RXRDY); + return 0; +} + +/* port->lock is not held. */ +static void sc26xx_shutdown(struct uart_port *port) +{ + /* disable interrupst */ + sc26xx_disable_irq(port, IMR_TXRDY | IMR_RXRDY); + + /* stop tx/rx */ + WRITE_SC_PORT(port, CR, CR_DIS_TX | CR_DIS_RX); +} + +/* port->lock is not held. */ +static void sc26xx_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + unsigned int baud = uart_get_baud_rate(port, termios, old, 0, 4000000); + unsigned int quot = uart_get_divisor(port, baud); + unsigned int iflag, cflag; + unsigned long flags; + u8 mr1, mr2, csr; + + spin_lock_irqsave(&port->lock, flags); + + while ((READ_SC_PORT(port, SR) & ((1 << 3) | (1 << 2))) != 0xc) + udelay(2); + + WRITE_SC_PORT(port, CR, CR_DIS_TX | CR_DIS_RX); + + iflag = termios->c_iflag; + cflag = termios->c_cflag; + + port->read_status_mask = SR_OVERRUN; + if (iflag & INPCK) + port->read_status_mask |= SR_PARITY | SR_FRAME; + if (iflag & (BRKINT | PARMRK)) + port->read_status_mask |= SR_BREAK; + + port->ignore_status_mask = 0; + if (iflag & IGNBRK) + port->ignore_status_mask |= SR_BREAK; + if ((cflag & CREAD) == 0) + port->ignore_status_mask |= SR_BREAK | SR_FRAME | + SR_PARITY | SR_OVERRUN; + + switch (cflag & CSIZE) { + case CS5: + mr1 = 0x00; + break; + case CS6: + mr1 = 0x01; + break; + case CS7: + mr1 = 0x02; + break; + default: + case CS8: + mr1 = 0x03; + break; + } + mr2 = 0x07; + if (cflag & CSTOPB) + mr2 = 0x0f; + if (cflag & PARENB) { + if (cflag & PARODD) + mr1 |= (1 << 2); + } else + mr1 |= (2 << 3); + + switch (baud) { + case 50: + csr = 0x00; + break; + case 110: + csr = 0x11; + break; + case 134: + csr = 0x22; + break; + case 200: + csr = 0x33; + break; + case 300: + csr = 0x44; + break; + case 600: + csr = 0x55; + break; + case 1200: + csr = 0x66; + break; + case 2400: + csr = 0x88; + break; + case 4800: + csr = 0x99; + break; + default: + case 9600: + csr = 0xbb; + break; + case 19200: + csr = 0xcc; + break; + } + + WRITE_SC_PORT(port, CR, CR_RES_MR); + WRITE_SC_PORT(port, MRx, mr1); + WRITE_SC_PORT(port, MRx, mr2); + + WRITE_SC(port, ACR, 0x80); + WRITE_SC_PORT(port, CSR, csr); + + /* reset tx and rx */ + WRITE_SC_PORT(port, CR, CR_RES_RX); + WRITE_SC_PORT(port, CR, CR_RES_TX); + + WRITE_SC_PORT(port, CR, CR_ENA_TX | CR_ENA_RX); + while ((READ_SC_PORT(port, SR) & ((1 << 3) | (1 << 2))) != 0xc) + udelay(2); + + /* XXX */ + uart_update_timeout(port, cflag, + (port->uartclk / (16 * quot))); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *sc26xx_type(struct uart_port *port) +{ + return "SC26XX"; +} + +static void sc26xx_release_port(struct uart_port *port) +{ +} + +static int sc26xx_request_port(struct uart_port *port) +{ + return 0; +} + +static void sc26xx_config_port(struct uart_port *port, int flags) +{ +} + +static int sc26xx_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + return -EINVAL; +} + +static struct uart_ops sc26xx_ops = { + .tx_empty = sc26xx_tx_empty, + .set_mctrl = sc26xx_set_mctrl, + .get_mctrl = sc26xx_get_mctrl, + .stop_tx = sc26xx_stop_tx, + .start_tx = sc26xx_start_tx, + .stop_rx = sc26xx_stop_rx, + .enable_ms = sc26xx_enable_ms, + .break_ctl = sc26xx_break_ctl, + .startup = sc26xx_startup, + .shutdown = sc26xx_shutdown, + .set_termios = sc26xx_set_termios, + .type = sc26xx_type, + .release_port = sc26xx_release_port, + .request_port = sc26xx_request_port, + .config_port = sc26xx_config_port, + .verify_port = sc26xx_verify_port, +}; + +static struct uart_port *sc26xx_port; + +#ifdef CONFIG_SERIAL_SC26XX_CONSOLE +static void sc26xx_console_putchar(struct uart_port *port, char c) +{ + unsigned long flags; + int limit = 1000000; + + spin_lock_irqsave(&port->lock, flags); + + while (limit-- > 0) { + if (READ_SC_PORT(port, SR) & SR_TXRDY) { + WRITE_SC_PORT(port, THR, c); + break; + } + udelay(2); + } + + spin_unlock_irqrestore(&port->lock, flags); +} + +static void sc26xx_console_write(struct console *con, const char *s, unsigned n) +{ + struct uart_port *port = sc26xx_port; + int i; + + for (i = 0; i < n; i++) { + if (*s == '\n') + sc26xx_console_putchar(port, '\r'); + sc26xx_console_putchar(port, *s++); + } +} + +static int __init sc26xx_console_setup(struct console *con, char *options) +{ + struct uart_port *port = sc26xx_port; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (port->type != PORT_SC26XX) + return -1; + + printk(KERN_INFO "Console: ttySC%d (SC26XX)\n", con->index); + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(port, con, baud, parity, bits, flow); +} + +static struct uart_driver sc26xx_reg; +static struct console sc26xx_console = { + .name = "ttySC", + .write = sc26xx_console_write, + .device = uart_console_device, + .setup = sc26xx_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &sc26xx_reg, +}; +#define SC26XX_CONSOLE &sc26xx_console +#else +#define SC26XX_CONSOLE NULL +#endif + +static struct uart_driver sc26xx_reg = { + .owner = THIS_MODULE, + .driver_name = "SC26xx", + .dev_name = "ttySC", + .major = SC26XX_MAJOR, + .minor = SC26XX_MINOR_START, + .nr = SC26XX_NR, + .cons = SC26XX_CONSOLE, +}; + +static u8 sc26xx_flags2mask(unsigned int flags, unsigned int bitpos) +{ + unsigned int bit = (flags >> bitpos) & 15; + + return bit ? (1 << (bit - 1)) : 0; +} + +static void __devinit sc26xx_init_masks(struct uart_sc26xx_port *up, + int line, unsigned int data) +{ + up->dtr_mask[line] = sc26xx_flags2mask(data, 0); + up->rts_mask[line] = sc26xx_flags2mask(data, 4); + up->dsr_mask[line] = sc26xx_flags2mask(data, 8); + up->cts_mask[line] = sc26xx_flags2mask(data, 12); + up->dcd_mask[line] = sc26xx_flags2mask(data, 16); + up->ri_mask[line] = sc26xx_flags2mask(data, 20); +} + +static int __devinit sc26xx_probe(struct platform_device *dev) +{ + struct resource *res; + struct uart_sc26xx_port *up; + unsigned int *sc26xx_data = dev->dev.platform_data; + int err; + + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + up = kzalloc(sizeof *up, GFP_KERNEL); + if (unlikely(!up)) + return -ENOMEM; + + up->port[0].line = 0; + up->port[0].ops = &sc26xx_ops; + up->port[0].type = PORT_SC26XX; + up->port[0].uartclk = (29491200 / 16); /* arbitrary */ + + up->port[0].mapbase = res->start; + up->port[0].membase = ioremap_nocache(up->port[0].mapbase, 0x40); + up->port[0].iotype = UPIO_MEM; + up->port[0].irq = platform_get_irq(dev, 0); + + up->port[0].dev = &dev->dev; + + sc26xx_init_masks(up, 0, sc26xx_data[0]); + + sc26xx_port = &up->port[0]; + + up->port[1].line = 1; + up->port[1].ops = &sc26xx_ops; + up->port[1].type = PORT_SC26XX; + up->port[1].uartclk = (29491200 / 16); /* arbitrary */ + + up->port[1].mapbase = up->port[0].mapbase; + up->port[1].membase = up->port[0].membase; + up->port[1].iotype = UPIO_MEM; + up->port[1].irq = up->port[0].irq; + + up->port[1].dev = &dev->dev; + + sc26xx_init_masks(up, 1, sc26xx_data[1]); + + err = uart_register_driver(&sc26xx_reg); + if (err) + goto out_free_port; + + sc26xx_reg.tty_driver->name_base = sc26xx_reg.minor; + + err = uart_add_one_port(&sc26xx_reg, &up->port[0]); + if (err) + goto out_unregister_driver; + + err = uart_add_one_port(&sc26xx_reg, &up->port[1]); + if (err) + goto out_remove_port0; + + err = request_irq(up->port[0].irq, sc26xx_interrupt, 0, "sc26xx", up); + if (err) + goto out_remove_ports; + + dev_set_drvdata(&dev->dev, up); + return 0; + +out_remove_ports: + uart_remove_one_port(&sc26xx_reg, &up->port[1]); +out_remove_port0: + uart_remove_one_port(&sc26xx_reg, &up->port[0]); + +out_unregister_driver: + uart_unregister_driver(&sc26xx_reg); + +out_free_port: + kfree(up); + sc26xx_port = NULL; + return err; +} + + +static int __exit sc26xx_driver_remove(struct platform_device *dev) +{ + struct uart_sc26xx_port *up = dev_get_drvdata(&dev->dev); + + free_irq(up->port[0].irq, up); + + uart_remove_one_port(&sc26xx_reg, &up->port[0]); + uart_remove_one_port(&sc26xx_reg, &up->port[1]); + + uart_unregister_driver(&sc26xx_reg); + + kfree(up); + sc26xx_port = NULL; + + dev_set_drvdata(&dev->dev, NULL); + return 0; +} + +static struct platform_driver sc26xx_driver = { + .probe = sc26xx_probe, + .remove = __devexit_p(sc26xx_driver_remove), + .driver = { + .name = "SC26xx", + }, +}; + +static int __init sc26xx_init(void) +{ + return platform_driver_register(&sc26xx_driver); +} + +static void __exit sc26xx_exit(void) +{ + platform_driver_unregister(&sc26xx_driver); +} + +module_init(sc26xx_init); +module_exit(sc26xx_exit); + + +MODULE_AUTHOR("Thomas Bogendörfer"); +MODULE_DESCRIPTION("SC681/SC2692 serial driver"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/serial/uartlite.c b/drivers/serial/uartlite.c index 80943409edb..bacf68dca01 100644 --- a/drivers/serial/uartlite.c +++ b/drivers/serial/uartlite.c @@ -142,7 +142,7 @@ static int ulite_transmit(struct uart_port *port, int stat) static irqreturn_t ulite_isr(int irq, void *dev_id) { - struct uart_port *port = (struct uart_port *)dev_id; + struct uart_port *port = dev_id; int busy; do { diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index aaaea81e412..d8107890db1 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -144,10 +144,10 @@ config SPI_OMAP_UWIRE This hooks up to the MicroWire controller on OMAP1 chips. config SPI_OMAP24XX - tristate "McSPI driver for OMAP24xx" - depends on SPI_MASTER && ARCH_OMAP24XX + tristate "McSPI driver for OMAP24xx/OMAP34xx" + depends on SPI_MASTER && (ARCH_OMAP24XX || ARCH_OMAP34XX) help - SPI master controller for OMAP24xx Multichannel SPI + SPI master controller for OMAP24xx/OMAP34xx Multichannel SPI (McSPI) modules. config SPI_PXA2XX @@ -176,6 +176,13 @@ config SPI_S3C24XX_GPIO the inbuilt hardware cannot provide the transfer mode, or where the board is using non hardware connected pins. +config SPI_SH_SCI + tristate "SuperH SCI SPI controller" + depends on SPI_MASTER && SUPERH + select SPI_BITBANG + help + SPI driver for SuperH SCI blocks. + config SPI_TXX9 tristate "Toshiba TXx9 SPI controller" depends on SPI_MASTER && GENERIC_GPIO && CPU_TX49XX diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 41fbac45c32..7fca043ce72 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx.o obj-$(CONFIG_SPI_TXX9) += spi_txx9.o obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o +obj-$(CONFIG_SPI_SH_SCI) += spi_sh_sci.o # ... add above this line ... # SPI protocol drivers (device/link on bus) diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c index ff10808183a..293b7cab3e5 100644 --- a/drivers/spi/atmel_spi.c +++ b/drivers/spi/atmel_spi.c @@ -51,7 +51,9 @@ struct atmel_spi { u8 stopping; struct list_head queue; struct spi_transfer *current_transfer; - unsigned long remaining_bytes; + unsigned long current_remaining_bytes; + struct spi_transfer *next_transfer; + unsigned long next_remaining_bytes; void *buffer; dma_addr_t buffer_dma; @@ -121,6 +123,48 @@ static void cs_deactivate(struct atmel_spi *as, struct spi_device *spi) gpio_set_value(gpio, !active); } +static inline int atmel_spi_xfer_is_last(struct spi_message *msg, + struct spi_transfer *xfer) +{ + return msg->transfers.prev == &xfer->transfer_list; +} + +static inline int atmel_spi_xfer_can_be_chained(struct spi_transfer *xfer) +{ + return xfer->delay_usecs == 0 && !xfer->cs_change; +} + +static void atmel_spi_next_xfer_data(struct spi_master *master, + struct spi_transfer *xfer, + dma_addr_t *tx_dma, + dma_addr_t *rx_dma, + u32 *plen) +{ + struct atmel_spi *as = spi_master_get_devdata(master); + u32 len = *plen; + + /* use scratch buffer only when rx or tx data is unspecified */ + if (xfer->rx_buf) + *rx_dma = xfer->rx_dma + xfer->len - len; + else { + *rx_dma = as->buffer_dma; + if (len > BUFFER_SIZE) + len = BUFFER_SIZE; + } + if (xfer->tx_buf) + *tx_dma = xfer->tx_dma + xfer->len - len; + else { + *tx_dma = as->buffer_dma; + if (len > BUFFER_SIZE) + len = BUFFER_SIZE; + memset(as->buffer, 0, len); + dma_sync_single_for_device(&as->pdev->dev, + as->buffer_dma, len, DMA_TO_DEVICE); + } + + *plen = len; +} + /* * Submit next transfer for DMA. * lock is held, spi irq is blocked @@ -130,53 +174,78 @@ static void atmel_spi_next_xfer(struct spi_master *master, { struct atmel_spi *as = spi_master_get_devdata(master); struct spi_transfer *xfer; - u32 len; + u32 len, remaining, total; dma_addr_t tx_dma, rx_dma; - xfer = as->current_transfer; - if (!xfer || as->remaining_bytes == 0) { - if (xfer) - xfer = list_entry(xfer->transfer_list.next, - struct spi_transfer, transfer_list); - else - xfer = list_entry(msg->transfers.next, - struct spi_transfer, transfer_list); - as->remaining_bytes = xfer->len; - as->current_transfer = xfer; - } + if (!as->current_transfer) + xfer = list_entry(msg->transfers.next, + struct spi_transfer, transfer_list); + else if (!as->next_transfer) + xfer = list_entry(as->current_transfer->transfer_list.next, + struct spi_transfer, transfer_list); + else + xfer = NULL; - len = as->remaining_bytes; + if (xfer) { + len = xfer->len; + atmel_spi_next_xfer_data(master, xfer, &tx_dma, &rx_dma, &len); + remaining = xfer->len - len; - tx_dma = xfer->tx_dma + xfer->len - len; - rx_dma = xfer->rx_dma + xfer->len - len; + spi_writel(as, RPR, rx_dma); + spi_writel(as, TPR, tx_dma); - /* use scratch buffer only when rx or tx data is unspecified */ - if (!xfer->rx_buf) { - rx_dma = as->buffer_dma; - if (len > BUFFER_SIZE) - len = BUFFER_SIZE; - } - if (!xfer->tx_buf) { - tx_dma = as->buffer_dma; - if (len > BUFFER_SIZE) - len = BUFFER_SIZE; - memset(as->buffer, 0, len); - dma_sync_single_for_device(&as->pdev->dev, - as->buffer_dma, len, DMA_TO_DEVICE); + if (msg->spi->bits_per_word > 8) + len >>= 1; + spi_writel(as, RCR, len); + spi_writel(as, TCR, len); + + dev_dbg(&msg->spi->dev, + " start xfer %p: len %u tx %p/%08x rx %p/%08x\n", + xfer, xfer->len, xfer->tx_buf, xfer->tx_dma, + xfer->rx_buf, xfer->rx_dma); + } else { + xfer = as->next_transfer; + remaining = as->next_remaining_bytes; } - spi_writel(as, RPR, rx_dma); - spi_writel(as, TPR, tx_dma); + as->current_transfer = xfer; + as->current_remaining_bytes = remaining; - as->remaining_bytes -= len; - if (msg->spi->bits_per_word > 8) - len >>= 1; + if (remaining > 0) + len = remaining; + else if (!atmel_spi_xfer_is_last(msg, xfer) + && atmel_spi_xfer_can_be_chained(xfer)) { + xfer = list_entry(xfer->transfer_list.next, + struct spi_transfer, transfer_list); + len = xfer->len; + } else + xfer = NULL; - /* REVISIT: when xfer->delay_usecs == 0, the PDC "next transfer" - * mechanism might help avoid the IRQ latency between transfers - * (and improve the nCS0 errata handling on at91rm9200 chips) - * - * We're also waiting for ENDRX before we start the next + as->next_transfer = xfer; + + if (xfer) { + total = len; + atmel_spi_next_xfer_data(master, xfer, &tx_dma, &rx_dma, &len); + as->next_remaining_bytes = total - len; + + spi_writel(as, RNPR, rx_dma); + spi_writel(as, TNPR, tx_dma); + + if (msg->spi->bits_per_word > 8) + len >>= 1; + spi_writel(as, RNCR, len); + spi_writel(as, TNCR, len); + + dev_dbg(&msg->spi->dev, + " next xfer %p: len %u tx %p/%08x rx %p/%08x\n", + xfer, xfer->len, xfer->tx_buf, xfer->tx_dma, + xfer->rx_buf, xfer->rx_dma); + } else { + spi_writel(as, RNCR, 0); + spi_writel(as, TNCR, 0); + } + + /* REVISIT: We're waiting for ENDRX before we start the next * transfer because we need to handle some difficult timing * issues otherwise. If we wait for ENDTX in one transfer and * then starts waiting for ENDRX in the next, it's difficult @@ -186,17 +255,7 @@ static void atmel_spi_next_xfer(struct spi_master *master, * * It should be doable, though. Just not now... */ - spi_writel(as, TNCR, 0); - spi_writel(as, RNCR, 0); spi_writel(as, IER, SPI_BIT(ENDRX) | SPI_BIT(OVRES)); - - dev_dbg(&msg->spi->dev, - " start xfer %p: len %u tx %p/%08x rx %p/%08x imr %03x\n", - xfer, xfer->len, xfer->tx_buf, xfer->tx_dma, - xfer->rx_buf, xfer->rx_dma, spi_readl(as, IMR)); - - spi_writel(as, RCR, len); - spi_writel(as, TCR, len); spi_writel(as, PTCR, SPI_BIT(TXTEN) | SPI_BIT(RXTEN)); } @@ -294,6 +353,7 @@ atmel_spi_msg_done(struct spi_master *master, struct atmel_spi *as, spin_lock(&as->lock); as->current_transfer = NULL; + as->next_transfer = NULL; /* continue if needed */ if (list_empty(&as->queue) || as->stopping) @@ -377,7 +437,7 @@ atmel_spi_interrupt(int irq, void *dev_id) spi_writel(as, IDR, pending); - if (as->remaining_bytes == 0) { + if (as->current_remaining_bytes == 0) { msg->actual_length += xfer->len; if (!msg->is_dma_mapped) @@ -387,7 +447,7 @@ atmel_spi_interrupt(int irq, void *dev_id) if (xfer->delay_usecs) udelay(xfer->delay_usecs); - if (msg->transfers.prev == &xfer->transfer_list) { + if (atmel_spi_xfer_is_last(msg, xfer)) { /* report completed message */ atmel_spi_msg_done(master, as, msg, 0, xfer->cs_change); @@ -490,9 +550,14 @@ static int atmel_spi_setup(struct spi_device *spi) if (!(spi->mode & SPI_CPHA)) csr |= SPI_BIT(NCPHA); - /* TODO: DLYBS and DLYBCT */ - csr |= SPI_BF(DLYBS, 10); - csr |= SPI_BF(DLYBCT, 10); + /* DLYBS is mostly irrelevant since we manage chipselect using GPIOs. + * + * DLYBCT would add delays between words, slowing down transfers. + * It could potentially be useful to cope with DMA bottlenecks, but + * in those cases it's probably best to just use a lower bitrate. + */ + csr |= SPI_BF(DLYBS, 0); + csr |= SPI_BF(DLYBCT, 0); /* chipselect must have been muxed as GPIO (e.g. in board setup) */ npcs_pin = (unsigned int)spi->controller_data; diff --git a/drivers/spi/omap2_mcspi.c b/drivers/spi/omap2_mcspi.c index ea61724ae22..a6ba11afb03 100644 --- a/drivers/spi/omap2_mcspi.c +++ b/drivers/spi/omap2_mcspi.c @@ -915,6 +915,28 @@ static u8 __initdata spi2_txdma_id[] = { OMAP24XX_DMA_SPI2_TX1, }; +#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP34XX) +static u8 __initdata spi3_rxdma_id[] = { + OMAP24XX_DMA_SPI3_RX0, + OMAP24XX_DMA_SPI3_RX1, +}; + +static u8 __initdata spi3_txdma_id[] = { + OMAP24XX_DMA_SPI3_TX0, + OMAP24XX_DMA_SPI3_TX1, +}; +#endif + +#ifdef CONFIG_ARCH_OMAP3 +static u8 __initdata spi4_rxdma_id[] = { + OMAP34XX_DMA_SPI4_RX0, +}; + +static u8 __initdata spi4_txdma_id[] = { + OMAP34XX_DMA_SPI4_TX0, +}; +#endif + static int __init omap2_mcspi_probe(struct platform_device *pdev) { struct spi_master *master; @@ -935,7 +957,20 @@ static int __init omap2_mcspi_probe(struct platform_device *pdev) txdma_id = spi2_txdma_id; num_chipselect = 2; break; - /* REVISIT omap2430 has a third McSPI ... */ +#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3) + case 3: + rxdma_id = spi3_rxdma_id; + txdma_id = spi3_txdma_id; + num_chipselect = 2; + break; +#endif +#ifdef CONFIG_ARCH_OMAP3 + case 4: + rxdma_id = spi4_rxdma_id; + txdma_id = spi4_txdma_id; + num_chipselect = 1; + break; +#endif default: return -EINVAL; } diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c index eb817b8eb02..365e0e355ae 100644 --- a/drivers/spi/pxa2xx_spi.c +++ b/drivers/spi/pxa2xx_spi.c @@ -1526,17 +1526,6 @@ static void pxa2xx_spi_shutdown(struct platform_device *pdev) } #ifdef CONFIG_PM -static int suspend_devices(struct device *dev, void *pm_message) -{ - pm_message_t *state = pm_message; - - if (dev->power.power_state.event != state->event) { - dev_warn(dev, "pm state does not match request\n"); - return -1; - } - - return 0; -} static int pxa2xx_spi_suspend(struct platform_device *pdev, pm_message_t state) { @@ -1544,12 +1533,6 @@ static int pxa2xx_spi_suspend(struct platform_device *pdev, pm_message_t state) struct ssp_device *ssp = drv_data->ssp; int status = 0; - /* Check all childern for current power state */ - if (device_for_each_child(&pdev->dev, &state, suspend_devices) != 0) { - dev_warn(&pdev->dev, "suspend aborted\n"); - return -1; - } - status = stop_queue(drv_data); if (status != 0) return status; diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 682a6a48fec..1ad12afc6ba 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -18,7 +18,6 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include <linux/autoconf.h> #include <linux/kernel.h> #include <linux/device.h> #include <linux/init.h> @@ -77,39 +76,33 @@ static int spi_uevent(struct device *dev, struct kobj_uevent_env *env) #ifdef CONFIG_PM -/* - * NOTE: the suspend() method for an spi_master controller driver - * should verify that all its child devices are marked as suspended; - * suspend requests delivered through sysfs power/state files don't - * enforce such constraints. - */ static int spi_suspend(struct device *dev, pm_message_t message) { - int value; + int value = 0; struct spi_driver *drv = to_spi_driver(dev->driver); - if (!drv || !drv->suspend) - return 0; - /* suspend will stop irqs and dma; no more i/o */ - value = drv->suspend(to_spi_device(dev), message); - if (value == 0) - dev->power.power_state = message; + if (drv) { + if (drv->suspend) + value = drv->suspend(to_spi_device(dev), message); + else + dev_dbg(dev, "... can't suspend\n"); + } return value; } static int spi_resume(struct device *dev) { - int value; + int value = 0; struct spi_driver *drv = to_spi_driver(dev->driver); - if (!drv || !drv->resume) - return 0; - /* resume may restart the i/o queue */ - value = drv->resume(to_spi_device(dev)); - if (value == 0) - dev->power.power_state = PMSG_ON; + if (drv) { + if (drv->resume) + value = drv->resume(to_spi_device(dev)); + else + dev_dbg(dev, "... can't resume\n"); + } return value; } diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c index 7ef39a6e8c0..d853fceb6bf 100644 --- a/drivers/spi/spi_bfin5xx.c +++ b/drivers/spi/spi_bfin5xx.c @@ -1,37 +1,11 @@ /* - * File: drivers/spi/bfin5xx_spi.c - * Maintainer: - * Bryan Wu <bryan.wu@analog.com> - * Original Author: - * Luke Yang (Analog Devices Inc.) - * - * Created: March. 10th 2006 - * Description: SPI controller driver for Blackfin BF5xx - * Bugs: Enter bugs at http://blackfin.uclinux.org/ - * - * Modified: - * March 10, 2006 bfin5xx_spi.c Created. (Luke Yang) - * August 7, 2006 added full duplex mode (Axel Weiss & Luke Yang) - * July 17, 2007 add support for BF54x SPI0 controller (Bryan Wu) - * July 30, 2007 add platfrom_resource interface to support multi-port - * SPI controller (Bryan Wu) + * Blackfin On-Chip SPI Driver * * Copyright 2004-2007 Analog Devices Inc. * - * 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, 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. + * Enter bugs at http://blackfin.uclinux.org/ * - * You should have received a copy of the GNU General Public License - * along with this program ; see the file COPYING. - * If not, write to the Free Software Foundation, - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Licensed under the GPL-2 or later. */ #include <linux/init.h> @@ -223,10 +197,9 @@ static void cs_deactive(struct driver_data *drv_data, struct chip_data *chip) #define MAX_SPI_SSEL 7 /* stop controller and re-config current chip*/ -static int restore_state(struct driver_data *drv_data) +static void restore_state(struct driver_data *drv_data) { struct chip_data *chip = drv_data->cur_chip; - int ret = 0; /* Clear status and disable clock */ write_STAT(drv_data, BIT_STAT_CLR); @@ -239,13 +212,6 @@ static int restore_state(struct driver_data *drv_data) bfin_spi_enable(drv_data); cs_active(drv_data, chip); - - if (ret) - dev_dbg(&drv_data->pdev->dev, - ": request chip select number %d failed\n", - chip->chip_select_num); - - return ret; } /* used to kick off transfer in rx mode */ @@ -286,32 +252,30 @@ static void u8_writer(struct driver_data *drv_data) dev_dbg(&drv_data->pdev->dev, "cr8-s is 0x%x\n", read_STAT(drv_data)); - /* poll for SPI completion before start */ - while (!(read_STAT(drv_data) & BIT_STAT_SPIF)) - cpu_relax(); - while (drv_data->tx < drv_data->tx_end) { write_TDBR(drv_data, (*(u8 *) (drv_data->tx))); while (read_STAT(drv_data) & BIT_STAT_TXS) cpu_relax(); ++drv_data->tx; } + + /* poll for SPI completion before return */ + while (!(read_STAT(drv_data) & BIT_STAT_SPIF)) + cpu_relax(); } static void u8_cs_chg_writer(struct driver_data *drv_data) { struct chip_data *chip = drv_data->cur_chip; - /* poll for SPI completion before start */ - while (!(read_STAT(drv_data) & BIT_STAT_SPIF)) - cpu_relax(); - while (drv_data->tx < drv_data->tx_end) { cs_active(drv_data, chip); write_TDBR(drv_data, (*(u8 *) (drv_data->tx))); while (read_STAT(drv_data) & BIT_STAT_TXS) cpu_relax(); + while (!(read_STAT(drv_data) & BIT_STAT_SPIF)) + cpu_relax(); cs_deactive(drv_data, chip); @@ -350,43 +314,28 @@ static void u8_cs_chg_reader(struct driver_data *drv_data) { struct chip_data *chip = drv_data->cur_chip; - /* poll for SPI completion before start */ - while (!(read_STAT(drv_data) & BIT_STAT_SPIF)) - cpu_relax(); - - /* clear TDBR buffer before read(else it will be shifted out) */ - write_TDBR(drv_data, 0xFFFF); + while (drv_data->rx < drv_data->rx_end) { + cs_active(drv_data, chip); + read_RDBR(drv_data); /* kick off */ - cs_active(drv_data, chip); - dummy_read(drv_data); + while (!(read_STAT(drv_data) & BIT_STAT_RXS)) + cpu_relax(); + while (!(read_STAT(drv_data) & BIT_STAT_SPIF)) + cpu_relax(); - while (drv_data->rx < drv_data->rx_end - 1) { + *(u8 *) (drv_data->rx) = read_SHAW(drv_data); cs_deactive(drv_data, chip); - while (!(read_STAT(drv_data) & BIT_STAT_RXS)) - cpu_relax(); - cs_active(drv_data, chip); - *(u8 *) (drv_data->rx) = read_RDBR(drv_data); ++drv_data->rx; } - cs_deactive(drv_data, chip); - - while (!(read_STAT(drv_data) & BIT_STAT_RXS)) - cpu_relax(); - *(u8 *) (drv_data->rx) = read_SHAW(drv_data); - ++drv_data->rx; } static void u8_duplex(struct driver_data *drv_data) { - /* poll for SPI completion before start */ - while (!(read_STAT(drv_data) & BIT_STAT_SPIF)) - cpu_relax(); - /* in duplex mode, clk is triggered by writing of TDBR */ while (drv_data->rx < drv_data->rx_end) { write_TDBR(drv_data, (*(u8 *) (drv_data->tx))); - while (read_STAT(drv_data) & BIT_STAT_TXS) + while (!(read_STAT(drv_data) & BIT_STAT_SPIF)) cpu_relax(); while (!(read_STAT(drv_data) & BIT_STAT_RXS)) cpu_relax(); @@ -400,15 +349,12 @@ static void u8_cs_chg_duplex(struct driver_data *drv_data) { struct chip_data *chip = drv_data->cur_chip; - /* poll for SPI completion before start */ - while (!(read_STAT(drv_data) & BIT_STAT_SPIF)) - cpu_relax(); - while (drv_data->rx < drv_data->rx_end) { cs_active(drv_data, chip); write_TDBR(drv_data, (*(u8 *) (drv_data->tx))); - while (read_STAT(drv_data) & BIT_STAT_TXS) + + while (!(read_STAT(drv_data) & BIT_STAT_SPIF)) cpu_relax(); while (!(read_STAT(drv_data) & BIT_STAT_RXS)) cpu_relax(); @@ -426,32 +372,30 @@ static void u16_writer(struct driver_data *drv_data) dev_dbg(&drv_data->pdev->dev, "cr16 is 0x%x\n", read_STAT(drv_data)); - /* poll for SPI completion before start */ - while (!(read_STAT(drv_data) & BIT_STAT_SPIF)) - cpu_relax(); - while (drv_data->tx < drv_data->tx_end) { write_TDBR(drv_data, (*(u16 *) (drv_data->tx))); while ((read_STAT(drv_data) & BIT_STAT_TXS)) cpu_relax(); drv_data->tx += 2; } + + /* poll for SPI completion before return */ + while (!(read_STAT(drv_data) & BIT_STAT_SPIF)) + cpu_relax(); } static void u16_cs_chg_writer(struct driver_data *drv_data) { struct chip_data *chip = drv_data->cur_chip; - /* poll for SPI completion before start */ - while (!(read_STAT(drv_data) & BIT_STAT_SPIF)) - cpu_relax(); - while (drv_data->tx < drv_data->tx_end) { cs_active(drv_data, chip); write_TDBR(drv_data, (*(u16 *) (drv_data->tx))); while ((read_STAT(drv_data) & BIT_STAT_TXS)) cpu_relax(); + while (!(read_STAT(drv_data) & BIT_STAT_SPIF)) + cpu_relax(); cs_deactive(drv_data, chip); @@ -519,14 +463,10 @@ static void u16_cs_chg_reader(struct driver_data *drv_data) static void u16_duplex(struct driver_data *drv_data) { - /* poll for SPI completion before start */ - while (!(read_STAT(drv_data) & BIT_STAT_SPIF)) - cpu_relax(); - /* in duplex mode, clk is triggered by writing of TDBR */ while (drv_data->tx < drv_data->tx_end) { write_TDBR(drv_data, (*(u16 *) (drv_data->tx))); - while (read_STAT(drv_data) & BIT_STAT_TXS) + while (!(read_STAT(drv_data) & BIT_STAT_SPIF)) cpu_relax(); while (!(read_STAT(drv_data) & BIT_STAT_RXS)) cpu_relax(); @@ -540,15 +480,11 @@ static void u16_cs_chg_duplex(struct driver_data *drv_data) { struct chip_data *chip = drv_data->cur_chip; - /* poll for SPI completion before start */ - while (!(read_STAT(drv_data) & BIT_STAT_SPIF)) - cpu_relax(); - while (drv_data->tx < drv_data->tx_end) { cs_active(drv_data, chip); write_TDBR(drv_data, (*(u16 *) (drv_data->tx))); - while (read_STAT(drv_data) & BIT_STAT_TXS) + while (!(read_STAT(drv_data) & BIT_STAT_SPIF)) cpu_relax(); while (!(read_STAT(drv_data) & BIT_STAT_RXS)) cpu_relax(); @@ -616,7 +552,7 @@ static void giveback(struct driver_data *drv_data) static irqreturn_t dma_irq_handler(int irq, void *dev_id) { - struct driver_data *drv_data = (struct driver_data *)dev_id; + struct driver_data *drv_data = dev_id; struct chip_data *chip = drv_data->cur_chip; struct spi_message *msg = drv_data->cur_msg; @@ -978,10 +914,7 @@ static void pump_messages(struct work_struct *work) /* Setup the SSP using the per chip configuration */ drv_data->cur_chip = spi_get_ctldata(drv_data->cur_msg->spi); - if (restore_state(drv_data)) { - spin_unlock_irqrestore(&drv_data->lock, flags); - return; - }; + restore_state(drv_data); list_del_init(&drv_data->cur_msg->queue); @@ -1187,7 +1120,7 @@ static int setup(struct spi_device *spi) if ((chip->chip_select_num > 0) && (chip->chip_select_num <= spi->master->num_chipselect)) peripheral_request(ssel[spi->master->bus_num] - [chip->chip_select_num-1], DRV_NAME); + [chip->chip_select_num-1], spi->modalias); cs_deactive(drv_data, chip); diff --git a/drivers/spi/spi_imx.c b/drivers/spi/spi_imx.c index 639963eb1ac..1b064712493 100644 --- a/drivers/spi/spi_imx.c +++ b/drivers/spi/spi_imx.c @@ -1686,17 +1686,6 @@ static void spi_imx_shutdown(struct platform_device *pdev) } #ifdef CONFIG_PM -static int suspend_devices(struct device *dev, void *pm_message) -{ - pm_message_t *state = pm_message; - - if (dev->power.power_state.event != state->event) { - dev_warn(dev, "pm state does not match request\n"); - return -1; - } - - return 0; -} static int spi_imx_suspend(struct platform_device *pdev, pm_message_t state) { diff --git a/drivers/spi/spi_s3c24xx.c b/drivers/spi/spi_s3c24xx.c index 89d6685a5ca..6e834b8b9d2 100644 --- a/drivers/spi/spi_s3c24xx.c +++ b/drivers/spi/spi_s3c24xx.c @@ -237,10 +237,8 @@ static int __init s3c24xx_spi_probe(struct platform_device *pdev) { struct s3c24xx_spi *hw; struct spi_master *master; - struct spi_board_info *bi; struct resource *res; int err = 0; - int i; master = spi_alloc_master(&pdev->dev, sizeof(struct s3c24xx_spi)); if (master == NULL) { @@ -348,16 +346,6 @@ static int __init s3c24xx_spi_probe(struct platform_device *pdev) goto err_register; } - /* register all the devices associated */ - - bi = &hw->pdata->board_info[0]; - for (i = 0; i < hw->pdata->board_size; i++, bi++) { - dev_info(hw->dev, "registering %s\n", bi->modalias); - - bi->controller_data = hw; - spi_new_device(master, bi); - } - return 0; err_register: diff --git a/drivers/spi/spi_s3c24xx_gpio.c b/drivers/spi/spi_s3c24xx_gpio.c index 109d82c1abc..82ae7d7eca3 100644 --- a/drivers/spi/spi_s3c24xx_gpio.c +++ b/drivers/spi/spi_s3c24xx_gpio.c @@ -100,7 +100,6 @@ static int s3c2410_spigpio_probe(struct platform_device *dev) struct spi_master *master; struct s3c2410_spigpio *sp; int ret; - int i; master = spi_alloc_master(&dev->dev, sizeof(struct s3c2410_spigpio)); if (master == NULL) { @@ -143,17 +142,6 @@ static int s3c2410_spigpio_probe(struct platform_device *dev) if (ret) goto err_no_bitbang; - /* register the chips to go with the board */ - - for (i = 0; i < sp->info->board_size; i++) { - dev_info(&dev->dev, "registering %p: %s\n", - &sp->info->board_info[i], - sp->info->board_info[i].modalias); - - sp->info->board_info[i].controller_data = sp; - spi_new_device(master, sp->info->board_info + i); - } - return 0; err_no_bitbang: diff --git a/drivers/spi/spi_sh_sci.c b/drivers/spi/spi_sh_sci.c new file mode 100644 index 00000000000..3dbe71b16d6 --- /dev/null +++ b/drivers/spi/spi_sh_sci.c @@ -0,0 +1,205 @@ +/* + * SH SCI SPI interface + * + * Copyright (c) 2008 Magnus Damm + * + * Based on S3C24XX GPIO based SPI driver, which is: + * Copyright (c) 2006 Ben Dooks + * Copyright (c) 2006 Simtec Electronics + * + * 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 <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/spinlock.h> +#include <linux/workqueue.h> +#include <linux/platform_device.h> + +#include <linux/spi/spi.h> +#include <linux/spi/spi_bitbang.h> + +#include <asm/spi.h> +#include <asm/io.h> + +struct sh_sci_spi { + struct spi_bitbang bitbang; + + void __iomem *membase; + unsigned char val; + struct sh_spi_info *info; + struct platform_device *dev; +}; + +#define SCSPTR(sp) (sp->membase + 0x1c) +#define PIN_SCK (1 << 2) +#define PIN_TXD (1 << 0) +#define PIN_RXD PIN_TXD +#define PIN_INIT ((1 << 1) | (1 << 3) | PIN_SCK | PIN_TXD) + +static inline void setbits(struct sh_sci_spi *sp, int bits, int on) +{ + /* + * We are the only user of SCSPTR so no locking is required. + * Reading bit 2 and 0 in SCSPTR gives pin state as input. + * Writing the same bits sets the output value. + * This makes regular read-modify-write difficult so we + * use sp->val to keep track of the latest register value. + */ + + if (on) + sp->val |= bits; + else + sp->val &= ~bits; + + iowrite8(sp->val, SCSPTR(sp)); +} + +static inline void setsck(struct spi_device *dev, int on) +{ + setbits(spi_master_get_devdata(dev->master), PIN_SCK, on); +} + +static inline void setmosi(struct spi_device *dev, int on) +{ + setbits(spi_master_get_devdata(dev->master), PIN_TXD, on); +} + +static inline u32 getmiso(struct spi_device *dev) +{ + struct sh_sci_spi *sp = spi_master_get_devdata(dev->master); + + return (ioread8(SCSPTR(sp)) & PIN_RXD) ? 1 : 0; +} + +#define spidelay(x) ndelay(x) + +#define EXPAND_BITBANG_TXRX +#include <linux/spi/spi_bitbang.h> + +static u32 sh_sci_spi_txrx_mode0(struct spi_device *spi, + unsigned nsecs, u32 word, u8 bits) +{ + return bitbang_txrx_be_cpha0(spi, nsecs, 0, word, bits); +} + +static u32 sh_sci_spi_txrx_mode1(struct spi_device *spi, + unsigned nsecs, u32 word, u8 bits) +{ + return bitbang_txrx_be_cpha1(spi, nsecs, 0, word, bits); +} + +static u32 sh_sci_spi_txrx_mode2(struct spi_device *spi, + unsigned nsecs, u32 word, u8 bits) +{ + return bitbang_txrx_be_cpha0(spi, nsecs, 1, word, bits); +} + +static u32 sh_sci_spi_txrx_mode3(struct spi_device *spi, + unsigned nsecs, u32 word, u8 bits) +{ + return bitbang_txrx_be_cpha1(spi, nsecs, 1, word, bits); +} + +static void sh_sci_spi_chipselect(struct spi_device *dev, int value) +{ + struct sh_sci_spi *sp = spi_master_get_devdata(dev->master); + + if (sp->info && sp->info->chip_select) + (sp->info->chip_select)(sp->info, dev->chip_select, value); +} + +static int sh_sci_spi_probe(struct platform_device *dev) +{ + struct resource *r; + struct spi_master *master; + struct sh_sci_spi *sp; + int ret; + + master = spi_alloc_master(&dev->dev, sizeof(struct sh_sci_spi)); + if (master == NULL) { + dev_err(&dev->dev, "failed to allocate spi master\n"); + ret = -ENOMEM; + goto err0; + } + + sp = spi_master_get_devdata(master); + + platform_set_drvdata(dev, sp); + sp->info = dev->dev.platform_data; + + /* setup spi bitbang adaptor */ + sp->bitbang.master = spi_master_get(master); + sp->bitbang.master->bus_num = sp->info->bus_num; + sp->bitbang.master->num_chipselect = sp->info->num_chipselect; + sp->bitbang.chipselect = sh_sci_spi_chipselect; + + sp->bitbang.txrx_word[SPI_MODE_0] = sh_sci_spi_txrx_mode0; + sp->bitbang.txrx_word[SPI_MODE_1] = sh_sci_spi_txrx_mode1; + sp->bitbang.txrx_word[SPI_MODE_2] = sh_sci_spi_txrx_mode2; + sp->bitbang.txrx_word[SPI_MODE_3] = sh_sci_spi_txrx_mode3; + + r = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (r == NULL) { + ret = -ENOENT; + goto err1; + } + sp->membase = ioremap(r->start, r->end - r->start + 1); + if (!sp->membase) { + ret = -ENXIO; + goto err1; + } + sp->val = ioread8(SCSPTR(sp)); + setbits(sp, PIN_INIT, 1); + + ret = spi_bitbang_start(&sp->bitbang); + if (!ret) + return 0; + + setbits(sp, PIN_INIT, 0); + iounmap(sp->membase); + err1: + spi_master_put(sp->bitbang.master); + err0: + return ret; +} + +static int sh_sci_spi_remove(struct platform_device *dev) +{ + struct sh_sci_spi *sp = platform_get_drvdata(dev); + + iounmap(sp->membase); + setbits(sp, PIN_INIT, 0); + spi_bitbang_stop(&sp->bitbang); + spi_master_put(sp->bitbang.master); + return 0; +} + +static struct platform_driver sh_sci_spi_drv = { + .probe = sh_sci_spi_probe, + .remove = sh_sci_spi_remove, + .driver = { + .name = "spi_sh_sci", + .owner = THIS_MODULE, + }, +}; + +static int __init sh_sci_spi_init(void) +{ + return platform_driver_register(&sh_sci_spi_drv); +} +module_init(sh_sci_spi_init); + +static void __exit sh_sci_spi_exit(void) +{ + platform_driver_unregister(&sh_sci_spi_drv); +} +module_exit(sh_sci_spi_exit); + +MODULE_DESCRIPTION("SH SCI SPI Driver"); +MODULE_AUTHOR("Magnus Damm <damm@opensource.se>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index cc246faa359..2a77e9d42c6 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c @@ -417,30 +417,28 @@ static void uio_vma_close(struct vm_area_struct *vma) idev->vma_count--; } -static struct page *uio_vma_nopage(struct vm_area_struct *vma, - unsigned long address, int *type) +static int uio_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { struct uio_device *idev = vma->vm_private_data; - struct page* page = NOPAGE_SIGBUS; + struct page *page; int mi = uio_find_mem_index(vma); if (mi < 0) - return page; + return VM_FAULT_SIGBUS; if (idev->info->mem[mi].memtype == UIO_MEM_LOGICAL) page = virt_to_page(idev->info->mem[mi].addr); else page = vmalloc_to_page((void*)idev->info->mem[mi].addr); get_page(page); - if (type) - *type = VM_FAULT_MINOR; - return page; + vmf->page = page; + return 0; } static struct vm_operations_struct uio_vm_ops = { .open = uio_vma_open, .close = uio_vma_close, - .nopage = uio_vma_nopage, + .fault = uio_vma_fault, }; static int uio_mmap_physical(struct vm_area_struct *vma) diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c index f8e71114750..fc65c02306d 100644 --- a/drivers/video/atmel_lcdfb.c +++ b/drivers/video/atmel_lcdfb.c @@ -16,6 +16,7 @@ #include <linux/fb.h> #include <linux/init.h> #include <linux/delay.h> +#include <linux/backlight.h> #include <asm/arch/board.h> #include <asm/arch/cpu.h> @@ -69,6 +70,107 @@ static void atmel_lcdfb_update_dma2d(struct atmel_lcdfb_info *sinfo, } #endif +static const u32 contrast_ctr = ATMEL_LCDC_PS_DIV8 + | ATMEL_LCDC_POL_POSITIVE + | ATMEL_LCDC_ENA_PWMENABLE; + +#ifdef CONFIG_BACKLIGHT_ATMEL_LCDC + +/* some bl->props field just changed */ +static int atmel_bl_update_status(struct backlight_device *bl) +{ + struct atmel_lcdfb_info *sinfo = bl_get_data(bl); + int power = sinfo->bl_power; + int brightness = bl->props.brightness; + + /* REVISIT there may be a meaningful difference between + * fb_blank and power ... there seem to be some cases + * this doesn't handle correctly. + */ + if (bl->props.fb_blank != sinfo->bl_power) + power = bl->props.fb_blank; + else if (bl->props.power != sinfo->bl_power) + power = bl->props.power; + + if (brightness < 0 && power == FB_BLANK_UNBLANK) + brightness = lcdc_readl(sinfo, ATMEL_LCDC_CONTRAST_VAL); + else if (power != FB_BLANK_UNBLANK) + brightness = 0; + + lcdc_writel(sinfo, ATMEL_LCDC_CONTRAST_VAL, brightness); + lcdc_writel(sinfo, ATMEL_LCDC_CONTRAST_CTR, + brightness ? contrast_ctr : 0); + + bl->props.fb_blank = bl->props.power = sinfo->bl_power = power; + + return 0; +} + +static int atmel_bl_get_brightness(struct backlight_device *bl) +{ + struct atmel_lcdfb_info *sinfo = bl_get_data(bl); + + return lcdc_readl(sinfo, ATMEL_LCDC_CONTRAST_VAL); +} + +static struct backlight_ops atmel_lcdc_bl_ops = { + .update_status = atmel_bl_update_status, + .get_brightness = atmel_bl_get_brightness, +}; + +static void init_backlight(struct atmel_lcdfb_info *sinfo) +{ + struct backlight_device *bl; + + sinfo->bl_power = FB_BLANK_UNBLANK; + + if (sinfo->backlight) + return; + + bl = backlight_device_register("backlight", &sinfo->pdev->dev, + sinfo, &atmel_lcdc_bl_ops); + if (IS_ERR(sinfo->backlight)) { + dev_err(&sinfo->pdev->dev, "error %ld on backlight register\n", + PTR_ERR(bl)); + return; + } + sinfo->backlight = bl; + + bl->props.power = FB_BLANK_UNBLANK; + bl->props.fb_blank = FB_BLANK_UNBLANK; + bl->props.max_brightness = 0xff; + bl->props.brightness = atmel_bl_get_brightness(bl); +} + +static void exit_backlight(struct atmel_lcdfb_info *sinfo) +{ + if (sinfo->backlight) + backlight_device_unregister(sinfo->backlight); +} + +#else + +static void init_backlight(struct atmel_lcdfb_info *sinfo) +{ + dev_warn(&sinfo->pdev->dev, "backlight control is not available\n"); +} + +static void exit_backlight(struct atmel_lcdfb_info *sinfo) +{ +} + +#endif + +static void init_contrast(struct atmel_lcdfb_info *sinfo) +{ + /* have some default contrast/backlight settings */ + lcdc_writel(sinfo, ATMEL_LCDC_CONTRAST_CTR, contrast_ctr); + lcdc_writel(sinfo, ATMEL_LCDC_CONTRAST_VAL, ATMEL_LCDC_CVAL_DEFAULT); + + if (sinfo->lcdcon_is_backlight) + init_backlight(sinfo); +} + static struct fb_fix_screeninfo atmel_lcdfb_fix __initdata = { .type = FB_TYPE_PACKED_PIXELS, @@ -203,6 +305,26 @@ static int atmel_lcdfb_check_var(struct fb_var_screeninfo *var, var->transp.offset = var->transp.length = 0; var->xoffset = var->yoffset = 0; + /* Saturate vertical and horizontal timings at maximum values */ + var->vsync_len = min_t(u32, var->vsync_len, + (ATMEL_LCDC_VPW >> ATMEL_LCDC_VPW_OFFSET) + 1); + var->upper_margin = min_t(u32, var->upper_margin, + ATMEL_LCDC_VBP >> ATMEL_LCDC_VBP_OFFSET); + var->lower_margin = min_t(u32, var->lower_margin, + ATMEL_LCDC_VFP); + var->right_margin = min_t(u32, var->right_margin, + (ATMEL_LCDC_HFP >> ATMEL_LCDC_HFP_OFFSET) + 1); + var->hsync_len = min_t(u32, var->hsync_len, + (ATMEL_LCDC_HPW >> ATMEL_LCDC_HPW_OFFSET) + 1); + var->left_margin = min_t(u32, var->left_margin, + ATMEL_LCDC_HBP + 1); + + /* Some parameters can't be zero */ + var->vsync_len = max_t(u32, var->vsync_len, 1); + var->right_margin = max_t(u32, var->right_margin, 1); + var->hsync_len = max_t(u32, var->hsync_len, 1); + var->left_margin = max_t(u32, var->left_margin, 1); + switch (var->bits_per_pixel) { case 1: case 2: @@ -370,10 +492,6 @@ static int atmel_lcdfb_set_par(struct fb_info *info) /* Disable all interrupts */ lcdc_writel(sinfo, ATMEL_LCDC_IDR, ~0UL); - /* Set contrast */ - value = ATMEL_LCDC_PS_DIV8 | ATMEL_LCDC_POL_POSITIVE | ATMEL_LCDC_ENA_PWMENABLE; - lcdc_writel(sinfo, ATMEL_LCDC_CONTRAST_CTR, value); - lcdc_writel(sinfo, ATMEL_LCDC_CONTRAST_VAL, ATMEL_LCDC_CVAL_DEFAULT); /* ...wait for DMA engine to become idle... */ while (lcdc_readl(sinfo, ATMEL_LCDC_DMACON) & ATMEL_LCDC_DMABUSY) msleep(10); @@ -577,6 +695,7 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev) sinfo->default_monspecs = pdata_sinfo->default_monspecs; sinfo->atmel_lcdfb_power_control = pdata_sinfo->atmel_lcdfb_power_control; sinfo->guard_time = pdata_sinfo->guard_time; + sinfo->lcdcon_is_backlight = pdata_sinfo->lcdcon_is_backlight; } else { dev_err(dev, "cannot get default configuration\n"); goto free_info; @@ -670,6 +789,9 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev) goto release_mem; } + /* Initialize PWM for contrast or backlight ("off") */ + init_contrast(sinfo); + /* interrupt */ ret = request_irq(sinfo->irq_base, atmel_lcdfb_interrupt, 0, pdev->name, info); if (ret) { @@ -721,6 +843,7 @@ free_cmap: unregister_irqs: free_irq(sinfo->irq_base, info); unmap_mmio: + exit_backlight(sinfo); iounmap(sinfo->mmio); release_mem: release_mem_region(info->fix.mmio_start, info->fix.mmio_len); @@ -755,6 +878,7 @@ static int __exit atmel_lcdfb_remove(struct platform_device *pdev) if (!sinfo) return 0; + exit_backlight(sinfo); if (sinfo->atmel_lcdfb_power_control) sinfo->atmel_lcdfb_power_control(0); unregister_framebuffer(info); @@ -781,6 +905,9 @@ static int __exit atmel_lcdfb_remove(struct platform_device *pdev) static struct platform_driver atmel_lcdfb_driver = { .remove = __exit_p(atmel_lcdfb_remove), + +// FIXME need suspend, resume + .driver = { .name = "atmel_lcdfb", .owner = THIS_MODULE, diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 9609a6c676b..924e2551044 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -50,6 +50,19 @@ config BACKLIGHT_CLASS_DEVICE To have support for your specific LCD panel you will have to select the proper drivers which depend on this option. +config BACKLIGHT_ATMEL_LCDC + bool "Atmel LCDC Contrast-as-Backlight control" + depends on BACKLIGHT_CLASS_DEVICE && FB_ATMEL + default y if MACH_SAM9261EK || MACH_SAM9263EK + help + This provides a backlight control internal to the Atmel LCDC + driver. If the LCD "contrast control" on your board is wired + so it controls the backlight brightness, select this option to + export this as a PWM-based backlight control. + + If in doubt, it's safe to enable this option; it doesn't kick + in unless the board's description says it's wired that way. + config BACKLIGHT_CORGI tristate "Generic (aka Sharp Corgi) Backlight Driver" depends on BACKLIGHT_CLASS_DEVICE diff --git a/drivers/video/bf54x-lq043fb.c b/drivers/video/bf54x-lq043fb.c index c8e7427a0bc..0ce791e6f79 100644 --- a/drivers/video/bf54x-lq043fb.c +++ b/drivers/video/bf54x-lq043fb.c @@ -498,8 +498,7 @@ static struct lcd_device *lcd_dev; static irqreturn_t bfin_bf54x_irq_error(int irq, void *dev_id) { - - /*struct bfin_bf54xfb_info *info = (struct bfin_bf54xfb_info *)dev_id;*/ + /*struct bfin_bf54xfb_info *info = dev_id;*/ u16 status = bfin_read_EPPI0_STATUS(); diff --git a/drivers/video/console/bitblit.c b/drivers/video/console/bitblit.c index 308850df16f..69864b1b3f9 100644 --- a/drivers/video/console/bitblit.c +++ b/drivers/video/console/bitblit.c @@ -63,7 +63,7 @@ static void bit_clear(struct vc_data *vc, struct fb_info *info, int sy, int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; struct fb_fillrect region; - region.color = attr_bgcol_ec(bgshift, vc); + region.color = attr_bgcol_ec(bgshift, vc, info); region.dx = sx * vc->vc_font.width; region.dy = sy * vc->vc_font.height; region.width = width * vc->vc_font.width; @@ -213,7 +213,7 @@ static void bit_clear_margins(struct vc_data *vc, struct fb_info *info, unsigned int bs = info->var.yres - bh; struct fb_fillrect region; - region.color = attr_bgcol_ec(bgshift, vc); + region.color = attr_bgcol_ec(bgshift, vc, info); region.rop = ROP_COPY; if (rw && !bottom_only) { diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index 0f32f4a00b2..022282494d3 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -84,7 +84,7 @@ #ifdef CONFIG_MAC #include <asm/macints.h> #endif -#if defined(__mc68000__) || defined(CONFIG_APUS) +#if defined(__mc68000__) #include <asm/machdep.h> #include <asm/setup.h> #endif @@ -147,7 +147,7 @@ static char fontname[40]; static int info_idx = -1; /* console rotation */ -static int rotate; +static int initial_rotation; static int fbcon_has_sysfs; static const struct consw fb_con; @@ -334,10 +334,7 @@ static inline int get_color(struct vc_data *vc, struct fb_info *info, switch (depth) { case 1: { - int col = ~(0xfff << (max(info->var.green.length, - max(info->var.red.length, - info->var.blue.length)))) & 0xff; - + int col = mono_col(info); /* 0 or 1 */ int fg = (info->fix.visual != FB_VISUAL_MONO01) ? col : 0; int bg = (info->fix.visual != FB_VISUAL_MONO01) ? 0 : col; @@ -537,9 +534,9 @@ static int __init fb_console_setup(char *this_opt) if (!strncmp(options, "rotate:", 7)) { options += 7; if (*options) - rotate = simple_strtoul(options, &options, 0); - if (rotate > 3) - rotate = 0; + initial_rotation = simple_strtoul(options, &options, 0); + if (initial_rotation > 3) + initial_rotation = 0; } } return 1; @@ -989,7 +986,7 @@ static const char *fbcon_startup(void) ops->graphics = 1; ops->cur_rotate = -1; info->fbcon_par = ops; - p->con_rotate = rotate; + p->con_rotate = initial_rotation; set_blitting_type(vc, info); if (info->fix.type != FB_TYPE_TEXT) { @@ -1176,7 +1173,7 @@ static void fbcon_init(struct vc_data *vc, int init) con_copy_unimap(vc, svc); ops = info->fbcon_par; - p->con_rotate = rotate; + p->con_rotate = initial_rotation; set_blitting_type(vc, info); cols = vc->vc_cols; @@ -2795,7 +2792,7 @@ static int fbcon_scrolldelta(struct vc_data *vc, int lines) { struct fb_info *info = registered_fb[con2fb_map[fg_console]]; struct fbcon_ops *ops = info->fbcon_par; - struct display *p = &fb_display[fg_console]; + struct display *disp = &fb_display[fg_console]; int offset, limit, scrollback_old; if (softback_top) { @@ -2833,7 +2830,7 @@ static int fbcon_scrolldelta(struct vc_data *vc, int lines) logo_shown = FBCON_LOGO_CANSHOW; } fbcon_cursor(vc, CM_ERASE | CM_SOFTBACK); - fbcon_redraw_softback(vc, p, lines); + fbcon_redraw_softback(vc, disp, lines); fbcon_cursor(vc, CM_DRAW | CM_SOFTBACK); return 0; } @@ -2855,9 +2852,9 @@ static int fbcon_scrolldelta(struct vc_data *vc, int lines) fbcon_cursor(vc, CM_ERASE); - offset = p->yscroll - scrollback_current; - limit = p->vrows; - switch (p->scrollmode) { + offset = disp->yscroll - scrollback_current; + limit = disp->vrows; + switch (disp->scrollmode) { case SCROLL_WRAP_MOVE: info->var.vmode |= FB_VMODE_YWRAP; break; diff --git a/drivers/video/console/fbcon.h b/drivers/video/console/fbcon.h index 8e6ef4bc7a5..3706307e70e 100644 --- a/drivers/video/console/fbcon.h +++ b/drivers/video/console/fbcon.h @@ -93,10 +93,6 @@ struct fbcon_ops { (((s) >> (fgshift)) & 0x0f) #define attr_bgcol(bgshift,s) \ (((s) >> (bgshift)) & 0x0f) -#define attr_bgcol_ec(bgshift,vc) \ - ((vc) ? (((vc)->vc_video_erase_char >> (bgshift)) & 0x0f) : 0) -#define attr_fgcol_ec(fgshift,vc) \ - ((vc) ? (((vc)->vc_video_erase_char >> (fgshift)) & 0x0f) : 0) /* Monochrome */ #define attr_bold(s) \ @@ -108,6 +104,49 @@ struct fbcon_ops { #define attr_blink(s) \ ((s) & 0x8000) +#define mono_col(info) \ + (~(0xfff << (max((info)->var.green.length, \ + max((info)->var.red.length, \ + (info)->var.blue.length)))) & 0xff) + +static inline int attr_col_ec(int shift, struct vc_data *vc, + struct fb_info *info, int is_fg) +{ + int is_mono01; + int col; + int fg; + int bg; + + if (!vc) + return 0; + + if (vc->vc_can_do_color) + return is_fg ? attr_fgcol(shift,vc->vc_video_erase_char) + : attr_bgcol(shift,vc->vc_video_erase_char); + + if (!info) + return 0; + + col = mono_col(info); + is_mono01 = info->fix.visual == FB_VISUAL_MONO01; + + if (attr_reverse(vc->vc_video_erase_char)) { + fg = is_mono01 ? col : 0; + bg = is_mono01 ? 0 : col; + } + else { + fg = is_mono01 ? 0 : col; + bg = is_mono01 ? col : 0; + } + + return is_fg ? fg : bg; +} + +#define attr_bgcol_ec(bgshift,vc,info) \ + attr_col_ec(bgshift,vc,info,0); +#define attr_fgcol_ec(fgshift,vc,info) \ + attr_col_ec(fgshift,vc,info,1); + /* Font */ #define REFCOUNT(fd) (((int *)(fd))[-1]) #define FNTSIZE(fd) (((int *)(fd))[-2]) diff --git a/drivers/video/console/fbcon_ccw.c b/drivers/video/console/fbcon_ccw.c index 825e6d6972a..bdf913ecf00 100644 --- a/drivers/video/console/fbcon_ccw.c +++ b/drivers/video/console/fbcon_ccw.c @@ -84,7 +84,7 @@ static void ccw_clear(struct vc_data *vc, struct fb_info *info, int sy, int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; u32 vyres = GETVYRES(ops->p->scrollmode, info); - region.color = attr_bgcol_ec(bgshift,vc); + region.color = attr_bgcol_ec(bgshift,vc,info); region.dx = sy * vc->vc_font.height; region.dy = vyres - ((sx + width) * vc->vc_font.width); region.height = width * vc->vc_font.width; @@ -198,7 +198,7 @@ static void ccw_clear_margins(struct vc_data *vc, struct fb_info *info, struct fb_fillrect region; int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; - region.color = attr_bgcol_ec(bgshift,vc); + region.color = attr_bgcol_ec(bgshift,vc,info); region.rop = ROP_COPY; if (rw && !bottom_only) { diff --git a/drivers/video/console/fbcon_cw.c b/drivers/video/console/fbcon_cw.c index c637e631880..a6819b9d177 100644 --- a/drivers/video/console/fbcon_cw.c +++ b/drivers/video/console/fbcon_cw.c @@ -70,7 +70,7 @@ static void cw_clear(struct vc_data *vc, struct fb_info *info, int sy, int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; u32 vxres = GETVXRES(ops->p->scrollmode, info); - region.color = attr_bgcol_ec(bgshift,vc); + region.color = attr_bgcol_ec(bgshift,vc,info); region.dx = vxres - ((sy + height) * vc->vc_font.height); region.dy = sx * vc->vc_font.width; region.height = width * vc->vc_font.width; @@ -182,7 +182,7 @@ static void cw_clear_margins(struct vc_data *vc, struct fb_info *info, struct fb_fillrect region; int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; - region.color = attr_bgcol_ec(bgshift,vc); + region.color = attr_bgcol_ec(bgshift,vc,info); region.rop = ROP_COPY; if (rw && !bottom_only) { diff --git a/drivers/video/console/fbcon_ud.c b/drivers/video/console/fbcon_ud.c index 1473506df5d..d9b5d6eb68a 100644 --- a/drivers/video/console/fbcon_ud.c +++ b/drivers/video/console/fbcon_ud.c @@ -71,7 +71,7 @@ static void ud_clear(struct vc_data *vc, struct fb_info *info, int sy, u32 vyres = GETVYRES(ops->p->scrollmode, info); u32 vxres = GETVXRES(ops->p->scrollmode, info); - region.color = attr_bgcol_ec(bgshift,vc); + region.color = attr_bgcol_ec(bgshift,vc,info); region.dy = vyres - ((sy + height) * vc->vc_font.height); region.dx = vxres - ((sx + width) * vc->vc_font.width); region.width = width * vc->vc_font.width; @@ -228,7 +228,7 @@ static void ud_clear_margins(struct vc_data *vc, struct fb_info *info, struct fb_fillrect region; int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; - region.color = attr_bgcol_ec(bgshift,vc); + region.color = attr_bgcol_ec(bgshift,vc,info); region.rop = ROP_COPY; if (rw && !bottom_only) { diff --git a/drivers/video/console/fonts.c b/drivers/video/console/fonts.c index 96979c37751..d0c03fd7087 100644 --- a/drivers/video/console/fonts.c +++ b/drivers/video/console/fonts.c @@ -15,7 +15,7 @@ #include <linux/module.h> #include <linux/types.h> #include <linux/string.h> -#if defined(__mc68000__) || defined(CONFIG_APUS) +#if defined(__mc68000__) #include <asm/setup.h> #endif #include <linux/font.h> @@ -120,7 +120,7 @@ const struct font_desc *get_default_font(int xres, int yres, u32 font_w, for(i=0; i<num_fonts; i++) { f = fonts[i]; c = f->pref; -#if defined(__mc68000__) || defined(CONFIG_APUS) +#if defined(__mc68000__) #ifdef CONFIG_FONT_PEARL_8x8 if (MACH_IS_AMIGA && f->idx == PEARL8x8_IDX) c = 100; diff --git a/drivers/video/console/tileblit.c b/drivers/video/console/tileblit.c index d981fe4d86c..0056a41e5c3 100644 --- a/drivers/video/console/tileblit.c +++ b/drivers/video/console/tileblit.c @@ -40,8 +40,8 @@ static void tile_clear(struct vc_data *vc, struct fb_info *info, int sy, rect.index = vc->vc_video_erase_char & ((vc->vc_hi_font_mask) ? 0x1ff : 0xff); - rect.fg = attr_fgcol_ec(fgshift, vc); - rect.bg = attr_bgcol_ec(bgshift, vc); + rect.fg = attr_fgcol_ec(fgshift, vc, info); + rect.bg = attr_bgcol_ec(bgshift, vc, info); rect.sx = sx; rect.sy = sy; rect.width = width; diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index f65bcd314d5..6df29a62d72 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -1153,8 +1153,6 @@ static int vgacon_do_font_op(struct vgastate *state,char *arg,int set,int ch512) /* if 512 char mode is already enabled don't re-enable it. */ if ((set) && (ch512 != vga_512_chars)) { - int i; - /* attribute controller */ for (i = 0; i < MAX_NR_CONSOLES; i++) { struct vc_data *c = vc_cons[i].d; diff --git a/drivers/video/fb_defio.c b/drivers/video/fb_defio.c index a0c5d9d90d7..0f8cfb988c9 100644 --- a/drivers/video/fb_defio.c +++ b/drivers/video/fb_defio.c @@ -25,8 +25,8 @@ #include <linux/pagemap.h> /* this is to find and return the vmalloc-ed fb pages */ -static struct page* fb_deferred_io_nopage(struct vm_area_struct *vma, - unsigned long vaddr, int *type) +static int fb_deferred_io_fault(struct vm_area_struct *vma, + struct vm_fault *vmf) { unsigned long offset; struct page *page; @@ -34,18 +34,17 @@ static struct page* fb_deferred_io_nopage(struct vm_area_struct *vma, /* info->screen_base is in System RAM */ void *screen_base = (void __force *) info->screen_base; - offset = (vaddr - vma->vm_start) + (vma->vm_pgoff << PAGE_SHIFT); + offset = vmf->pgoff << PAGE_SHIFT; if (offset >= info->fix.smem_len) - return NOPAGE_SIGBUS; + return VM_FAULT_SIGBUS; page = vmalloc_to_page(screen_base + offset); if (!page) - return NOPAGE_OOM; + return VM_FAULT_SIGBUS; get_page(page); - if (type) - *type = VM_FAULT_MINOR; - return page; + vmf->page = page; + return 0; } int fb_deferred_io_fsync(struct file *file, struct dentry *dentry, int datasync) @@ -84,7 +83,7 @@ static int fb_deferred_io_mkwrite(struct vm_area_struct *vma, } static struct vm_operations_struct fb_deferred_io_vm_ops = { - .nopage = fb_deferred_io_nopage, + .fault = fb_deferred_io_fault, .page_mkwrite = fb_deferred_io_mkwrite, }; diff --git a/drivers/video/fb_draw.h b/drivers/video/fb_draw.h index cdafbe14ef1..a2a0618d86a 100644 --- a/drivers/video/fb_draw.h +++ b/drivers/video/fb_draw.h @@ -91,6 +91,7 @@ static inline unsigned long fb_rev_pixels_in_long(unsigned long val, val = comp(val >> 2, val << 2, REV_PIXELS_MASK2); if (bswapmask & 3) val = comp(val >> 4, val << 4, REV_PIXELS_MASK4); + return val; } static inline u32 fb_shifted_pixels_mask_u32(u32 index, u32 bswapmask) diff --git a/drivers/video/fbmon.c b/drivers/video/fbmon.c index 4ba9c089441..052e1805849 100644 --- a/drivers/video/fbmon.c +++ b/drivers/video/fbmon.c @@ -4,7 +4,7 @@ * Copyright (C) 2002 James Simmons <jsimmons@users.sf.net> * * Credits: - * + * * The EDID Parser is a conglomeration from the following sources: * * 1. SciTech SNAP Graphics Architecture @@ -12,13 +12,13 @@ * * 2. XFree86 4.3.0, interpret_edid.c * Copyright 1998 by Egbert Eich <Egbert.Eich@Physik.TU-Darmstadt.DE> - * - * 3. John Fremlin <vii@users.sourceforge.net> and + * + * 3. John Fremlin <vii@users.sourceforge.net> and * Ani Joshi <ajoshi@unixbox.com> - * + * * Generalized Timing Formula is derived from: * - * GTF Spreadsheet by Andy Morrish (1/5/97) + * GTF Spreadsheet by Andy Morrish (1/5/97) * available at http://www.vesa.org * * This file is subject to the terms and conditions of the GNU General Public @@ -36,7 +36,7 @@ #endif #include "edid.h" -/* +/* * EDID parser */ @@ -160,8 +160,8 @@ static int check_edid(unsigned char *edid) for (i = 0; i < ARRAY_SIZE(brokendb); i++) { if (!strncmp(manufacturer, brokendb[i].manufacturer, 4) && brokendb[i].model == model) { - fix = brokendb[i].fix; - break; + fix = brokendb[i].fix; + break; } } @@ -323,7 +323,7 @@ static void get_dpms_capabilities(unsigned char flags, (flags & DPMS_SUSPEND) ? "yes" : "no", (flags & DPMS_STANDBY) ? "yes" : "no"); } - + static void get_chroma(unsigned char *block, struct fb_monspecs *specs) { int tmp; @@ -365,7 +365,7 @@ static void get_chroma(unsigned char *block, struct fb_monspecs *specs) tmp += 512; specs->chroma.bluey = tmp/1024; DPRINTK("BlueY: 0.%03d\n", specs->chroma.bluey); - + tmp = ((block[6] & (3 << 2)) >> 2) | (block[0xd] << 2); tmp *= 1000; tmp += 512; @@ -383,7 +383,7 @@ static void calc_mode_timings(int xres, int yres, int refresh, struct fb_videomode *mode) { struct fb_var_screeninfo *var; - + var = kzalloc(sizeof(struct fb_var_screeninfo), GFP_KERNEL); if (var) { @@ -451,11 +451,11 @@ static int get_est_timing(unsigned char *block, struct fb_videomode *mode) c = block[1]; if (c&0x80) { - mode[num++] = vesa_modes[9]; + mode[num++] = vesa_modes[9]; DPRINTK(" 800x600@72Hz\n"); } if (c&0x40) { - mode[num++] = vesa_modes[10]; + mode[num++] = vesa_modes[10]; DPRINTK(" 800x600@75Hz\n"); } if (c&0x20) { @@ -495,7 +495,7 @@ static int get_est_timing(unsigned char *block, struct fb_videomode *mode) static int get_std_timing(unsigned char *block, struct fb_videomode *mode) { int xres, yres = 0, refresh, ratio, i; - + xres = (block[0] + 31) * 8; if (xres <= 256) return 0; @@ -519,7 +519,7 @@ static int get_std_timing(unsigned char *block, struct fb_videomode *mode) DPRINTK(" %dx%d@%dHz\n", xres, yres, refresh); for (i = 0; i < VESA_MODEDB_SIZE; i++) { - if (vesa_modes[i].xres == xres && + if (vesa_modes[i].xres == xres && vesa_modes[i].yres == yres && vesa_modes[i].refresh == refresh) { *mode = vesa_modes[i]; @@ -536,13 +536,13 @@ static int get_dst_timing(unsigned char *block, { int j, num = 0; - for (j = 0; j < 6; j++, block+= STD_TIMING_DESCRIPTION_SIZE) + for (j = 0; j < 6; j++, block += STD_TIMING_DESCRIPTION_SIZE) num += get_std_timing(block, &mode[num]); return num; } -static void get_detailed_timing(unsigned char *block, +static void get_detailed_timing(unsigned char *block, struct fb_videomode *mode) { mode->xres = H_ACTIVE; @@ -553,7 +553,7 @@ static void get_detailed_timing(unsigned char *block, mode->right_margin = H_SYNC_OFFSET; mode->left_margin = (H_ACTIVE + H_BLANKING) - (H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH); - mode->upper_margin = V_BLANKING - V_SYNC_OFFSET - + mode->upper_margin = V_BLANKING - V_SYNC_OFFSET - V_SYNC_WIDTH; mode->lower_margin = V_SYNC_OFFSET; mode->hsync_len = H_SYNC_WIDTH; @@ -597,7 +597,7 @@ static struct fb_videomode *fb_create_modedb(unsigned char *edid, int *dbsize) if (mode == NULL) return NULL; - if (edid == NULL || !edid_checksum(edid) || + if (edid == NULL || !edid_checksum(edid) || !edid_check_header(edid)) { kfree(mode); return NULL; @@ -632,7 +632,7 @@ static struct fb_videomode *fb_create_modedb(unsigned char *edid, int *dbsize) if (block[0] == 0x00 && block[1] == 0x00 && block[3] == 0xfa) num += get_dst_timing(block + 5, &mode[num]); } - + /* Yikes, EDID data is totally useless */ if (!num) { kfree(mode); @@ -686,7 +686,7 @@ static int fb_get_monitor_limits(unsigned char *edid, struct fb_monspecs *specs) /* estimate monitor limits based on modes supported */ if (retval) { struct fb_videomode *modes, *mode; - int num_modes, i, hz, hscan, pixclock; + int num_modes, hz, hscan, pixclock; int vtotal, htotal; modes = fb_create_modedb(edid, &num_modes); @@ -713,7 +713,7 @@ static int fb_get_monitor_limits(unsigned char *edid, struct fb_monspecs *specs) hscan = (pixclock + htotal / 2) / htotal; hscan = (hscan + 500) / 1000 * 1000; hz = (hscan + vtotal / 2) / vtotal; - + if (specs->dclkmax == 0 || specs->dclkmax < pixclock) specs->dclkmax = pixclock; @@ -966,8 +966,8 @@ void fb_edid_to_monspecs(unsigned char *edid, struct fb_monspecs *specs) DPRINTK("========================================\n"); } -/* - * VESA Generalized Timing Formula (GTF) +/* + * VESA Generalized Timing Formula (GTF) */ #define FLYBACK 550 @@ -996,7 +996,7 @@ struct __fb_timings { * @hfreq: horizontal freq * * DESCRIPTION: - * vblank = right_margin + vsync_len + left_margin + * vblank = right_margin + vsync_len + left_margin * * given: right_margin = 1 (V_FRONTPORCH) * vsync_len = 3 @@ -1010,12 +1010,12 @@ static u32 fb_get_vblank(u32 hfreq) { u32 vblank; - vblank = (hfreq * FLYBACK)/1000; + vblank = (hfreq * FLYBACK)/1000; vblank = (vblank + 500)/1000; return (vblank + V_FRONTPORCH); } -/** +/** * fb_get_hblank_by_freq - get horizontal blank time given hfreq * @hfreq: horizontal freq * @xres: horizontal resolution in pixels @@ -1031,7 +1031,7 @@ static u32 fb_get_vblank(u32 hfreq) * * where: C = ((offset - scale factor) * blank_scale) * -------------------------------------- + scale factor - * 256 + * 256 * M = blank_scale * gradient * */ @@ -1039,7 +1039,7 @@ static u32 fb_get_hblank_by_hfreq(u32 hfreq, u32 xres) { u32 c_val, m_val, duty_cycle, hblank; - c_val = (((H_OFFSET - H_SCALEFACTOR) * H_BLANKSCALE)/256 + + c_val = (((H_OFFSET - H_SCALEFACTOR) * H_BLANKSCALE)/256 + H_SCALEFACTOR) * 1000; m_val = (H_BLANKSCALE * H_GRADIENT)/256; m_val = (m_val * 1000000)/hfreq; @@ -1048,7 +1048,7 @@ static u32 fb_get_hblank_by_hfreq(u32 hfreq, u32 xres) return (hblank); } -/** +/** * fb_get_hblank_by_dclk - get horizontal blank time given pixelclock * @dclk: pixelclock in Hz * @xres: horizontal resolution in pixels @@ -1061,7 +1061,7 @@ static u32 fb_get_hblank_by_hfreq(u32 hfreq, u32 xres) * * duty cycle = percent of htotal assigned to inactive display * duty cycle = C - (M * h_period) - * + * * where: h_period = SQRT(100 - C + (0.4 * xres * M)/dclk) + C - 100 * ----------------------------------------------- * 2 * M @@ -1077,11 +1077,11 @@ static u32 fb_get_hblank_by_dclk(u32 dclk, u32 xres) h_period = 100 - C_VAL; h_period *= h_period; h_period += (M_VAL * xres * 2 * 1000)/(5 * dclk); - h_period *=10000; + h_period *= 10000; h_period = int_sqrt(h_period); h_period -= (100 - C_VAL) * 100; - h_period *= 1000; + h_period *= 1000; h_period /= 2 * M_VAL; duty_cycle = C_VAL * 1000 - (M_VAL * h_period)/100; @@ -1089,7 +1089,7 @@ static u32 fb_get_hblank_by_dclk(u32 dclk, u32 xres) hblank &= ~15; return (hblank); } - + /** * fb_get_hfreq - estimate hsync * @vfreq: vertical refresh rate @@ -1100,13 +1100,13 @@ static u32 fb_get_hblank_by_dclk(u32 dclk, u32 xres) * (yres + front_port) * vfreq * 1000000 * hfreq = ------------------------------------- * (1000000 - (vfreq * FLYBACK) - * + * */ static u32 fb_get_hfreq(u32 vfreq, u32 yres) { u32 divisor, hfreq; - + divisor = (1000000 - (vfreq * FLYBACK))/1000; hfreq = (yres + V_FRONTPORCH) * vfreq * 1000; return (hfreq/divisor); @@ -1117,7 +1117,7 @@ static void fb_timings_vfreq(struct __fb_timings *timings) timings->hfreq = fb_get_hfreq(timings->vfreq, timings->vactive); timings->vblank = fb_get_vblank(timings->hfreq); timings->vtotal = timings->vactive + timings->vblank; - timings->hblank = fb_get_hblank_by_hfreq(timings->hfreq, + timings->hblank = fb_get_hblank_by_hfreq(timings->hfreq, timings->hactive); timings->htotal = timings->hactive + timings->hblank; timings->dclk = timings->htotal * timings->hfreq; @@ -1128,7 +1128,7 @@ static void fb_timings_hfreq(struct __fb_timings *timings) timings->vblank = fb_get_vblank(timings->hfreq); timings->vtotal = timings->vactive + timings->vblank; timings->vfreq = timings->hfreq/timings->vtotal; - timings->hblank = fb_get_hblank_by_hfreq(timings->hfreq, + timings->hblank = fb_get_hblank_by_hfreq(timings->hfreq, timings->hactive); timings->htotal = timings->hactive + timings->hblank; timings->dclk = timings->htotal * timings->hfreq; @@ -1136,7 +1136,7 @@ static void fb_timings_hfreq(struct __fb_timings *timings) static void fb_timings_dclk(struct __fb_timings *timings) { - timings->hblank = fb_get_hblank_by_dclk(timings->dclk, + timings->hblank = fb_get_hblank_by_dclk(timings->dclk, timings->hactive); timings->htotal = timings->hactive + timings->hblank; timings->hfreq = timings->dclk/timings->htotal; @@ -1156,29 +1156,29 @@ static void fb_timings_dclk(struct __fb_timings *timings) * @info: pointer to fb_info * * DESCRIPTION: - * Calculates video mode based on monitor specs using VESA GTF. - * The GTF is best for VESA GTF compliant monitors but is + * Calculates video mode based on monitor specs using VESA GTF. + * The GTF is best for VESA GTF compliant monitors but is * specifically formulated to work for older monitors as well. * - * If @flag==0, the function will attempt to maximize the + * If @flag==0, the function will attempt to maximize the * refresh rate. Otherwise, it will calculate timings based on - * the flag and accompanying value. + * the flag and accompanying value. * - * If FB_IGNOREMON bit is set in @flags, monitor specs will be + * If FB_IGNOREMON bit is set in @flags, monitor specs will be * ignored and @var will be filled with the calculated timings. * * All calculations are based on the VESA GTF Spreadsheet * available at VESA's public ftp (http://www.vesa.org). - * + * * NOTES: * The timings generated by the GTF will be different from VESA * DMT. It might be a good idea to keep a table of standard * VESA modes as well. The GTF may also not work for some displays, * such as, and especially, analog TV. - * + * * REQUIRES: * A valid info->monspecs, otherwise 'safe numbers' will be used. - */ + */ int fb_get_mode(int flags, u32 val, struct fb_var_screeninfo *var, struct fb_info *info) { struct __fb_timings *timings; @@ -1191,7 +1191,7 @@ int fb_get_mode(int flags, u32 val, struct fb_var_screeninfo *var, struct fb_inf if (!timings) return -ENOMEM; - /* + /* * If monspecs are invalid, use values that are enough * for 640x480@60 */ @@ -1214,7 +1214,7 @@ int fb_get_mode(int flags, u32 val, struct fb_var_screeninfo *var, struct fb_inf timings->hactive = var->xres; timings->vactive = var->yres; - if (var->vmode & FB_VMODE_INTERLACED) { + if (var->vmode & FB_VMODE_INTERLACED) { timings->vactive /= 2; interlace = 2; } @@ -1250,9 +1250,9 @@ int fb_get_mode(int flags, u32 val, struct fb_var_screeninfo *var, struct fb_inf break; default: err = -EINVAL; - - } - + + } + if (err || (!(flags & FB_IGNOREMON) && (timings->vfreq < vfmin || timings->vfreq > vfmax || timings->hfreq < hfmin || timings->hfreq > hfmax || @@ -1269,7 +1269,7 @@ int fb_get_mode(int flags, u32 val, struct fb_var_screeninfo *var, struct fb_inf var->upper_margin = (timings->vblank * interlace)/dscan - (var->vsync_len + var->lower_margin); } - + kfree(timings); return err; } @@ -1291,7 +1291,7 @@ int fb_get_mode(int flags, u32 val, struct fb_var_screeninfo *var, return -EINVAL; } #endif /* CONFIG_FB_MODE_HELPERS */ - + /* * fb_validate_mode - validates var against monitor capabilities * @var: pointer to fb_var_screeninfo @@ -1309,7 +1309,7 @@ int fb_validate_mode(const struct fb_var_screeninfo *var, struct fb_info *info) u32 hfreq, vfreq, htotal, vtotal, pixclock; u32 hfmin, hfmax, vfmin, vfmax, dclkmin, dclkmax; - /* + /* * If monspecs are invalid, use values that are enough * for 640x480@60 */ @@ -1333,10 +1333,10 @@ int fb_validate_mode(const struct fb_var_screeninfo *var, struct fb_info *info) if (!var->pixclock) return -EINVAL; pixclock = PICOS2KHZ(var->pixclock) * 1000; - - htotal = var->xres + var->right_margin + var->hsync_len + + + htotal = var->xres + var->right_margin + var->hsync_len + var->left_margin; - vtotal = var->yres + var->lower_margin + var->vsync_len + + vtotal = var->yres + var->lower_margin + var->vsync_len + var->upper_margin; if (var->vmode & FB_VMODE_INTERLACED) @@ -1349,7 +1349,7 @@ int fb_validate_mode(const struct fb_var_screeninfo *var, struct fb_info *info) vfreq = hfreq/vtotal; - return (vfreq < vfmin || vfreq > vfmax || + return (vfreq < vfmin || vfreq > vfmax || hfreq < hfmin || hfreq > hfmax || pixclock < dclkmin || pixclock > dclkmax) ? -EINVAL : 0; diff --git a/drivers/video/geode/lxfb_core.c b/drivers/video/geode/lxfb_core.c index 583185fd7c9..eb6b8817153 100644 --- a/drivers/video/geode/lxfb_core.c +++ b/drivers/video/geode/lxfb_core.c @@ -34,7 +34,7 @@ static int fbsize; * we try to make it something sane - 640x480-60 is sane */ -const struct fb_videomode geode_modedb[] __initdata = { +static const struct fb_videomode geode_modedb[] __initdata = { /* 640x480-60 */ { NULL, 60, 640, 480, 39682, 48, 8, 25, 2, 88, 2, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, diff --git a/drivers/video/hpfb.c b/drivers/video/hpfb.c index b18486ad8e1..2eb4fb15908 100644 --- a/drivers/video/hpfb.c +++ b/drivers/video/hpfb.c @@ -207,7 +207,8 @@ static struct fb_ops hpfb_ops = { #define HPFB_FBOMSB 0x5d /* Frame buffer offset */ #define HPFB_FBOLSB 0x5f -static int __init hpfb_init_one(unsigned long phys_base, unsigned long virt_base) +static int __devinit hpfb_init_one(unsigned long phys_base, + unsigned long virt_base) { unsigned long fboff, fb_width, fb_height, fb_start; diff --git a/drivers/video/i810/i810_main.c b/drivers/video/i810/i810_main.c index 1a7d7789d87..1d13dd099af 100644 --- a/drivers/video/i810/i810_main.c +++ b/drivers/video/i810/i810_main.c @@ -1476,7 +1476,7 @@ static int i810fb_cursor(struct fb_info *info, struct fb_cursor *cursor) struct i810fb_par *par = info->par; u8 __iomem *mmio = par->mmio_start_virtual; - if (!par->dev_flags & LOCKUP) + if (!(par->dev_flags & LOCKUP)) return -ENXIO; if (cursor->image.width > 64 || cursor->image.height > 64) diff --git a/drivers/video/igafb.c b/drivers/video/igafb.c index b87ea21d3d7..3a81060137a 100644 --- a/drivers/video/igafb.c +++ b/drivers/video/igafb.c @@ -400,6 +400,7 @@ int __init igafb_init(void) info = kzalloc(size, GFP_ATOMIC); if (!info) { printk("igafb_init: can't alloc fb_info\n"); + pci_dev_put(pdev); return -ENOMEM; } @@ -409,12 +410,14 @@ int __init igafb_init(void) if ((addr = pdev->resource[0].start) == 0) { printk("igafb_init: no memory start\n"); kfree(info); + pci_dev_put(pdev); return -ENXIO; } if ((info->screen_base = ioremap(addr, 1024*1024*2)) == 0) { printk("igafb_init: can't remap %lx[2M]\n", addr); kfree(info); + pci_dev_put(pdev); return -ENXIO; } @@ -449,6 +452,7 @@ int __init igafb_init(void) printk("igafb_init: can't remap %lx[4K]\n", igafb_fix.mmio_start); iounmap((void *)info->screen_base); kfree(info); + pci_dev_put(pdev); return -ENXIO; } @@ -466,6 +470,7 @@ int __init igafb_init(void) iounmap((void *)par->io_base); iounmap(info->screen_base); kfree(info); + pci_dev_put(pdev); return -ENOMEM; } diff --git a/drivers/video/intelfb/intelfbhw.c b/drivers/video/intelfb/intelfbhw.c index 5f6fb7d2c40..fa1fff55356 100644 --- a/drivers/video/intelfb/intelfbhw.c +++ b/drivers/video/intelfb/intelfbhw.c @@ -1971,7 +1971,7 @@ void intelfbhw_cursor_reset(struct intelfb_info *dinfo) static irqreturn_t intelfbhw_irq(int irq, void *dev_id) { u16 tmp; - struct intelfb_info *dinfo = (struct intelfb_info *)dev_id; + struct intelfb_info *dinfo = dev_id; spin_lock(&dinfo->int_lock); diff --git a/drivers/video/neofb.c b/drivers/video/neofb.c index 4b6a99b5be0..5246b0402d7 100644 --- a/drivers/video/neofb.c +++ b/drivers/video/neofb.c @@ -2066,40 +2066,49 @@ static struct fb_info *__devinit neo_alloc_fb_info(struct pci_dev *dev, const st switch (info->fix.accel) { case FB_ACCEL_NEOMAGIC_NM2070: - sprintf(info->fix.id, "MagicGraph 128"); + snprintf(info->fix.id, sizeof(info->fix.id), + "MagicGraph 128"); break; case FB_ACCEL_NEOMAGIC_NM2090: - sprintf(info->fix.id, "MagicGraph 128V"); + snprintf(info->fix.id, sizeof(info->fix.id), + "MagicGraph 128V"); break; case FB_ACCEL_NEOMAGIC_NM2093: - sprintf(info->fix.id, "MagicGraph 128ZV"); + snprintf(info->fix.id, sizeof(info->fix.id), + "MagicGraph 128ZV"); break; case FB_ACCEL_NEOMAGIC_NM2097: - sprintf(info->fix.id, "MagicGraph 128ZV+"); + snprintf(info->fix.id, sizeof(info->fix.id), + "MagicGraph 128ZV+"); break; case FB_ACCEL_NEOMAGIC_NM2160: - sprintf(info->fix.id, "MagicGraph 128XD"); + snprintf(info->fix.id, sizeof(info->fix.id), + "MagicGraph 128XD"); break; case FB_ACCEL_NEOMAGIC_NM2200: - sprintf(info->fix.id, "MagicGraph 256AV"); + snprintf(info->fix.id, sizeof(info->fix.id), + "MagicGraph 256AV"); info->flags |= FBINFO_HWACCEL_IMAGEBLIT | FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT; break; case FB_ACCEL_NEOMAGIC_NM2230: - sprintf(info->fix.id, "MagicGraph 256AV+"); + snprintf(info->fix.id, sizeof(info->fix.id), + "MagicGraph 256AV+"); info->flags |= FBINFO_HWACCEL_IMAGEBLIT | FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT; break; case FB_ACCEL_NEOMAGIC_NM2360: - sprintf(info->fix.id, "MagicGraph 256ZX"); + snprintf(info->fix.id, sizeof(info->fix.id), + "MagicGraph 256ZX"); info->flags |= FBINFO_HWACCEL_IMAGEBLIT | FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT; break; case FB_ACCEL_NEOMAGIC_NM2380: - sprintf(info->fix.id, "MagicGraph 256XL+"); + snprintf(info->fix.id, sizeof(info->fix.id), + "MagicGraph 256XL+"); info->flags |= FBINFO_HWACCEL_IMAGEBLIT | FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT; diff --git a/drivers/video/nvidia/nvidia.c b/drivers/video/nvidia/nvidia.c index 30e14eb1f51..74517b1b26a 100644 --- a/drivers/video/nvidia/nvidia.c +++ b/drivers/video/nvidia/nvidia.c @@ -849,9 +849,27 @@ static int nvidiafb_check_var(struct fb_var_screeninfo *var, if (!mode_valid && info->monspecs.modedb_len) return -EINVAL; + /* + * If we're on a flat panel, check if the mode is outside of the + * panel dimensions. If so, cap it and try for the next best mode + * before bailing out. + */ if (par->fpWidth && par->fpHeight && (par->fpWidth < var->xres || - par->fpHeight < var->yres)) - return -EINVAL; + par->fpHeight < var->yres)) { + const struct fb_videomode *mode; + + var->xres = par->fpWidth; + var->yres = par->fpHeight; + + mode = fb_find_best_mode(var, &info->modelist); + if (!mode) { + printk(KERN_ERR PFX "mode out of range of flat " + "panel dimensions\n"); + return -EINVAL; + } + + fb_videomode_to_var(var, mode); + } if (var->yres_virtual < var->yres) var->yres_virtual = var->yres; diff --git a/drivers/video/pm2fb.c b/drivers/video/pm2fb.c index 5591dfb22b1..30181b59382 100644 --- a/drivers/video/pm2fb.c +++ b/drivers/video/pm2fb.c @@ -1159,6 +1159,11 @@ static void pm2fb_imageblit(struct fb_info *info, const struct fb_image *image) u32 fgx, bgx; const u32 *src = (const u32 *)image->data; u32 xres = (info->var.xres + 31) & ~31; + int raster_mode = 1; /* invert bits */ + +#ifdef __LITTLE_ENDIAN + raster_mode |= 3 << 7; /* reverse byte order */ +#endif if (info->state != FBINFO_STATE_RUNNING) return; @@ -1208,9 +1213,8 @@ static void pm2fb_imageblit(struct fb_info *info, const struct fb_image *image) pm2_WR(par, PM2R_RENDER, PM2F_RENDER_RECTANGLE | PM2F_INCREASE_X | PM2F_INCREASE_Y); - /* BitMapPackEachScanline & invert bits and byte order*/ - /* force background */ - pm2_WR(par, PM2R_RASTERIZER_MODE, (1 << 9) | 1 | (3 << 7)); + /* BitMapPackEachScanline */ + pm2_WR(par, PM2R_RASTERIZER_MODE, raster_mode | (1 << 9)); pm2_WR(par, PM2R_CONSTANT_COLOR, fgx); pm2_WR(par, PM2R_RENDER, PM2F_RENDER_RECTANGLE | @@ -1224,8 +1228,7 @@ static void pm2fb_imageblit(struct fb_info *info, const struct fb_image *image) PM2F_RENDER_RECTANGLE | PM2F_RENDER_FASTFILL | PM2F_INCREASE_X | PM2F_INCREASE_Y); - /* invert bits and byte order*/ - pm2_WR(par, PM2R_RASTERIZER_MODE, 1 | (3 << 7)); + pm2_WR(par, PM2R_RASTERIZER_MODE, raster_mode); pm2_WR(par, PM2R_FB_BLOCK_COLOR, fgx); pm2_WR(par, PM2R_RENDER, PM2F_RENDER_RECTANGLE | diff --git a/drivers/video/pm3fb.c b/drivers/video/pm3fb.c index 070659992c1..5dba8cdd051 100644 --- a/drivers/video/pm3fb.c +++ b/drivers/video/pm3fb.c @@ -1227,7 +1227,7 @@ static struct fb_ops pm3fb_ops = { /* mmio register are already mapped when this function is called */ /* the pm3fb_fix.smem_start is also set */ -static unsigned long pm3fb_size_memory(struct pm3_par *par) +static unsigned long __devinit pm3fb_size_memory(struct pm3_par *par) { unsigned long memsize = 0; unsigned long tempBypass, i, temp1, temp2; diff --git a/drivers/video/pmag-aa-fb.c b/drivers/video/pmag-aa-fb.c index a864438b600..6515ec11c16 100644 --- a/drivers/video/pmag-aa-fb.c +++ b/drivers/video/pmag-aa-fb.c @@ -150,7 +150,7 @@ static int aafbcon_set_font(struct display *disp, int width, int height) { struct aafb_info *info = (struct aafb_info *)disp->fb_info; struct aafb_cursor *c = &info->cursor; - u8 fgc = ~attr_bgcol_ec(disp, disp->conp); + u8 fgc = ~attr_bgcol_ec(disp, disp->conp, &info->info); if (width > 64 || height > 64 || width < 0 || height < 0) return -EINVAL; diff --git a/drivers/video/ps3fb.c b/drivers/video/ps3fb.c index 044a423a72c..dc3af1c78c5 100644 --- a/drivers/video/ps3fb.c +++ b/drivers/video/ps3fb.c @@ -57,8 +57,6 @@ #define GPU_ALIGN_UP(x) _ALIGN_UP((x), 64) #define GPU_MAX_LINE_LENGTH (65536 - 64) -#define PS3FB_FULL_MODE_BIT 0x80 - #define GPU_INTR_STATUS_VSYNC_0 0 /* vsync on head A */ #define GPU_INTR_STATUS_VSYNC_1 1 /* vsync on head B */ #define GPU_INTR_STATUS_FLIP_0 3 /* flip head A */ @@ -118,8 +116,6 @@ struct ps3fb_priv { unsigned int irq_no; u64 context_handle, memory_handle; - void *xdr_ea; - size_t xdr_size; struct gpu_driver_info *dinfo; u64 vblank_count; /* frame count */ @@ -136,42 +132,19 @@ static struct ps3fb_priv ps3fb; struct ps3fb_par { u32 pseudo_palette[16]; int mode_id, new_mode_id; - int res_index; unsigned int num_frames; /* num of frame buffers */ unsigned int width; unsigned int height; - unsigned long full_offset; /* start of fullscreen DDR fb */ - unsigned long fb_offset; /* start of actual DDR fb */ - unsigned long pan_offset; + unsigned int ddr_line_length; + unsigned int ddr_frame_size; + unsigned int xdr_frame_size; + unsigned int full_offset; /* start of fullscreen DDR fb */ + unsigned int fb_offset; /* start of actual DDR fb */ + unsigned int pan_offset; }; -struct ps3fb_res_table { - u32 xres; - u32 yres; - u32 xoff; - u32 yoff; - u32 type; -}; -#define PS3FB_RES_FULL 1 -static const struct ps3fb_res_table ps3fb_res[] = { - /* res_x,y margin_x,y full */ - { 720, 480, 72, 48 , 0}, - { 720, 576, 72, 58 , 0}, - { 1280, 720, 78, 38 , 0}, - { 1920, 1080, 116, 58 , 0}, - /* full mode */ - { 720, 480, 0, 0 , PS3FB_RES_FULL}, - { 720, 576, 0, 0 , PS3FB_RES_FULL}, - { 1280, 720, 0, 0 , PS3FB_RES_FULL}, - { 1920, 1080, 0, 0 , PS3FB_RES_FULL}, - /* vesa: normally full mode */ - { 1280, 768, 0, 0 , 0}, - { 1280, 1024, 0, 0 , 0}, - { 1920, 1200, 0, 0 , 0}, - { 0, 0, 0, 0 , 0} }; - -/* default resolution */ -#define GPU_RES_INDEX 0 /* 720 x 480 */ + +#define FIRST_NATIVE_MODE_INDEX 10 static const struct fb_videomode ps3fb_modedb[] = { /* 60 Hz broadcast modes (modes "1" to "5") */ @@ -211,7 +184,7 @@ static const struct fb_videomode ps3fb_modedb[] = { "720p", 50, 1124, 644, 13468, 298, 478, 57, 44, 80, 5, FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED }, { - /* 1080 */ + /* 1080i */ "1080i", 50, 1688, 964, 13468, 264, 600, 94, 62, 88, 5, FB_SYNC_BROADCAST, FB_VMODE_INTERLACED }, { @@ -220,24 +193,7 @@ static const struct fb_videomode ps3fb_modedb[] = { FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED }, - /* VESA modes (modes "11" to "13") */ - { - /* WXGA */ - "wxga", 60, 1280, 768, 12924, 160, 24, 29, 3, 136, 6, - 0, FB_VMODE_NONINTERLACED, - FB_MODE_IS_VESA - }, { - /* SXGA */ - "sxga", 60, 1280, 1024, 9259, 248, 48, 38, 1, 112, 3, - FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, - FB_MODE_IS_VESA - }, { - /* WUXGA */ - "wuxga", 60, 1920, 1200, 6494, 80, 48, 26, 3, 32, 6, - FB_SYNC_HOR_HIGH_ACT, FB_VMODE_NONINTERLACED, - FB_MODE_IS_VESA - }, - + [FIRST_NATIVE_MODE_INDEX] = /* 60 Hz broadcast modes (full resolution versions of modes "1" to "5") */ { /* 480if */ @@ -276,12 +232,30 @@ static const struct fb_videomode ps3fb_modedb[] = { FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED }, { /* 1080if */ - "1080f", 50, 1920, 1080, 13468, 148, 484, 36, 4, 88, 5, + "1080if", 50, 1920, 1080, 13468, 148, 484, 36, 4, 88, 5, FB_SYNC_BROADCAST, FB_VMODE_INTERLACED }, { /* 1080pf */ "1080pf", 50, 1920, 1080, 6734, 148, 484, 36, 4, 88, 5, FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED + }, + + /* VESA modes (modes "11" to "13") */ + { + /* WXGA */ + "wxga", 60, 1280, 768, 12924, 160, 24, 29, 3, 136, 6, + 0, FB_VMODE_NONINTERLACED, + FB_MODE_IS_VESA + }, { + /* SXGA */ + "sxga", 60, 1280, 1024, 9259, 248, 48, 38, 1, 112, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, + FB_MODE_IS_VESA + }, { + /* WUXGA */ + "wuxga", 60, 1920, 1200, 6494, 80, 48, 26, 3, 32, 6, + FB_SYNC_HOR_HIGH_ACT, FB_VMODE_NONINTERLACED, + FB_MODE_IS_VESA } }; @@ -289,110 +263,188 @@ static const struct fb_videomode ps3fb_modedb[] = { #define HEAD_A #define HEAD_B -#define X_OFF(i) (ps3fb_res[i].xoff) /* left/right margin (pixel) */ -#define Y_OFF(i) (ps3fb_res[i].yoff) /* top/bottom margin (pixel) */ -#define WIDTH(i) (ps3fb_res[i].xres) /* width of FB */ -#define HEIGHT(i) (ps3fb_res[i].yres) /* height of FB */ #define BPP 4 /* number of bytes per pixel */ -/* Start of the virtual frame buffer (relative to fullscreen ) */ -#define VP_OFF(i) ((WIDTH(i) * Y_OFF(i) + X_OFF(i)) * BPP) - static int ps3fb_mode; module_param(ps3fb_mode, int, 0); static char *mode_option __devinitdata; -static int ps3fb_get_res_table(u32 xres, u32 yres, int mode) +static int ps3fb_cmp_mode(const struct fb_videomode *vmode, + const struct fb_var_screeninfo *var) { - int full_mode; - unsigned int i; - u32 x, y, f; - - full_mode = (mode & PS3FB_FULL_MODE_BIT) ? PS3FB_RES_FULL : 0; - for (i = 0;; i++) { - x = ps3fb_res[i].xres; - y = ps3fb_res[i].yres; - f = ps3fb_res[i].type; - - if (!x) { - pr_debug("ERROR: ps3fb_get_res_table()\n"); - return -1; - } + long xres, yres, left_margin, right_margin, upper_margin, lower_margin; + long dx, dy; + + /* maximum values */ + if (var->xres > vmode->xres || var->yres > vmode->yres || + var->pixclock > vmode->pixclock || + var->hsync_len > vmode->hsync_len || + var->vsync_len > vmode->vsync_len) + return -1; - if (full_mode == PS3FB_RES_FULL && f != PS3FB_RES_FULL) - continue; + /* progressive/interlaced must match */ + if ((var->vmode & FB_VMODE_MASK) != vmode->vmode) + return -1; - if (x == xres && (yres == 0 || y == yres)) - break; + /* minimum resolution */ + xres = max(var->xres, 1U); + yres = max(var->yres, 1U); + + /* minimum margins */ + left_margin = max(var->left_margin, vmode->left_margin); + right_margin = max(var->right_margin, vmode->right_margin); + upper_margin = max(var->upper_margin, vmode->upper_margin); + lower_margin = max(var->lower_margin, vmode->lower_margin); + + /* resolution + margins may not exceed native parameters */ + dx = ((long)vmode->left_margin + (long)vmode->xres + + (long)vmode->right_margin) - + (left_margin + xres + right_margin); + if (dx < 0) + return -1; - x = x - 2 * ps3fb_res[i].xoff; - y = y - 2 * ps3fb_res[i].yoff; - if (x == xres && (yres == 0 || y == yres)) - break; + dy = ((long)vmode->upper_margin + (long)vmode->yres + + (long)vmode->lower_margin) - + (upper_margin + yres + lower_margin); + if (dy < 0) + return -1; + + /* exact match */ + if (!dx && !dy) + return 0; + + /* resolution difference */ + return (vmode->xres - xres) * (vmode->yres - yres); +} + +static const struct fb_videomode *ps3fb_native_vmode(enum ps3av_mode_num id) +{ + return &ps3fb_modedb[FIRST_NATIVE_MODE_INDEX + id - 1]; +} + +static const struct fb_videomode *ps3fb_vmode(int id) +{ + u32 mode = id & PS3AV_MODE_MASK; + + if (mode < PS3AV_MODE_480I || mode > PS3AV_MODE_WUXGA) + return NULL; + + if (mode <= PS3AV_MODE_1080P50 && !(id & PS3AV_MODE_FULL)) { + /* Non-fullscreen broadcast mode */ + return &ps3fb_modedb[mode - 1]; } - return i; + + return ps3fb_native_vmode(mode); } -static unsigned int ps3fb_find_mode(const struct fb_var_screeninfo *var, +static unsigned int ps3fb_find_mode(struct fb_var_screeninfo *var, u32 *ddr_line_length, u32 *xdr_line_length) { - unsigned int i, mode; - - for (i = 0; i < ARRAY_SIZE(ps3fb_modedb); i++) - if (var->xres == ps3fb_modedb[i].xres && - var->yres == ps3fb_modedb[i].yres && - var->pixclock == ps3fb_modedb[i].pixclock && - var->hsync_len == ps3fb_modedb[i].hsync_len && - var->vsync_len == ps3fb_modedb[i].vsync_len && - var->left_margin == ps3fb_modedb[i].left_margin && - var->right_margin == ps3fb_modedb[i].right_margin && - var->upper_margin == ps3fb_modedb[i].upper_margin && - var->lower_margin == ps3fb_modedb[i].lower_margin && - var->sync == ps3fb_modedb[i].sync && - (var->vmode & FB_VMODE_MASK) == ps3fb_modedb[i].vmode) - goto found; - - pr_debug("ps3fb_find_mode: mode not found\n"); - return 0; + unsigned int id, best_id; + int diff, best_diff; + const struct fb_videomode *vmode; + long gap; + + best_id = 0; + best_diff = INT_MAX; + pr_debug("%s: wanted %u [%u] %u x %u [%u] %u\n", __func__, + var->left_margin, var->xres, var->right_margin, + var->upper_margin, var->yres, var->lower_margin); + for (id = PS3AV_MODE_480I; id <= PS3AV_MODE_WUXGA; id++) { + vmode = ps3fb_native_vmode(id); + diff = ps3fb_cmp_mode(vmode, var); + pr_debug("%s: mode %u: %u [%u] %u x %u [%u] %u: diff = %d\n", + __func__, id, vmode->left_margin, vmode->xres, + vmode->right_margin, vmode->upper_margin, + vmode->yres, vmode->lower_margin, diff); + if (diff < 0) + continue; + if (diff < best_diff) { + best_id = id; + if (!diff) + break; + best_diff = diff; + } + } -found: - /* Cropped broadcast modes use the full line length */ - *ddr_line_length = ps3fb_modedb[i < 10 ? i + 13 : i].xres * BPP; + if (!best_id) { + pr_debug("%s: no suitable mode found\n", __func__); + return 0; + } - if (ps3_compare_firmware_version(1, 9, 0) >= 0) { - *xdr_line_length = GPU_ALIGN_UP(max(var->xres, - var->xres_virtual) * BPP); - if (*xdr_line_length > GPU_MAX_LINE_LENGTH) - *xdr_line_length = GPU_MAX_LINE_LENGTH; - } else - *xdr_line_length = *ddr_line_length; + id = best_id; + vmode = ps3fb_native_vmode(id); - /* Full broadcast modes have the full mode bit set */ - mode = i > 12 ? (i - 12) | PS3FB_FULL_MODE_BIT : i + 1; + *ddr_line_length = vmode->xres * BPP; - pr_debug("ps3fb_find_mode: mode %u\n", mode); + /* minimum resolution */ + if (!var->xres) + var->xres = 1; + if (!var->yres) + var->yres = 1; - return mode; -} + /* minimum virtual resolution */ + if (var->xres_virtual < var->xres) + var->xres_virtual = var->xres; + if (var->yres_virtual < var->yres) + var->yres_virtual = var->yres; -static const struct fb_videomode *ps3fb_default_mode(int id) -{ - u32 mode = id & PS3AV_MODE_MASK; - u32 flags; + /* minimum margins */ + if (var->left_margin < vmode->left_margin) + var->left_margin = vmode->left_margin; + if (var->right_margin < vmode->right_margin) + var->right_margin = vmode->right_margin; + if (var->upper_margin < vmode->upper_margin) + var->upper_margin = vmode->upper_margin; + if (var->lower_margin < vmode->lower_margin) + var->lower_margin = vmode->lower_margin; + + /* extra margins */ + gap = ((long)vmode->left_margin + (long)vmode->xres + + (long)vmode->right_margin) - + ((long)var->left_margin + (long)var->xres + + (long)var->right_margin); + if (gap > 0) { + var->left_margin += gap/2; + var->right_margin += (gap+1)/2; + pr_debug("%s: rounded up H to %u [%u] %u\n", __func__, + var->left_margin, var->xres, var->right_margin); + } - if (mode < 1 || mode > 13) - return NULL; + gap = ((long)vmode->upper_margin + (long)vmode->yres + + (long)vmode->lower_margin) - + ((long)var->upper_margin + (long)var->yres + + (long)var->lower_margin); + if (gap > 0) { + var->upper_margin += gap/2; + var->lower_margin += (gap+1)/2; + pr_debug("%s: rounded up V to %u [%u] %u\n", __func__, + var->upper_margin, var->yres, var->lower_margin); + } + + /* fixed fields */ + var->pixclock = vmode->pixclock; + var->hsync_len = vmode->hsync_len; + var->vsync_len = vmode->vsync_len; + var->sync = vmode->sync; - flags = id & ~PS3AV_MODE_MASK; + if (ps3_compare_firmware_version(1, 9, 0) >= 0) { + *xdr_line_length = GPU_ALIGN_UP(var->xres_virtual * BPP); + if (*xdr_line_length > GPU_MAX_LINE_LENGTH) + *xdr_line_length = GPU_MAX_LINE_LENGTH; + } else + *xdr_line_length = *ddr_line_length; - if (mode <= 10 && flags & PS3FB_FULL_MODE_BIT) { - /* Full broadcast mode */ - return &ps3fb_modedb[mode + 12]; + if (vmode->sync & FB_SYNC_BROADCAST) { + /* Full broadcast modes have the full mode bit set */ + if (vmode->xres == var->xres && vmode->yres == var->yres) + id |= PS3AV_MODE_FULL; } - return &ps3fb_modedb[mode - 1]; + pr_debug("%s: mode %u\n", __func__, id); + return id; } static void ps3fb_sync_image(struct device *dev, u64 frame_offset, @@ -439,8 +491,7 @@ static void ps3fb_sync_image(struct device *dev, u64 frame_offset, static int ps3fb_sync(struct fb_info *info, u32 frame) { struct ps3fb_par *par = info->par; - int i, error = 0; - u32 ddr_line_length, xdr_line_length; + int error = 0; u64 ddr_base, xdr_base; if (frame > par->num_frames - 1) { @@ -450,16 +501,13 @@ static int ps3fb_sync(struct fb_info *info, u32 frame) goto out; } - i = par->res_index; - xdr_line_length = info->fix.line_length; - ddr_line_length = ps3fb_res[i].xres * BPP; - xdr_base = frame * info->var.yres_virtual * xdr_line_length; - ddr_base = frame * ps3fb_res[i].yres * ddr_line_length; + xdr_base = frame * par->xdr_frame_size; + ddr_base = frame * par->ddr_frame_size; ps3fb_sync_image(info->device, ddr_base + par->full_offset, ddr_base + par->fb_offset, xdr_base + par->pan_offset, - par->width, par->height, ddr_line_length, - xdr_line_length); + par->width, par->height, par->ddr_line_length, + info->fix.line_length); out: return error; @@ -498,22 +546,11 @@ static int ps3fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) u32 xdr_line_length, ddr_line_length; int mode; - dev_dbg(info->device, "var->xres:%u info->var.xres:%u\n", var->xres, - info->var.xres); - dev_dbg(info->device, "var->yres:%u info->var.yres:%u\n", var->yres, - info->var.yres); - - /* FIXME For now we do exact matches only */ mode = ps3fb_find_mode(var, &ddr_line_length, &xdr_line_length); if (!mode) return -EINVAL; /* Virtual screen */ - if (var->xres_virtual < var->xres) - var->xres_virtual = var->xres; - if (var->yres_virtual < var->yres) - var->yres_virtual = var->yres; - if (var->xres_virtual > xdr_line_length / BPP) { dev_dbg(info->device, "Horizontal virtual screen size too large\n"); @@ -559,7 +596,7 @@ static int ps3fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) } /* Memory limit */ - if (var->yres_virtual * xdr_line_length > ps3fb.xdr_size) { + if (var->yres_virtual * xdr_line_length > info->fix.smem_len) { dev_dbg(info->device, "Not enough memory\n"); return -ENOMEM; } @@ -578,39 +615,38 @@ static int ps3fb_set_par(struct fb_info *info) { struct ps3fb_par *par = info->par; unsigned int mode, ddr_line_length, xdr_line_length, lines, maxlines; - int i; - unsigned long offset; + unsigned int ddr_xoff, ddr_yoff, offset; + const struct fb_videomode *vmode; u64 dst; - dev_dbg(info->device, "xres:%d xv:%d yres:%d yv:%d clock:%d\n", - info->var.xres, info->var.xres_virtual, - info->var.yres, info->var.yres_virtual, info->var.pixclock); - mode = ps3fb_find_mode(&info->var, &ddr_line_length, &xdr_line_length); if (!mode) return -EINVAL; - i = ps3fb_get_res_table(info->var.xres, info->var.yres, mode); - par->res_index = i; + vmode = ps3fb_native_vmode(mode & PS3AV_MODE_MASK); - info->fix.smem_start = virt_to_abs(ps3fb.xdr_ea); - info->fix.smem_len = ps3fb.xdr_size; info->fix.xpanstep = info->var.xres_virtual > info->var.xres ? 1 : 0; info->fix.ypanstep = info->var.yres_virtual > info->var.yres ? 1 : 0; info->fix.line_length = xdr_line_length; - info->screen_base = (char __iomem *)ps3fb.xdr_ea; + par->ddr_line_length = ddr_line_length; + par->ddr_frame_size = vmode->yres * ddr_line_length; + par->xdr_frame_size = info->var.yres_virtual * xdr_line_length; - par->num_frames = ps3fb.xdr_size / - max(ps3fb_res[i].yres * ddr_line_length, - info->var.yres_virtual * xdr_line_length); + par->num_frames = info->fix.smem_len / + max(par->ddr_frame_size, par->xdr_frame_size); /* Keep the special bits we cannot set using fb_var_screeninfo */ par->new_mode_id = (par->new_mode_id & ~PS3AV_MODE_MASK) | mode; par->width = info->var.xres; par->height = info->var.yres; - offset = VP_OFF(i); + + /* Start of the virtual frame buffer (relative to fullscreen) */ + ddr_xoff = info->var.left_margin - vmode->left_margin; + ddr_yoff = info->var.upper_margin - vmode->upper_margin; + offset = ddr_yoff * ddr_line_length + ddr_xoff * BPP; + par->fb_offset = GPU_ALIGN_UP(offset); par->full_offset = par->fb_offset - offset; par->pan_offset = info->var.yoffset * xdr_line_length + @@ -625,16 +661,16 @@ static int ps3fb_set_par(struct fb_info *info) } /* Clear XDR frame buffer memory */ - memset(ps3fb.xdr_ea, 0, ps3fb.xdr_size); + memset((void __force *)info->screen_base, 0, info->fix.smem_len); /* Clear DDR frame buffer memory */ - lines = ps3fb_res[i].yres * par->num_frames; + lines = vmode->yres * par->num_frames; if (par->full_offset) lines++; - maxlines = ps3fb.xdr_size / ddr_line_length; + maxlines = info->fix.smem_len / ddr_line_length; for (dst = 0; lines; dst += maxlines * ddr_line_length) { unsigned int l = min(lines, maxlines); - ps3fb_sync_image(info->device, 0, dst, 0, ps3fb_res[i].xres, l, + ps3fb_sync_image(info->device, 0, dst, 0, vmode->xres, l, ddr_line_length, ddr_line_length); lines -= l; } @@ -797,7 +833,7 @@ static int ps3fb_ioctl(struct fb_info *info, unsigned int cmd, case PS3FB_IOCTL_SETMODE: { struct ps3fb_par *par = info->par; - const struct fb_videomode *mode; + const struct fb_videomode *vmode; struct fb_var_screeninfo var; if (copy_from_user(&val, argp, sizeof(val))) @@ -810,10 +846,10 @@ static int ps3fb_ioctl(struct fb_info *info, unsigned int cmd, } dev_dbg(info->device, "PS3FB_IOCTL_SETMODE:%x\n", val); retval = -EINVAL; - mode = ps3fb_default_mode(val); - if (mode) { + vmode = ps3fb_vmode(val); + if (vmode) { var = info->var; - fb_videomode_to_var(&var, mode); + fb_videomode_to_var(&var, vmode); acquire_console_sem(); info->flags |= FBINFO_MISC_USEREVENT; /* Force, in case only special bits changed */ @@ -975,10 +1011,9 @@ static int ps3fb_xdr_settings(u64 xdr_lpar, struct device *dev) __func__, status); return -ENXIO; } - dev_dbg(dev, - "video:%p xdr_ea:%p ioif:%lx lpar:%lx phys:%lx size:%lx\n", - ps3fb_videomemory.address, ps3fb.xdr_ea, GPU_IOIF, xdr_lpar, - virt_to_abs(ps3fb.xdr_ea), ps3fb_videomemory.size); + dev_dbg(dev, "video:%p ioif:%lx lpar:%lx size:%lx\n", + ps3fb_videomemory.address, GPU_IOIF, xdr_lpar, + ps3fb_videomemory.size); status = lv1_gpu_context_attribute(ps3fb.context_handle, L1GPU_CONTEXT_ATTRIBUTE_FB_SETUP, @@ -1055,14 +1090,14 @@ static int __devinit ps3fb_probe(struct ps3_system_bus_device *dev) struct fb_info *info; struct ps3fb_par *par; int retval = -ENOMEM; - u32 xres, yres; u64 ddr_lpar = 0; u64 lpar_dma_control = 0; u64 lpar_driver_info = 0; u64 lpar_reports = 0; u64 lpar_reports_size = 0; u64 xdr_lpar; - int status, res_index; + void *fb_start; + int status; struct task_struct *task; unsigned long max_ps3fb_size; @@ -1080,14 +1115,7 @@ static int __devinit ps3fb_probe(struct ps3_system_bus_device *dev) if (!ps3fb_mode) ps3fb_mode = ps3av_get_mode(); - dev_dbg(&dev->core, "ps3av_mode:%d\n", ps3fb_mode); - - if (ps3fb_mode > 0 && - !ps3av_video_mode2res(ps3fb_mode, &xres, &yres)) { - res_index = ps3fb_get_res_table(xres, yres, ps3fb_mode); - dev_dbg(&dev->core, "res_index:%d\n", res_index); - } else - res_index = GPU_RES_INDEX; + dev_dbg(&dev->core, "ps3fb_mode: %d\n", ps3fb_mode); atomic_set(&ps3fb.f_count, -1); /* fbcon opens ps3fb */ atomic_set(&ps3fb.ext_flip, 0); /* for flip with vsync */ @@ -1124,7 +1152,7 @@ static int __devinit ps3fb_probe(struct ps3_system_bus_device *dev) } /* vsync interrupt */ - ps3fb.dinfo = ioremap(lpar_driver_info, 128 * 1024); + ps3fb.dinfo = (void __force *)ioremap(lpar_driver_info, 128 * 1024); if (!ps3fb.dinfo) { dev_err(&dev->core, "%s: ioremap failed\n", __func__); goto err_gpu_context_free; @@ -1134,22 +1162,10 @@ static int __devinit ps3fb_probe(struct ps3_system_bus_device *dev) if (retval) goto err_iounmap_dinfo; - /* XDR frame buffer */ - ps3fb.xdr_ea = ps3fb_videomemory.address; - xdr_lpar = ps3_mm_phys_to_lpar(__pa(ps3fb.xdr_ea)); - /* Clear memory to prevent kernel info leakage into userspace */ - memset(ps3fb.xdr_ea, 0, ps3fb_videomemory.size); - - /* - * The GPU command buffer is at the start of video memory - * As we don't use the full command buffer, we can put the actual - * frame buffer at offset GPU_FB_START and save some precious XDR - * memory - */ - ps3fb.xdr_ea += GPU_FB_START; - ps3fb.xdr_size = ps3fb_videomemory.size - GPU_FB_START; + memset(ps3fb_videomemory.address, 0, ps3fb_videomemory.size); + xdr_lpar = ps3_mm_phys_to_lpar(__pa(ps3fb_videomemory.address)); retval = ps3fb_xdr_settings(xdr_lpar, &dev->core); if (retval) goto err_free_irq; @@ -1161,15 +1177,22 @@ static int __devinit ps3fb_probe(struct ps3_system_bus_device *dev) par = info->par; par->mode_id = ~ps3fb_mode; /* != ps3fb_mode, to trigger change */ par->new_mode_id = ps3fb_mode; - par->res_index = res_index; par->num_frames = 1; - info->screen_base = (char __iomem *)ps3fb.xdr_ea; info->fbops = &ps3fb_ops; - info->fix = ps3fb_fix; - info->fix.smem_start = virt_to_abs(ps3fb.xdr_ea); - info->fix.smem_len = ps3fb.xdr_size; + + /* + * The GPU command buffer is at the start of video memory + * As we don't use the full command buffer, we can put the actual + * frame buffer at offset GPU_FB_START and save some precious XDR + * memory + */ + fb_start = ps3fb_videomemory.address + GPU_FB_START; + info->screen_base = (char __force __iomem *)fb_start; + info->fix.smem_start = virt_to_abs(fb_start); + info->fix.smem_len = ps3fb_videomemory.size - GPU_FB_START; + info->pseudo_palette = par->pseudo_palette; info->flags = FBINFO_DEFAULT | FBINFO_READS_FAST | FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN; @@ -1180,7 +1203,7 @@ static int __devinit ps3fb_probe(struct ps3_system_bus_device *dev) if (!fb_find_mode(&info->var, info, mode_option, ps3fb_modedb, ARRAY_SIZE(ps3fb_modedb), - ps3fb_default_mode(par->new_mode_id), 32)) { + ps3fb_vmode(par->new_mode_id), 32)) { retval = -EINVAL; goto err_fb_dealloc; } @@ -1194,9 +1217,9 @@ static int __devinit ps3fb_probe(struct ps3_system_bus_device *dev) dev->core.driver_data = info; - dev_info(info->device, "%s %s, using %lu KiB of video memory\n", + dev_info(info->device, "%s %s, using %u KiB of video memory\n", dev_driver_string(info->dev), info->dev->bus_id, - ps3fb.xdr_size >> 10); + info->fix.smem_len >> 10); task = kthread_run(ps3fbd, info, DEVICE_NAME); if (IS_ERR(task)) { @@ -1219,7 +1242,7 @@ err_free_irq: free_irq(ps3fb.irq_no, &dev->core); ps3_irq_plug_destroy(ps3fb.irq_no); err_iounmap_dinfo: - iounmap((u8 __iomem *)ps3fb.dinfo); + iounmap((u8 __force __iomem *)ps3fb.dinfo); err_gpu_context_free: lv1_gpu_context_free(ps3fb.context_handle); err_gpu_memory_free: @@ -1254,7 +1277,7 @@ static int ps3fb_shutdown(struct ps3_system_bus_device *dev) framebuffer_release(info); info = dev->core.driver_data = NULL; } - iounmap((u8 __iomem *)ps3fb.dinfo); + iounmap((u8 __force __iomem *)ps3fb.dinfo); status = lv1_gpu_context_free(ps3fb.context_handle); if (status) diff --git a/drivers/video/s3c2410fb.c b/drivers/video/s3c2410fb.c index b3c31d9dc59..71fa6edb5c4 100644 --- a/drivers/video/s3c2410fb.c +++ b/drivers/video/s3c2410fb.c @@ -110,6 +110,11 @@ static int debug = 0; /* useful functions */ +static int is_s3c2412(struct s3c2410fb_info *fbi) +{ + return (fbi->drv_type == DRV_S3C2412); +} + /* s3c2410fb_set_lcdaddr * * initialise lcd controller address pointers @@ -501,7 +506,7 @@ static void schedule_palette_update(struct s3c2410fb_info *fbi, { unsigned long flags; unsigned long irqen; - void __iomem *regs = fbi->io; + void __iomem *irq_base = fbi->irq_base; local_irq_save(flags); @@ -511,9 +516,9 @@ static void schedule_palette_update(struct s3c2410fb_info *fbi, fbi->palette_ready = 1; /* enable IRQ */ - irqen = readl(regs + S3C2410_LCDINTMSK); + irqen = readl(irq_base + S3C24XX_LCDINTMSK); irqen &= ~S3C2410_LCDINT_FRSYNC; - writel(irqen, regs + S3C2410_LCDINTMSK); + writel(irqen, irq_base + S3C24XX_LCDINTMSK); } local_irq_restore(flags); @@ -594,15 +599,17 @@ static int s3c2410fb_setcolreg(unsigned regno, static int s3c2410fb_blank(int blank_mode, struct fb_info *info) { struct s3c2410fb_info *fbi = info->par; - void __iomem *regs = fbi->io; + void __iomem *tpal_reg = fbi->io; dprintk("blank(mode=%d, info=%p)\n", blank_mode, info); + tpal_reg += is_s3c2412(fbi) ? S3C2412_TPAL : S3C2410_TPAL; + if (blank_mode == FB_BLANK_UNBLANK) - writel(0x0, regs + S3C2410_TPAL); + writel(0x0, tpal_reg); else { dprintk("setting TPAL to output 0x000000\n"); - writel(S3C2410_TPAL_EN, regs + S3C2410_TPAL); + writel(S3C2410_TPAL_EN, tpal_reg); } return 0; @@ -663,7 +670,7 @@ static int __init s3c2410fb_map_video_memory(struct fb_info *info) dma_addr_t map_dma; unsigned map_size = PAGE_ALIGN(info->fix.smem_len); - dprintk("map_video_memory(fbi=%p)\n", fbi); + dprintk("map_video_memory(fbi=%p) map_size %u\n", fbi, map_size); info->screen_base = dma_alloc_writecombine(fbi->dev, map_size, &map_dma, GFP_KERNEL); @@ -672,7 +679,7 @@ static int __init s3c2410fb_map_video_memory(struct fb_info *info) /* prevent initial garbage on screen */ dprintk("map_video_memory: clear %p:%08x\n", info->screen_base, map_size); - memset(info->screen_base, 0xf0, map_size); + memset(info->screen_base, 0x00, map_size); info->fix.smem_start = map_dma; @@ -709,6 +716,16 @@ static int s3c2410fb_init_registers(struct fb_info *info) struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data; unsigned long flags; void __iomem *regs = fbi->io; + void __iomem *tpal; + void __iomem *lpcsel; + + if (is_s3c2412(fbi)) { + tpal = regs + S3C2412_TPAL; + lpcsel = regs + S3C2412_TCONSEL; + } else { + tpal = regs + S3C2410_TPAL; + lpcsel = regs + S3C2410_LPCSEL; + } /* Initialise LCD with values from haret */ @@ -724,12 +741,12 @@ static int s3c2410fb_init_registers(struct fb_info *info) local_irq_restore(flags); dprintk("LPCSEL = 0x%08lx\n", mach_info->lpcsel); - writel(mach_info->lpcsel, regs + S3C2410_LPCSEL); + writel(mach_info->lpcsel, lpcsel); - dprintk("replacing TPAL %08x\n", readl(regs + S3C2410_TPAL)); + dprintk("replacing TPAL %08x\n", readl(tpal)); /* ensure temporary palette disabled */ - writel(0x00, regs + S3C2410_TPAL); + writel(0x00, tpal); return 0; } @@ -763,15 +780,15 @@ static void s3c2410fb_write_palette(struct s3c2410fb_info *fbi) static irqreturn_t s3c2410fb_irq(int irq, void *dev_id) { struct s3c2410fb_info *fbi = dev_id; - void __iomem *regs = fbi->io; - unsigned long lcdirq = readl(regs + S3C2410_LCDINTPND); + void __iomem *irq_base = fbi->irq_base; + unsigned long lcdirq = readl(irq_base + S3C24XX_LCDINTPND); if (lcdirq & S3C2410_LCDINT_FRSYNC) { if (fbi->palette_ready) s3c2410fb_write_palette(fbi); - writel(S3C2410_LCDINT_FRSYNC, regs + S3C2410_LCDINTPND); - writel(S3C2410_LCDINT_FRSYNC, regs + S3C2410_LCDSRCPND); + writel(S3C2410_LCDINT_FRSYNC, irq_base + S3C24XX_LCDINTPND); + writel(S3C2410_LCDINT_FRSYNC, irq_base + S3C24XX_LCDSRCPND); } return IRQ_HANDLED; @@ -779,7 +796,8 @@ static irqreturn_t s3c2410fb_irq(int irq, void *dev_id) static char driver_name[] = "s3c2410fb"; -static int __init s3c2410fb_probe(struct platform_device *pdev) +static int __init s3c24xxfb_probe(struct platform_device *pdev, + enum s3c_drv_type drv_type) { struct s3c2410fb_info *info; struct s3c2410fb_display *display; @@ -799,6 +817,12 @@ static int __init s3c2410fb_probe(struct platform_device *pdev) return -EINVAL; } + if (mach_info->default_display >= mach_info->num_displays) { + dev_err(&pdev->dev, "default is %d but only %d displays\n", + mach_info->default_display, mach_info->num_displays); + return -EINVAL; + } + display = mach_info->displays + mach_info->default_display; irq = platform_get_irq(pdev, 0); @@ -815,6 +839,7 @@ static int __init s3c2410fb_probe(struct platform_device *pdev) info = fbinfo->par; info->dev = &pdev->dev; + info->drv_type = drv_type; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) { @@ -838,6 +863,8 @@ static int __init s3c2410fb_probe(struct platform_device *pdev) goto release_mem; } + info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE); + dprintk("devinit\n"); strcpy(fbinfo->fix.id, driver_name); @@ -946,6 +973,16 @@ dealloc_fb: return ret; } +static int __init s3c2410fb_probe(struct platform_device *pdev) +{ + return s3c24xxfb_probe(pdev, DRV_S3C2410); +} + +static int __init s3c2412fb_probe(struct platform_device *pdev) +{ + return s3c24xxfb_probe(pdev, DRV_S3C2412); +} + /* s3c2410fb_stop_lcd * * shutdown the lcd controller @@ -1047,14 +1084,31 @@ static struct platform_driver s3c2410fb_driver = { }, }; +static struct platform_driver s3c2412fb_driver = { + .probe = s3c2412fb_probe, + .remove = s3c2410fb_remove, + .suspend = s3c2410fb_suspend, + .resume = s3c2410fb_resume, + .driver = { + .name = "s3c2412-lcd", + .owner = THIS_MODULE, + }, +}; + int __init s3c2410fb_init(void) { - return platform_driver_register(&s3c2410fb_driver); + int ret = platform_driver_register(&s3c2410fb_driver); + + if (ret == 0) + ret = platform_driver_register(&s3c2412fb_driver);; + + return ret; } static void __exit s3c2410fb_cleanup(void) { platform_driver_unregister(&s3c2410fb_driver); + platform_driver_unregister(&s3c2412fb_driver); } module_init(s3c2410fb_init); diff --git a/drivers/video/s3c2410fb.h b/drivers/video/s3c2410fb.h index 6ce5dc26c5f..dbb73b95e2e 100644 --- a/drivers/video/s3c2410fb.h +++ b/drivers/video/s3c2410fb.h @@ -25,13 +25,20 @@ #ifndef __S3C2410FB_H #define __S3C2410FB_H +enum s3c_drv_type { + DRV_S3C2410, + DRV_S3C2412, +}; + struct s3c2410fb_info { struct device *dev; struct clk *clk; struct resource *mem; void __iomem *io; + void __iomem *irq_base; + enum s3c_drv_type drv_type; struct s3c2410fb_hw regs; unsigned int palette_ready; diff --git a/drivers/video/sis/sis_main.c b/drivers/video/sis/sis_main.c index 93ae747440c..73803624c13 100644 --- a/drivers/video/sis/sis_main.c +++ b/drivers/video/sis/sis_main.c @@ -427,7 +427,7 @@ sisfb_interpret_edid(struct sisfb_monitor *monitor, u8 *buffer) monitor->feature = buffer[0x18]; - if(!buffer[0x14] & 0x80) { + if(!(buffer[0x14] & 0x80)) { if(!(buffer[0x14] & 0x08)) { printk(KERN_INFO "sisfb: WARNING: Monitor does not support separate syncs\n"); @@ -4621,9 +4621,9 @@ sisfb_find_host_bridge(struct sis_video_info *ivideo, struct pci_dev *mypdev, while((pdev = pci_get_class(PCI_CLASS_BRIDGE_HOST, pdev))) { temp = pdev->vendor; - pci_dev_put(pdev); if(temp == pcivendor) { ret = 1; + pci_dev_put(pdev); break; } } diff --git a/drivers/video/sm501fb.c b/drivers/video/sm501fb.c index 58f200c69be..e83dfba7e63 100644 --- a/drivers/video/sm501fb.c +++ b/drivers/video/sm501fb.c @@ -641,6 +641,7 @@ static void sm501fb_panel_power(struct sm501fb_info *fbi, int to) { unsigned long control; void __iomem *ctrl_reg = fbi->regs + SM501_DC_PANEL_CONTROL; + struct sm501_platdata_fbsub *pd = fbi->pdata->fb_pnl; control = readl(ctrl_reg); @@ -657,26 +658,34 @@ static void sm501fb_panel_power(struct sm501fb_info *fbi, int to) sm501fb_sync_regs(fbi); mdelay(10); - control |= SM501_DC_PANEL_CONTROL_BIAS; /* VBIASEN */ - writel(control, ctrl_reg); - sm501fb_sync_regs(fbi); - mdelay(10); - - control |= SM501_DC_PANEL_CONTROL_FPEN; - writel(control, ctrl_reg); + if (pd->flags & SM501FB_FLAG_PANEL_USE_VBIASEN) { + control |= SM501_DC_PANEL_CONTROL_BIAS; /* VBIASEN */ + writel(control, ctrl_reg); + sm501fb_sync_regs(fbi); + mdelay(10); + } + if (pd->flags & SM501FB_FLAG_PANEL_USE_FPEN) { + control |= SM501_DC_PANEL_CONTROL_FPEN; + writel(control, ctrl_reg); + sm501fb_sync_regs(fbi); + mdelay(10); + } } else if (!to && (control & SM501_DC_PANEL_CONTROL_VDD) != 0) { /* disable panel power */ + if (pd->flags & SM501FB_FLAG_PANEL_USE_FPEN) { + control &= ~SM501_DC_PANEL_CONTROL_FPEN; + writel(control, ctrl_reg); + sm501fb_sync_regs(fbi); + mdelay(10); + } - control &= ~SM501_DC_PANEL_CONTROL_FPEN; - writel(control, ctrl_reg); - sm501fb_sync_regs(fbi); - mdelay(10); - - control &= ~SM501_DC_PANEL_CONTROL_BIAS; - writel(control, ctrl_reg); - sm501fb_sync_regs(fbi); - mdelay(10); + if (pd->flags & SM501FB_FLAG_PANEL_USE_VBIASEN) { + control &= ~SM501_DC_PANEL_CONTROL_BIAS; + writel(control, ctrl_reg); + sm501fb_sync_regs(fbi); + mdelay(10); + } control &= ~SM501_DC_PANEL_CONTROL_DATA; writel(control, ctrl_reg); @@ -1267,6 +1276,7 @@ static int sm501fb_start(struct sm501fb_info *info, { struct resource *res; struct device *dev; + int k; int ret; info->dev = dev = &pdev->dev; @@ -1328,6 +1338,13 @@ static int sm501fb_start(struct sm501fb_info *info, info->fbmem_len = (res->end - res->start)+1; + /* clear framebuffer memory - avoids garbage data on unused fb */ + memset(info->fbmem, 0, info->fbmem_len); + + /* clear palette ram - undefined at power on */ + for (k = 0; k < (256 * 3); k++) + writel(0, info->regs + SM501_DC_PANEL_PALETTE + (k * 4)); + /* enable display controller */ sm501_unit_power(dev->parent, SM501_GATE_DISPLAY, 1); @@ -1681,6 +1698,15 @@ static int sm501fb_suspend_fb(struct sm501fb_info *info, if (par->screen.size == 0) return 0; + /* blank the relevant interface to ensure unit power minimised */ + (par->ops.fb_blank)(FB_BLANK_POWERDOWN, fbi); + + /* tell console/fb driver we are suspending */ + + acquire_console_sem(); + fb_set_suspend(fbi, 1); + release_console_sem(); + /* backup copies in case chip is powered down over suspend */ par->store_fb = vmalloc(par->screen.size); @@ -1700,12 +1726,6 @@ static int sm501fb_suspend_fb(struct sm501fb_info *info, memcpy_fromio(par->store_fb, par->screen.k_addr, par->screen.size); memcpy_fromio(par->store_cursor, par->cursor.k_addr, par->cursor.size); - /* blank the relevant interface to ensure unit power minimised */ - (par->ops.fb_blank)(FB_BLANK_POWERDOWN, fbi); - - acquire_console_sem(); - fb_set_suspend(fbi, 1); - release_console_sem(); return 0; diff --git a/drivers/video/tdfxfb.c b/drivers/video/tdfxfb.c index 057bdd59380..71e179ea5f9 100644 --- a/drivers/video/tdfxfb.c +++ b/drivers/video/tdfxfb.c @@ -1342,7 +1342,7 @@ out_err: } #ifndef MODULE -static void tdfxfb_setup(char *options) +static void __init tdfxfb_setup(char *options) { char *this_opt; diff --git a/drivers/video/uvesafb.c b/drivers/video/uvesafb.c index a14ef894d57..be27b9c1ed7 100644 --- a/drivers/video/uvesafb.c +++ b/drivers/video/uvesafb.c @@ -2003,12 +2003,12 @@ static void __devexit uvesafb_exit(void) module_exit(uvesafb_exit); -static inline int param_get_scroll(char *buffer, struct kernel_param *kp) +static int param_get_scroll(char *buffer, struct kernel_param *kp) { return 0; } -static inline int param_set_scroll(const char *val, struct kernel_param *kp) +static int param_set_scroll(const char *val, struct kernel_param *kp) { ypan = 0; @@ -2022,11 +2022,11 @@ static inline int param_set_scroll(const char *val, struct kernel_param *kp) return 0; } -#define param_check_scroll(name, p) __param_check(name, p, void); +#define param_check_scroll(name, p) __param_check(name, p, void) module_param_named(scroll, ypan, scroll, 0); MODULE_PARM_DESC(scroll, - "Scrolling mode, set to 'redraw', ''ypan' or 'ywrap'"); + "Scrolling mode, set to 'redraw', 'ypan', or 'ywrap'"); module_param_named(vgapal, pmi_setpal, invbool, 0); MODULE_PARM_DESC(vgapal, "Set palette using VGA registers"); module_param_named(pmipal, pmi_setpal, bool, 0); diff --git a/drivers/video/vermilion/vermilion.c b/drivers/video/vermilion/vermilion.c index 1c656667b93..2aa71eb67c2 100644 --- a/drivers/video/vermilion/vermilion.c +++ b/drivers/video/vermilion/vermilion.c @@ -651,7 +651,7 @@ static int vmlfb_check_var_locked(struct fb_var_screeninfo *var, return -EINVAL; } - pitch = __ALIGN_MASK((var->xres * var->bits_per_pixel) >> 3, 0x3F); + pitch = ALIGN((var->xres * var->bits_per_pixel) >> 3, 0x40); mem = pitch * var->yres_virtual; if (mem > vinfo->vram_contig_size) { return -ENOMEM; @@ -785,8 +785,7 @@ static int vmlfb_set_par_locked(struct vml_info *vinfo) int clock; vinfo->bytes_per_pixel = var->bits_per_pixel >> 3; - vinfo->stride = - __ALIGN_MASK(var->xres_virtual * vinfo->bytes_per_pixel, 0x3F); + vinfo->stride = ALIGN(var->xres_virtual * vinfo->bytes_per_pixel, 0x40); info->fix.line_length = vinfo->stride; if (!subsys) diff --git a/drivers/w1/masters/Kconfig b/drivers/w1/masters/Kconfig index 8236d447adf..c4493091c65 100644 --- a/drivers/w1/masters/Kconfig +++ b/drivers/w1/masters/Kconfig @@ -42,5 +42,15 @@ config W1_MASTER_DS1WM in HP iPAQ devices like h5xxx, h2200, and ASIC3-based like hx4700. +config W1_MASTER_GPIO + tristate "GPIO 1-wire busmaster" + depends on GENERIC_GPIO + help + Say Y here if you want to communicate with your 1-wire devices using + GPIO pins. This driver uses the GPIO API to control the wire. + + This support is also available as a module. If so, the module + will be called w1-gpio.ko. + endmenu diff --git a/drivers/w1/masters/Makefile b/drivers/w1/masters/Makefile index 11551b32818..1420b5bbdda 100644 --- a/drivers/w1/masters/Makefile +++ b/drivers/w1/masters/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_W1_MASTER_MATROX) += matrox_w1.o obj-$(CONFIG_W1_MASTER_DS2490) += ds2490.o obj-$(CONFIG_W1_MASTER_DS2482) += ds2482.o obj-$(CONFIG_W1_MASTER_DS1WM) += ds1wm.o +obj-$(CONFIG_W1_MASTER_GPIO) += w1-gpio.o diff --git a/drivers/w1/masters/w1-gpio.c b/drivers/w1/masters/w1-gpio.c new file mode 100644 index 00000000000..9e1138a75e8 --- /dev/null +++ b/drivers/w1/masters/w1-gpio.c @@ -0,0 +1,124 @@ +/* + * w1-gpio - GPIO w1 bus master driver + * + * Copyright (C) 2007 Ville Syrjala <syrjala@sci.fi> + * + * 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 <linux/init.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/w1-gpio.h> + +#include "../w1.h" +#include "../w1_int.h" + +#include <asm/gpio.h> + +static void w1_gpio_write_bit_dir(void *data, u8 bit) +{ + struct w1_gpio_platform_data *pdata = data; + + if (bit) + gpio_direction_input(pdata->pin); + else + gpio_direction_output(pdata->pin, 0); +} + +static void w1_gpio_write_bit_val(void *data, u8 bit) +{ + struct w1_gpio_platform_data *pdata = data; + + gpio_set_value(pdata->pin, bit); +} + +static u8 w1_gpio_read_bit(void *data) +{ + struct w1_gpio_platform_data *pdata = data; + + return gpio_get_value(pdata->pin); +} + +static int __init w1_gpio_probe(struct platform_device *pdev) +{ + struct w1_bus_master *master; + struct w1_gpio_platform_data *pdata = pdev->dev.platform_data; + int err; + + if (!pdata) + return -ENXIO; + + master = kzalloc(sizeof(struct w1_bus_master), GFP_KERNEL); + if (!master) + return -ENOMEM; + + err = gpio_request(pdata->pin, "w1"); + if (err) + goto free_master; + + master->data = pdata; + master->read_bit = w1_gpio_read_bit; + + if (pdata->is_open_drain) { + gpio_direction_output(pdata->pin, 1); + master->write_bit = w1_gpio_write_bit_val; + } else { + gpio_direction_input(pdata->pin); + master->write_bit = w1_gpio_write_bit_dir; + } + + err = w1_add_master_device(master); + if (err) + goto free_gpio; + + platform_set_drvdata(pdev, master); + + return 0; + + free_gpio: + gpio_free(pdata->pin); + free_master: + kfree(master); + + return err; +} + +static int __exit w1_gpio_remove(struct platform_device *pdev) +{ + struct w1_bus_master *master = platform_get_drvdata(pdev); + struct w1_gpio_platform_data *pdata = pdev->dev.platform_data; + + w1_remove_master_device(master); + gpio_free(pdata->pin); + kfree(master); + + return 0; +} + +static struct platform_driver w1_gpio_driver = { + .driver = { + .name = "w1-gpio", + .owner = THIS_MODULE, + }, + .remove = __exit_p(w1_gpio_remove), +}; + +static int __init w1_gpio_init(void) +{ + return platform_driver_probe(&w1_gpio_driver, w1_gpio_probe); +} + +static void __exit w1_gpio_exit(void) +{ + platform_driver_unregister(&w1_gpio_driver); +} + +module_init(w1_gpio_init); +module_exit(w1_gpio_exit); + +MODULE_DESCRIPTION("GPIO w1 bus master driver"); +MODULE_AUTHOR("Ville Syrjala <syrjala@sci.fi>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c index 112f4ec5903..fb28acaeed6 100644 --- a/drivers/w1/slaves/w1_therm.c +++ b/drivers/w1/slaves/w1_therm.c @@ -92,6 +92,7 @@ struct w1_therm_family_converter int (*convert)(u8 rom[9]); }; +/* The return value is millidegrees Centigrade. */ static inline int w1_DS18B20_convert_temp(u8 rom[9]); static inline int w1_DS18S20_convert_temp(u8 rom[9]); @@ -113,7 +114,7 @@ static struct w1_therm_family_converter w1_therm_families[] = { static inline int w1_DS18B20_convert_temp(u8 rom[9]) { s16 t = (rom[1] << 8) | rom[0]; - t /= 16; + t = t*1000/16; return t; } diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c index 33e50310e9e..7293c9b11f9 100644 --- a/drivers/w1/w1.c +++ b/drivers/w1/w1.c @@ -675,7 +675,6 @@ static void w1_slave_found(void *data, u64 rn) struct w1_slave *sl; struct list_head *ent; struct w1_reg_num *tmp; - int family_found = 0; struct w1_master *dev; u64 rn_le = cpu_to_le64(rn); @@ -698,9 +697,6 @@ static void w1_slave_found(void *data, u64 rn) sl->reg_num.crc == tmp->crc) { set_bit(W1_SLAVE_ACTIVE, (long *)&sl->flags); break; - } else if (sl->reg_num.family == tmp->family) { - family_found = 1; - break; } slave_count++; |