diff options
Diffstat (limited to 'drivers')
396 files changed, 18784 insertions, 2825 deletions
diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c index 864dd46c346..18645f4e83c 100644 --- a/drivers/acpi/apei/erst.c +++ b/drivers/acpi/apei/erst.c @@ -33,6 +33,7 @@ #include <linux/uaccess.h> #include <linux/cper.h> #include <linux/nmi.h> +#include <linux/hardirq.h> #include <acpi/apei.h> #include "apei-internal.h" diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 8fae6afd6a3..65e3e270837 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -651,6 +651,17 @@ config PATA_VIA If unsure, say N. +config PATA_PXA + tristate "PXA DMA-capable PATA support" + depends on ARCH_PXA + help + This option enables support for harddrive attached to PXA CPU's bus. + + NOTE: This driver utilizes PXA DMA controller, in case your hardware + is not capable of doing MWDMA, use pata_platform instead. + + If unsure, say N. + config PATA_WINBOND tristate "Winbond SL82C105 PATA support" depends on PCI diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile index 6540632bda0..158eaa961b1 100644 --- a/drivers/ata/Makefile +++ b/drivers/ata/Makefile @@ -91,6 +91,8 @@ obj-$(CONFIG_PATA_RZ1000) += pata_rz1000.o obj-$(CONFIG_PATA_SAMSUNG_CF) += pata_samsung_cf.o obj-$(CONFIG_PATA_WINBOND_VLB) += pata_winbond.o +obj-$(CONFIG_PATA_PXA) += pata_pxa.o + # Should be last but two libata driver obj-$(CONFIG_PATA_ACPI) += pata_acpi.o # Should be last but one libata driver diff --git a/drivers/ata/pata_mpc52xx.c b/drivers/ata/pata_mpc52xx.c index f087ab55b1d..8cc536e49a0 100644 --- a/drivers/ata/pata_mpc52xx.c +++ b/drivers/ata/pata_mpc52xx.c @@ -680,7 +680,7 @@ mpc52xx_ata_remove_one(struct device *dev) /* ======================================================================== */ static int __devinit -mpc52xx_ata_probe(struct of_device *op, const struct of_device_id *match) +mpc52xx_ata_probe(struct platform_device *op, const struct of_device_id *match) { unsigned int ipb_freq; struct resource res_mem; @@ -821,7 +821,7 @@ mpc52xx_ata_probe(struct of_device *op, const struct of_device_id *match) } static int -mpc52xx_ata_remove(struct of_device *op) +mpc52xx_ata_remove(struct platform_device *op) { struct mpc52xx_ata_priv *priv; int task_irq; @@ -848,7 +848,7 @@ mpc52xx_ata_remove(struct of_device *op) #ifdef CONFIG_PM static int -mpc52xx_ata_suspend(struct of_device *op, pm_message_t state) +mpc52xx_ata_suspend(struct platform_device *op, pm_message_t state) { struct ata_host *host = dev_get_drvdata(&op->dev); @@ -856,7 +856,7 @@ mpc52xx_ata_suspend(struct of_device *op, pm_message_t state) } static int -mpc52xx_ata_resume(struct of_device *op) +mpc52xx_ata_resume(struct platform_device *op) { struct ata_host *host = dev_get_drvdata(&op->dev); struct mpc52xx_ata_priv *priv = host->private_data; diff --git a/drivers/ata/pata_of_platform.c b/drivers/ata/pata_of_platform.c index 5a1b82c08be..480e043ce6b 100644 --- a/drivers/ata/pata_of_platform.c +++ b/drivers/ata/pata_of_platform.c @@ -14,7 +14,7 @@ #include <linux/of_platform.h> #include <linux/ata_platform.h> -static int __devinit pata_of_platform_probe(struct of_device *ofdev, +static int __devinit pata_of_platform_probe(struct platform_device *ofdev, const struct of_device_id *match) { int ret; @@ -78,7 +78,7 @@ static int __devinit pata_of_platform_probe(struct of_device *ofdev, reg_shift, pio_mask); } -static int __devexit pata_of_platform_remove(struct of_device *ofdev) +static int __devexit pata_of_platform_remove(struct platform_device *ofdev) { return __pata_platform_remove(&ofdev->dev); } diff --git a/drivers/ata/pata_pxa.c b/drivers/ata/pata_pxa.c new file mode 100644 index 00000000000..1898c6ed4b4 --- /dev/null +++ b/drivers/ata/pata_pxa.c @@ -0,0 +1,411 @@ +/* + * Generic PXA PATA driver + * + * Copyright (C) 2010 Marek Vasut <marek.vasut@gmail.com> + * + * 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. + * + * 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, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/blkdev.h> +#include <linux/ata.h> +#include <linux/libata.h> +#include <linux/platform_device.h> +#include <linux/gpio.h> +#include <linux/slab.h> +#include <linux/completion.h> + +#include <scsi/scsi_host.h> + +#include <mach/pxa2xx-regs.h> +#include <mach/pata_pxa.h> +#include <mach/dma.h> + +#define DRV_NAME "pata_pxa" +#define DRV_VERSION "0.1" + +struct pata_pxa_data { + uint32_t dma_channel; + struct pxa_dma_desc *dma_desc; + dma_addr_t dma_desc_addr; + uint32_t dma_desc_id; + + /* DMA IO physical address */ + uint32_t dma_io_addr; + /* PXA DREQ<0:2> pin selector */ + uint32_t dma_dreq; + /* DMA DCSR register value */ + uint32_t dma_dcsr; + + struct completion dma_done; +}; + +/* + * Setup the DMA descriptors. The size is transfer capped at 4k per descriptor, + * if the transfer is longer, it is split into multiple chained descriptors. + */ +static void pxa_load_dmac(struct scatterlist *sg, struct ata_queued_cmd *qc) +{ + struct pata_pxa_data *pd = qc->ap->private_data; + + uint32_t cpu_len, seg_len; + dma_addr_t cpu_addr; + + cpu_addr = sg_dma_address(sg); + cpu_len = sg_dma_len(sg); + + do { + seg_len = (cpu_len > 0x1000) ? 0x1000 : cpu_len; + + pd->dma_desc[pd->dma_desc_id].ddadr = pd->dma_desc_addr + + ((pd->dma_desc_id + 1) * sizeof(struct pxa_dma_desc)); + + pd->dma_desc[pd->dma_desc_id].dcmd = DCMD_BURST32 | + DCMD_WIDTH2 | (DCMD_LENGTH & seg_len); + + if (qc->tf.flags & ATA_TFLAG_WRITE) { + pd->dma_desc[pd->dma_desc_id].dsadr = cpu_addr; + pd->dma_desc[pd->dma_desc_id].dtadr = pd->dma_io_addr; + pd->dma_desc[pd->dma_desc_id].dcmd |= DCMD_INCSRCADDR | + DCMD_FLOWTRG; + } else { + pd->dma_desc[pd->dma_desc_id].dsadr = pd->dma_io_addr; + pd->dma_desc[pd->dma_desc_id].dtadr = cpu_addr; + pd->dma_desc[pd->dma_desc_id].dcmd |= DCMD_INCTRGADDR | + DCMD_FLOWSRC; + } + + cpu_len -= seg_len; + cpu_addr += seg_len; + pd->dma_desc_id++; + + } while (cpu_len); + + /* Should not happen */ + if (seg_len & 0x1f) + DALGN |= (1 << pd->dma_dreq); +} + +/* + * Prepare taskfile for submission. + */ +static void pxa_qc_prep(struct ata_queued_cmd *qc) +{ + struct pata_pxa_data *pd = qc->ap->private_data; + int si = 0; + struct scatterlist *sg; + + if (!(qc->flags & ATA_QCFLAG_DMAMAP)) + return; + + pd->dma_desc_id = 0; + + DCSR(pd->dma_channel) = 0; + DALGN &= ~(1 << pd->dma_dreq); + + for_each_sg(qc->sg, sg, qc->n_elem, si) + pxa_load_dmac(sg, qc); + + pd->dma_desc[pd->dma_desc_id - 1].ddadr = DDADR_STOP; + + /* Fire IRQ only at the end of last block */ + pd->dma_desc[pd->dma_desc_id - 1].dcmd |= DCMD_ENDIRQEN; + + DDADR(pd->dma_channel) = pd->dma_desc_addr; + DRCMR(pd->dma_dreq) = DRCMR_MAPVLD | pd->dma_channel; + +} + +/* + * Configure the DMA controller, load the DMA descriptors, but don't start the + * DMA controller yet. Only issue the ATA command. + */ +static void pxa_bmdma_setup(struct ata_queued_cmd *qc) +{ + qc->ap->ops->sff_exec_command(qc->ap, &qc->tf); +} + +/* + * Execute the DMA transfer. + */ +static void pxa_bmdma_start(struct ata_queued_cmd *qc) +{ + struct pata_pxa_data *pd = qc->ap->private_data; + init_completion(&pd->dma_done); + DCSR(pd->dma_channel) = DCSR_RUN; +} + +/* + * Wait until the DMA transfer completes, then stop the DMA controller. + */ +static void pxa_bmdma_stop(struct ata_queued_cmd *qc) +{ + struct pata_pxa_data *pd = qc->ap->private_data; + + if ((DCSR(pd->dma_channel) & DCSR_RUN) && + wait_for_completion_timeout(&pd->dma_done, HZ)) + dev_err(qc->ap->dev, "Timeout waiting for DMA completion!"); + + DCSR(pd->dma_channel) = 0; +} + +/* + * Read DMA status. The bmdma_stop() will take care of properly finishing the + * DMA transfer so we always have DMA-complete interrupt here. + */ +static unsigned char pxa_bmdma_status(struct ata_port *ap) +{ + struct pata_pxa_data *pd = ap->private_data; + unsigned char ret = ATA_DMA_INTR; + + if (pd->dma_dcsr & DCSR_BUSERR) + ret |= ATA_DMA_ERR; + + return ret; +} + +/* + * No IRQ register present so we do nothing. + */ +static void pxa_irq_clear(struct ata_port *ap) +{ +} + +/* + * Check for ATAPI DMA. ATAPI DMA is unsupported by this driver. It's still + * unclear why ATAPI has DMA issues. + */ +static int pxa_check_atapi_dma(struct ata_queued_cmd *qc) +{ + return -EOPNOTSUPP; +} + +static struct scsi_host_template pxa_ata_sht = { + ATA_BMDMA_SHT(DRV_NAME), +}; + +static struct ata_port_operations pxa_ata_port_ops = { + .inherits = &ata_bmdma_port_ops, + .cable_detect = ata_cable_40wire, + + .bmdma_setup = pxa_bmdma_setup, + .bmdma_start = pxa_bmdma_start, + .bmdma_stop = pxa_bmdma_stop, + .bmdma_status = pxa_bmdma_status, + + .check_atapi_dma = pxa_check_atapi_dma, + + .sff_irq_clear = pxa_irq_clear, + + .qc_prep = pxa_qc_prep, +}; + +/* + * DMA interrupt handler. + */ +static void pxa_ata_dma_irq(int dma, void *port) +{ + struct ata_port *ap = port; + struct pata_pxa_data *pd = ap->private_data; + + pd->dma_dcsr = DCSR(dma); + DCSR(dma) = pd->dma_dcsr; + + if (pd->dma_dcsr & DCSR_STOPSTATE) + complete(&pd->dma_done); +} + +static int __devinit pxa_ata_probe(struct platform_device *pdev) +{ + struct ata_host *host; + struct ata_port *ap; + struct pata_pxa_data *data; + struct resource *cmd_res; + struct resource *ctl_res; + struct resource *dma_res; + struct resource *irq_res; + struct pata_pxa_pdata *pdata = pdev->dev.platform_data; + int ret = 0; + + /* + * Resource validation, three resources are needed: + * - CMD port base address + * - CTL port base address + * - DMA port base address + * - IRQ pin + */ + if (pdev->num_resources != 4) { + dev_err(&pdev->dev, "invalid number of resources\n"); + return -EINVAL; + } + + /* + * CMD port base address + */ + cmd_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (unlikely(cmd_res == NULL)) + return -EINVAL; + + /* + * CTL port base address + */ + ctl_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (unlikely(ctl_res == NULL)) + return -EINVAL; + + /* + * DMA port base address + */ + dma_res = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (unlikely(dma_res == NULL)) + return -EINVAL; + + /* + * IRQ pin + */ + irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (unlikely(irq_res == NULL)) + return -EINVAL; + + /* + * Allocate the host + */ + host = ata_host_alloc(&pdev->dev, 1); + if (!host) + return -ENOMEM; + + ap = host->ports[0]; + ap->ops = &pxa_ata_port_ops; + ap->pio_mask = ATA_PIO4; + ap->mwdma_mask = ATA_MWDMA2; + ap->flags = ATA_FLAG_MMIO; + + ap->ioaddr.cmd_addr = devm_ioremap(&pdev->dev, cmd_res->start, + resource_size(cmd_res)); + ap->ioaddr.ctl_addr = devm_ioremap(&pdev->dev, ctl_res->start, + resource_size(ctl_res)); + ap->ioaddr.bmdma_addr = devm_ioremap(&pdev->dev, dma_res->start, + resource_size(dma_res)); + + /* + * Adjust register offsets + */ + ap->ioaddr.altstatus_addr = ap->ioaddr.ctl_addr; + ap->ioaddr.data_addr = ap->ioaddr.cmd_addr + + (ATA_REG_DATA << pdata->reg_shift); + ap->ioaddr.error_addr = ap->ioaddr.cmd_addr + + (ATA_REG_ERR << pdata->reg_shift); + ap->ioaddr.feature_addr = ap->ioaddr.cmd_addr + + (ATA_REG_FEATURE << pdata->reg_shift); + ap->ioaddr.nsect_addr = ap->ioaddr.cmd_addr + + (ATA_REG_NSECT << pdata->reg_shift); + ap->ioaddr.lbal_addr = ap->ioaddr.cmd_addr + + (ATA_REG_LBAL << pdata->reg_shift); + ap->ioaddr.lbam_addr = ap->ioaddr.cmd_addr + + (ATA_REG_LBAM << pdata->reg_shift); + ap->ioaddr.lbah_addr = ap->ioaddr.cmd_addr + + (ATA_REG_LBAH << pdata->reg_shift); + ap->ioaddr.device_addr = ap->ioaddr.cmd_addr + + (ATA_REG_DEVICE << pdata->reg_shift); + ap->ioaddr.status_addr = ap->ioaddr.cmd_addr + + (ATA_REG_STATUS << pdata->reg_shift); + ap->ioaddr.command_addr = ap->ioaddr.cmd_addr + + (ATA_REG_CMD << pdata->reg_shift); + + /* + * Allocate and load driver's internal data structure + */ + data = devm_kzalloc(&pdev->dev, sizeof(struct pata_pxa_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + ap->private_data = data; + data->dma_dreq = pdata->dma_dreq; + data->dma_io_addr = dma_res->start; + + /* + * Allocate space for the DMA descriptors + */ + data->dma_desc = dmam_alloc_coherent(&pdev->dev, PAGE_SIZE, + &data->dma_desc_addr, GFP_KERNEL); + if (!data->dma_desc) + return -EINVAL; + + /* + * Request the DMA channel + */ + data->dma_channel = pxa_request_dma(DRV_NAME, DMA_PRIO_LOW, + pxa_ata_dma_irq, ap); + if (data->dma_channel < 0) + return -EBUSY; + + /* + * Stop and clear the DMA channel + */ + DCSR(data->dma_channel) = 0; + + /* + * Activate the ATA host + */ + ret = ata_host_activate(host, irq_res->start, ata_sff_interrupt, + pdata->irq_flags, &pxa_ata_sht); + if (ret) + pxa_free_dma(data->dma_channel); + + return ret; +} + +static int __devexit pxa_ata_remove(struct platform_device *pdev) +{ + struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct pata_pxa_data *data = host->ports[0]->private_data; + + pxa_free_dma(data->dma_channel); + + ata_host_detach(host); + + return 0; +} + +static struct platform_driver pxa_ata_driver = { + .probe = pxa_ata_probe, + .remove = __devexit_p(pxa_ata_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init pxa_ata_init(void) +{ + return platform_driver_register(&pxa_ata_driver); +} + +static void __exit pxa_ata_exit(void) +{ + platform_driver_unregister(&pxa_ata_driver); +} + +module_init(pxa_ata_init); +module_exit(pxa_ata_exit); + +MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); +MODULE_DESCRIPTION("DMA-capable driver for PATA on PXA CPU"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c index 18c986dbb7f..7325f77480d 100644 --- a/drivers/ata/sata_fsl.c +++ b/drivers/ata/sata_fsl.c @@ -1296,7 +1296,7 @@ static const struct ata_port_info sata_fsl_port_info[] = { }, }; -static int sata_fsl_probe(struct of_device *ofdev, +static int sata_fsl_probe(struct platform_device *ofdev, const struct of_device_id *match) { int retval = -ENXIO; @@ -1370,7 +1370,7 @@ error_exit_with_cleanup: return retval; } -static int sata_fsl_remove(struct of_device *ofdev) +static int sata_fsl_remove(struct platform_device *ofdev) { struct ata_host *host = dev_get_drvdata(&ofdev->dev); struct sata_fsl_host_priv *host_priv = host->private_data; @@ -1387,13 +1387,13 @@ static int sata_fsl_remove(struct of_device *ofdev) } #ifdef CONFIG_PM -static int sata_fsl_suspend(struct of_device *op, pm_message_t state) +static int sata_fsl_suspend(struct platform_device *op, pm_message_t state) { struct ata_host *host = dev_get_drvdata(&op->dev); return ata_host_suspend(host, state); } -static int sata_fsl_resume(struct of_device *op) +static int sata_fsl_resume(struct platform_device *op) { struct ata_host *host = dev_get_drvdata(&op->dev); struct sata_fsl_host_priv *host_priv = host->private_data; diff --git a/drivers/atm/fore200e.c b/drivers/atm/fore200e.c index b7385e07771..c8fc69c85a0 100644 --- a/drivers/atm/fore200e.c +++ b/drivers/atm/fore200e.c @@ -674,7 +674,7 @@ static void fore200e_sba_write(u32 val, volatile u32 __iomem *addr) static u32 fore200e_sba_dma_map(struct fore200e *fore200e, void* virt_addr, int size, int direction) { - struct of_device *op = fore200e->bus_dev; + struct platform_device *op = fore200e->bus_dev; u32 dma_addr; dma_addr = dma_map_single(&op->dev, virt_addr, size, direction); @@ -687,7 +687,7 @@ static u32 fore200e_sba_dma_map(struct fore200e *fore200e, void* virt_addr, int static void fore200e_sba_dma_unmap(struct fore200e *fore200e, u32 dma_addr, int size, int direction) { - struct of_device *op = fore200e->bus_dev; + struct platform_device *op = fore200e->bus_dev; DPRINTK(3, "SBUS DVMA unmapping: dma_addr = 0x%08x, size = %d, direction = %d,\n", dma_addr, size, direction); @@ -697,7 +697,7 @@ static void fore200e_sba_dma_unmap(struct fore200e *fore200e, u32 dma_addr, int static void fore200e_sba_dma_sync_for_cpu(struct fore200e *fore200e, u32 dma_addr, int size, int direction) { - struct of_device *op = fore200e->bus_dev; + struct platform_device *op = fore200e->bus_dev; DPRINTK(3, "SBUS DVMA sync: dma_addr = 0x%08x, size = %d, direction = %d\n", dma_addr, size, direction); @@ -706,7 +706,7 @@ static void fore200e_sba_dma_sync_for_cpu(struct fore200e *fore200e, u32 dma_add static void fore200e_sba_dma_sync_for_device(struct fore200e *fore200e, u32 dma_addr, int size, int direction) { - struct of_device *op = fore200e->bus_dev; + struct platform_device *op = fore200e->bus_dev; DPRINTK(3, "SBUS DVMA sync: dma_addr = 0x%08x, size = %d, direction = %d\n", dma_addr, size, direction); @@ -719,7 +719,7 @@ static void fore200e_sba_dma_sync_for_device(struct fore200e *fore200e, u32 dma_ static int fore200e_sba_dma_chunk_alloc(struct fore200e *fore200e, struct chunk *chunk, int size, int nbr, int alignment) { - struct of_device *op = fore200e->bus_dev; + struct platform_device *op = fore200e->bus_dev; chunk->alloc_size = chunk->align_size = size * nbr; @@ -738,7 +738,7 @@ static int fore200e_sba_dma_chunk_alloc(struct fore200e *fore200e, struct chunk /* free a DVMA consistent chunk of memory */ static void fore200e_sba_dma_chunk_free(struct fore200e *fore200e, struct chunk *chunk) { - struct of_device *op = fore200e->bus_dev; + struct platform_device *op = fore200e->bus_dev; dma_free_coherent(&op->dev, chunk->alloc_size, chunk->alloc_addr, chunk->dma_addr); @@ -770,7 +770,7 @@ static void fore200e_sba_reset(struct fore200e *fore200e) static int __init fore200e_sba_map(struct fore200e *fore200e) { - struct of_device *op = fore200e->bus_dev; + struct platform_device *op = fore200e->bus_dev; unsigned int bursts; /* gain access to the SBA specific registers */ @@ -800,7 +800,7 @@ static int __init fore200e_sba_map(struct fore200e *fore200e) static void fore200e_sba_unmap(struct fore200e *fore200e) { - struct of_device *op = fore200e->bus_dev; + struct platform_device *op = fore200e->bus_dev; of_iounmap(&op->resource[0], fore200e->regs.sba.hcr, SBA200E_HCR_LENGTH); of_iounmap(&op->resource[1], fore200e->regs.sba.bsr, SBA200E_BSR_LENGTH); @@ -816,7 +816,7 @@ static int __init fore200e_sba_configure(struct fore200e *fore200e) static int __init fore200e_sba_prom_read(struct fore200e *fore200e, struct prom_data *prom) { - struct of_device *op = fore200e->bus_dev; + struct platform_device *op = fore200e->bus_dev; const u8 *prop; int len; @@ -840,7 +840,7 @@ static int __init fore200e_sba_prom_read(struct fore200e *fore200e, struct prom_ static int fore200e_sba_proc_read(struct fore200e *fore200e, char *page) { - struct of_device *op = fore200e->bus_dev; + struct platform_device *op = fore200e->bus_dev; const struct linux_prom_registers *regs; regs = of_get_property(op->dev.of_node, "reg", NULL); @@ -2513,7 +2513,7 @@ fore200e_load_and_start_fw(struct fore200e* fore200e) device = &((struct pci_dev *) fore200e->bus_dev)->dev; #ifdef CONFIG_SBUS else if (strcmp(fore200e->bus->model_name, "SBA-200E") == 0) - device = &((struct of_device *) fore200e->bus_dev)->dev; + device = &((struct platform_device *) fore200e->bus_dev)->dev; #endif else return err; @@ -2643,7 +2643,7 @@ fore200e_init(struct fore200e* fore200e) } #ifdef CONFIG_SBUS -static int __devinit fore200e_sba_probe(struct of_device *op, +static int __devinit fore200e_sba_probe(struct platform_device *op, const struct of_device_id *match) { const struct fore200e_bus *bus = match->data; @@ -2675,7 +2675,7 @@ static int __devinit fore200e_sba_probe(struct of_device *op, return 0; } -static int __devexit fore200e_sba_remove(struct of_device *op) +static int __devexit fore200e_sba_remove(struct platform_device *op) { struct fore200e *fore200e = dev_get_drvdata(&op->dev); diff --git a/drivers/block/xsysace.c b/drivers/block/xsysace.c index b71888b909a..2982b3ee946 100644 --- a/drivers/block/xsysace.c +++ b/drivers/block/xsysace.c @@ -1194,7 +1194,7 @@ static struct platform_driver ace_platform_driver = { #if defined(CONFIG_OF) static int __devinit -ace_of_probe(struct of_device *op, const struct of_device_id *match) +ace_of_probe(struct platform_device *op, const struct of_device_id *match) { struct resource res; resource_size_t physaddr; @@ -1226,7 +1226,7 @@ ace_of_probe(struct of_device *op, const struct of_device_id *match) return ace_alloc(&op->dev, id ? *id : 0, physaddr, irq, bus_width); } -static int __devexit ace_of_remove(struct of_device *op) +static int __devexit ace_of_remove(struct platform_device *op) { ace_free(&op->dev); return 0; diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 7cfcc629a7f..3d44ec724c1 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -1002,7 +1002,7 @@ config SCx200_GPIO config PC8736x_GPIO tristate "NatSemi PC8736x GPIO Support" - depends on X86 + depends on X86_32 default SCx200_GPIO # mostly N select NSC_GPIO # needed for support routines help diff --git a/drivers/char/hw_random/n2-drv.c b/drivers/char/hw_random/n2-drv.c index 7a4f080f835..1acdb250951 100644 --- a/drivers/char/hw_random/n2-drv.c +++ b/drivers/char/hw_random/n2-drv.c @@ -619,7 +619,7 @@ static void __devinit n2rng_driver_version(void) pr_info("%s", version); } -static int __devinit n2rng_probe(struct of_device *op, +static int __devinit n2rng_probe(struct platform_device *op, const struct of_device_id *match) { int victoria_falls = (match->data != NULL); @@ -714,7 +714,7 @@ out: return err; } -static int __devexit n2rng_remove(struct of_device *op) +static int __devexit n2rng_remove(struct platform_device *op) { struct n2rng *np = dev_get_drvdata(&op->dev); diff --git a/drivers/char/hw_random/n2rng.h b/drivers/char/hw_random/n2rng.h index a2b81e7bfc1..4bea07f3097 100644 --- a/drivers/char/hw_random/n2rng.h +++ b/drivers/char/hw_random/n2rng.h @@ -65,7 +65,7 @@ struct n2rng_unit { }; struct n2rng { - struct of_device *op; + struct platform_device *op; unsigned long flags; #define N2RNG_FLAG_VF 0x00000001 /* Victoria Falls RNG, else N2 */ diff --git a/drivers/char/hw_random/pasemi-rng.c b/drivers/char/hw_random/pasemi-rng.c index 261ba8f22b8..a31c830ca8c 100644 --- a/drivers/char/hw_random/pasemi-rng.c +++ b/drivers/char/hw_random/pasemi-rng.c @@ -94,7 +94,7 @@ static struct hwrng pasemi_rng = { .data_read = pasemi_rng_data_read, }; -static int __devinit rng_probe(struct of_device *ofdev, +static int __devinit rng_probe(struct platform_device *ofdev, const struct of_device_id *match) { void __iomem *rng_regs; @@ -123,7 +123,7 @@ static int __devinit rng_probe(struct of_device *ofdev, return err; } -static int __devexit rng_remove(struct of_device *dev) +static int __devexit rng_remove(struct platform_device *dev) { void __iomem *rng_regs = (void __iomem *)pasemi_rng.priv; diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index ff68e7c34ce..3822b4f49c8 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -1804,9 +1804,12 @@ static int hotmod_handler(const char *val, struct kernel_param *kp) info->irq_setup = std_irq_setup; info->slave_addr = ipmb; - if (!add_smi(info)) + if (!add_smi(info)) { if (try_smi_init(info)) cleanup_one_si(info); + } else { + kfree(info); + } } else { /* remove */ struct smi_info *e, *tmp_e; @@ -1890,9 +1893,12 @@ static __devinit void hardcode_find_bmc(void) info->irq_setup = std_irq_setup; info->slave_addr = slave_addrs[i]; - if (!add_smi(info)) + if (!add_smi(info)) { if (try_smi_init(info)) cleanup_one_si(info); + } else { + kfree(info); + } } } @@ -1965,8 +1971,8 @@ static int acpi_gpe_irq_setup(struct smi_info *info) /* * Defined at - * http://h21007.www2.hp.com/dspp/files/unprotected/devresource/ - * Docs/TechPapers/IA64/hpspmi.pdf + * http://h21007.www2.hp.com/portal/download/files + * /unprot/hpspmi.pdf */ struct SPMITable { s8 Signature[4]; @@ -2013,18 +2019,12 @@ struct SPMITable { static __devinit int try_init_spmi(struct SPMITable *spmi) { struct smi_info *info; - u8 addr_space; if (spmi->IPMIlegacy != 1) { printk(KERN_INFO PFX "Bad SPMI legacy %d\n", spmi->IPMIlegacy); return -ENODEV; } - if (spmi->addr.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) - addr_space = IPMI_MEM_ADDR_SPACE; - else - addr_space = IPMI_IO_ADDR_SPACE; - info = kzalloc(sizeof(*info), GFP_KERNEL); if (!info) { printk(KERN_ERR PFX "Could not allocate SI data (3)\n"); @@ -2088,7 +2088,13 @@ static __devinit int try_init_spmi(struct SPMITable *spmi) } info->io.addr_data = spmi->addr.address; - add_smi(info); + pr_info("ipmi_si: SPMI: %s %#lx regsize %d spacing %d irq %d\n", + (info->io.addr_type == IPMI_IO_ADDR_SPACE) ? "io" : "mem", + info->io.addr_data, info->io.regsize, info->io.regspacing, + info->irq); + + if (add_smi(info)) + kfree(info); return 0; } @@ -2204,7 +2210,10 @@ static int __devinit ipmi_pnp_probe(struct pnp_dev *dev, res, info->io.regsize, info->io.regspacing, info->irq); - return add_smi(info); + if (add_smi(info)) + goto err_free; + + return 0; err_free: kfree(info); @@ -2362,7 +2371,13 @@ static __devinit void try_init_dmi(struct dmi_ipmi_data *ipmi_data) if (info->irq) info->irq_setup = std_irq_setup; - add_smi(info); + pr_info("ipmi_si: SMBIOS: %s %#lx regsize %d spacing %d irq %d\n", + (info->io.addr_type == IPMI_IO_ADDR_SPACE) ? "io" : "mem", + info->io.addr_data, info->io.regsize, info->io.regspacing, + info->irq); + + if (add_smi(info)) + kfree(info); } static void __devinit dmi_find_bmc(void) @@ -2468,7 +2483,10 @@ static int __devinit ipmi_pci_probe(struct pci_dev *pdev, &pdev->resource[0], info->io.regsize, info->io.regspacing, info->irq); - return add_smi(info); + if (add_smi(info)) + kfree(info); + + return 0; } static void __devexit ipmi_pci_remove(struct pci_dev *pdev) @@ -2510,7 +2528,7 @@ static struct pci_driver ipmi_pci_driver = { #ifdef CONFIG_PPC_OF -static int __devinit ipmi_of_probe(struct of_device *dev, +static int __devinit ipmi_of_probe(struct platform_device *dev, const struct of_device_id *match) { struct smi_info *info; @@ -2581,10 +2599,15 @@ static int __devinit ipmi_of_probe(struct of_device *dev, dev_set_drvdata(&dev->dev, info); - return add_smi(info); + if (add_smi(info)) { + kfree(info); + return -EBUSY; + } + + return 0; } -static int __devexit ipmi_of_remove(struct of_device *dev) +static int __devexit ipmi_of_remove(struct platform_device *dev) { cleanup_one_si(dev_get_drvdata(&dev->dev)); return 0; @@ -3014,6 +3037,8 @@ static __devinit void default_find_bmc(void) info->io.addr_data); } else cleanup_one_si(info); + } else { + kfree(info); } } } @@ -3041,7 +3066,7 @@ static int add_smi(struct smi_info *new_smi) si_to_str[new_smi->si_type]); mutex_lock(&smi_infos_lock); if (!is_new_interface(new_smi)) { - printk(KERN_CONT PFX "duplicate interface\n"); + printk(KERN_CONT " duplicate interface\n"); rv = -EBUSY; goto out_err; } diff --git a/drivers/char/n_gsm.c b/drivers/char/n_gsm.c index 099105e0894..04ef3ef0a42 100644 --- a/drivers/char/n_gsm.c +++ b/drivers/char/n_gsm.c @@ -919,7 +919,7 @@ static void gsm_dlci_data_sweep(struct gsm_mux *gsm) else len = gsm_dlci_data_output_framed(gsm, dlci); if (len < 0) - return; + break; /* DLCI empty - try the next */ if (len == 0) i++; diff --git a/drivers/char/nozomi.c b/drivers/char/nozomi.c index 18af923093c..817169cbb24 100644 --- a/drivers/char/nozomi.c +++ b/drivers/char/nozomi.c @@ -1743,8 +1743,7 @@ static int ntty_write_room(struct tty_struct *tty) if (dc) { mutex_lock(&port->tty_sem); if (port->port.count) - room = port->fifo_ul.size - - kfifo_len(&port->fifo_ul); + room = kfifo_avail(&port->fifo_ul); mutex_unlock(&port->tty_sem); } return room; diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c index 95acb8c880f..dfa8b3062fd 100644 --- a/drivers/char/rtc.c +++ b/drivers/char/rtc.c @@ -961,7 +961,7 @@ static int __init rtc_init(void) #endif #ifdef CONFIG_SPARC32 struct device_node *ebus_dp; - struct of_device *op; + struct platform_device *op; #else void *r; #ifdef RTC_IRQ diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.c b/drivers/char/xilinx_hwicap/xilinx_hwicap.c index ed8a9cec2a0..0ed763cd2e7 100644 --- a/drivers/char/xilinx_hwicap/xilinx_hwicap.c +++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.c @@ -761,7 +761,7 @@ static struct platform_driver hwicap_platform_driver = { #if defined(CONFIG_OF) static int __devinit -hwicap_of_probe(struct of_device *op, const struct of_device_id *match) +hwicap_of_probe(struct platform_device *op, const struct of_device_id *match) { struct resource res; const unsigned int *id; @@ -798,7 +798,7 @@ hwicap_of_probe(struct of_device *op, const struct of_device_id *match) regs); } -static int __devexit hwicap_of_remove(struct of_device *op) +static int __devexit hwicap_of_remove(struct platform_device *op) { return hwicap_remove(&op->dev); } diff --git a/drivers/crypto/amcc/crypto4xx_core.c b/drivers/crypto/amcc/crypto4xx_core.c index 983530ba04a..2b1baee525b 100644 --- a/drivers/crypto/amcc/crypto4xx_core.c +++ b/drivers/crypto/amcc/crypto4xx_core.c @@ -1150,7 +1150,7 @@ struct crypto4xx_alg_common crypto4xx_alg[] = { /** * Module Initialization Routine */ -static int __init crypto4xx_probe(struct of_device *ofdev, +static int __init crypto4xx_probe(struct platform_device *ofdev, const struct of_device_id *match) { int rc; @@ -1258,7 +1258,7 @@ err_alloc_dev: return rc; } -static int __exit crypto4xx_remove(struct of_device *ofdev) +static int __exit crypto4xx_remove(struct platform_device *ofdev) { struct device *dev = &ofdev->dev; struct crypto4xx_core_device *core_dev = dev_get_drvdata(dev); diff --git a/drivers/crypto/amcc/crypto4xx_core.h b/drivers/crypto/amcc/crypto4xx_core.h index da9cbe3b9fc..bac0bdeb4b5 100644 --- a/drivers/crypto/amcc/crypto4xx_core.h +++ b/drivers/crypto/amcc/crypto4xx_core.h @@ -104,7 +104,7 @@ struct crypto4xx_device { struct crypto4xx_core_device { struct device *device; - struct of_device *ofdev; + struct platform_device *ofdev; struct crypto4xx_device *dev; u32 int_status; u32 irq; diff --git a/drivers/crypto/ixp4xx_crypto.c b/drivers/crypto/ixp4xx_crypto.c index f17ddf37a1e..0d662213c06 100644 --- a/drivers/crypto/ixp4xx_crypto.c +++ b/drivers/crypto/ixp4xx_crypto.c @@ -97,8 +97,13 @@ struct buffer_desc { u32 phys_next; +#ifdef __ARMEB__ u16 buf_len; u16 pkt_len; +#else + u16 pkt_len; + u16 buf_len; +#endif u32 phys_addr; u32 __reserved[4]; struct buffer_desc *next; @@ -106,17 +111,30 @@ struct buffer_desc { }; struct crypt_ctl { +#ifdef __ARMEB__ u8 mode; /* NPE_OP_* operation mode */ u8 init_len; u16 reserved; +#else + u16 reserved; + u8 init_len; + u8 mode; /* NPE_OP_* operation mode */ +#endif u8 iv[MAX_IVLEN]; /* IV for CBC mode or CTR IV for CTR mode */ u32 icv_rev_aes; /* icv or rev aes */ u32 src_buf; u32 dst_buf; +#ifdef __ARMEB__ u16 auth_offs; /* Authentication start offset */ u16 auth_len; /* Authentication data length */ u16 crypt_offs; /* Cryption start offset */ u16 crypt_len; /* Cryption data length */ +#else + u16 auth_len; /* Authentication data length */ + u16 auth_offs; /* Authentication start offset */ + u16 crypt_len; /* Cryption data length */ + u16 crypt_offs; /* Cryption start offset */ +#endif u32 aadAddr; /* Additional Auth Data Addr for CCM mode */ u32 crypto_ctx; /* NPE Crypto Param structure address */ @@ -652,6 +670,9 @@ static int setup_auth(struct crypto_tfm *tfm, int encrypt, unsigned authsize, /* write cfg word to cryptinfo */ cfgword = algo->cfgword | ( authsize << 6); /* (authsize/4) << 8 */ +#ifndef __ARMEB__ + cfgword ^= 0xAA000000; /* change the "byte swap" flags */ +#endif *(u32*)cinfo = cpu_to_be32(cfgword); cinfo += sizeof(cfgword); diff --git a/drivers/crypto/n2_core.c b/drivers/crypto/n2_core.c index 26af2dd5d83..88ee01510ec 100644 --- a/drivers/crypto/n2_core.c +++ b/drivers/crypto/n2_core.c @@ -1552,7 +1552,7 @@ static void __exit n2_unregister_algs(void) /* To map CWQ queues to interrupt sources, the hypervisor API provides * a devino. This isn't very useful to us because all of the - * interrupts listed in the of_device node have been translated to + * interrupts listed in the device_node have been translated to * Linux virtual IRQ cookie numbers. * * So we have to back-translate, going through the 'intr' and 'ino' @@ -1560,7 +1560,7 @@ static void __exit n2_unregister_algs(void) * 'interrupts' property entries, in order to to figure out which * devino goes to which already-translated IRQ. */ -static int find_devino_index(struct of_device *dev, struct spu_mdesc_info *ip, +static int find_devino_index(struct platform_device *dev, struct spu_mdesc_info *ip, unsigned long dev_ino) { const unsigned int *dev_intrs; @@ -1580,7 +1580,7 @@ static int find_devino_index(struct of_device *dev, struct spu_mdesc_info *ip, if (!dev_intrs) return -ENODEV; - for (i = 0; i < dev->num_irqs; i++) { + for (i = 0; i < dev->archdata.num_irqs; i++) { if (dev_intrs[i] == intr) return i; } @@ -1588,7 +1588,7 @@ static int find_devino_index(struct of_device *dev, struct spu_mdesc_info *ip, return -ENODEV; } -static int spu_map_ino(struct of_device *dev, struct spu_mdesc_info *ip, +static int spu_map_ino(struct platform_device *dev, struct spu_mdesc_info *ip, const char *irq_name, struct spu_queue *p, irq_handler_t handler) { @@ -1603,7 +1603,7 @@ static int spu_map_ino(struct of_device *dev, struct spu_mdesc_info *ip, if (index < 0) return index; - p->irq = dev->irqs[index]; + p->irq = dev->archdata.irqs[index]; sprintf(p->irq_name, "%s-%d", irq_name, index); @@ -1736,7 +1736,7 @@ static void spu_list_destroy(struct list_head *list) * gathering cpu membership information. */ static int spu_mdesc_walk_arcs(struct mdesc_handle *mdesc, - struct of_device *dev, + struct platform_device *dev, u64 node, struct spu_queue *p, struct spu_queue **table) { @@ -1763,7 +1763,7 @@ static int spu_mdesc_walk_arcs(struct mdesc_handle *mdesc, /* Process an 'exec-unit' MDESC node of type 'cwq'. */ static int handle_exec_unit(struct spu_mdesc_info *ip, struct list_head *list, - struct of_device *dev, struct mdesc_handle *mdesc, + struct platform_device *dev, struct mdesc_handle *mdesc, u64 node, const char *iname, unsigned long q_type, irq_handler_t handler, struct spu_queue **table) { @@ -1794,7 +1794,7 @@ static int handle_exec_unit(struct spu_mdesc_info *ip, struct list_head *list, return spu_map_ino(dev, ip, iname, p, handler); } -static int spu_mdesc_scan(struct mdesc_handle *mdesc, struct of_device *dev, +static int spu_mdesc_scan(struct mdesc_handle *mdesc, struct platform_device *dev, struct spu_mdesc_info *ip, struct list_head *list, const char *exec_name, unsigned long q_type, irq_handler_t handler, struct spu_queue **table) @@ -1855,7 +1855,7 @@ static int __devinit get_irq_props(struct mdesc_handle *mdesc, u64 node, } static int __devinit grab_mdesc_irq_props(struct mdesc_handle *mdesc, - struct of_device *dev, + struct platform_device *dev, struct spu_mdesc_info *ip, const char *node_name) { @@ -2004,7 +2004,7 @@ static void __devinit n2_spu_driver_version(void) pr_info("%s", version); } -static int __devinit n2_crypto_probe(struct of_device *dev, +static int __devinit n2_crypto_probe(struct platform_device *dev, const struct of_device_id *match) { struct mdesc_handle *mdesc; @@ -2081,7 +2081,7 @@ out_free_n2cp: return err; } -static int __devexit n2_crypto_remove(struct of_device *dev) +static int __devexit n2_crypto_remove(struct platform_device *dev) { struct n2_crypto *np = dev_get_drvdata(&dev->dev); @@ -2116,7 +2116,7 @@ static void free_ncp(struct n2_mau *mp) kfree(mp); } -static int __devinit n2_mau_probe(struct of_device *dev, +static int __devinit n2_mau_probe(struct platform_device *dev, const struct of_device_id *match) { struct mdesc_handle *mdesc; @@ -2184,7 +2184,7 @@ out_free_ncp: return err; } -static int __devexit n2_mau_remove(struct of_device *dev) +static int __devexit n2_mau_remove(struct platform_device *dev) { struct n2_mau *mp = dev_get_drvdata(&dev->dev); diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c index 97f4af1d8a6..4bcd825b573 100644 --- a/drivers/crypto/talitos.c +++ b/drivers/crypto/talitos.c @@ -118,7 +118,7 @@ struct talitos_channel { struct talitos_private { struct device *dev; - struct of_device *ofdev; + struct platform_device *ofdev; void __iomem *reg; int irq; @@ -2308,7 +2308,7 @@ static int hw_supports(struct device *dev, __be32 desc_hdr_template) return ret; } -static int talitos_remove(struct of_device *ofdev) +static int talitos_remove(struct platform_device *ofdev) { struct device *dev = &ofdev->dev; struct talitos_private *priv = dev_get_drvdata(dev); @@ -2401,7 +2401,7 @@ static struct talitos_crypto_alg *talitos_alg_alloc(struct device *dev, return t_alg; } -static int talitos_probe(struct of_device *ofdev, +static int talitos_probe(struct platform_device *ofdev, const struct of_device_id *match) { struct device *dev = &ofdev->dev; diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index fed57634b6c..9520cf02edc 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -141,7 +141,7 @@ config TXX9_DMAC config SH_DMAE tristate "Renesas SuperH DMAC support" - depends on SUPERH && SH_DMA + depends on (SUPERH && SH_DMA) || (ARM && ARCH_SHMOBILE) depends on !SH_DMA_API select DMA_ENGINE help diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index f0fd6db6063..cea08bed9cf 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -1297,7 +1297,7 @@ static void fsl_dma_chan_remove(struct fsldma_chan *chan) kfree(chan); } -static int __devinit fsldma_of_probe(struct of_device *op, +static int __devinit fsldma_of_probe(struct platform_device *op, const struct of_device_id *match) { struct fsldma_device *fdev; @@ -1382,7 +1382,7 @@ out_return: return err; } -static int fsldma_of_remove(struct of_device *op) +static int fsldma_of_remove(struct platform_device *op) { struct fsldma_device *fdev; unsigned int i; diff --git a/drivers/dma/mpc512x_dma.c b/drivers/dma/mpc512x_dma.c index 14a8c0f1698..4e9cbf30059 100644 --- a/drivers/dma/mpc512x_dma.c +++ b/drivers/dma/mpc512x_dma.c @@ -627,7 +627,7 @@ mpc_dma_prep_memcpy(struct dma_chan *chan, dma_addr_t dst, dma_addr_t src, return &mdesc->desc; } -static int __devinit mpc_dma_probe(struct of_device *op, +static int __devinit mpc_dma_probe(struct platform_device *op, const struct of_device_id *match) { struct device_node *dn = op->dev.of_node; @@ -753,7 +753,7 @@ static int __devinit mpc_dma_probe(struct of_device *op, return retval; } -static int __devexit mpc_dma_remove(struct of_device *op) +static int __devexit mpc_dma_remove(struct platform_device *op) { struct device *dev = &op->dev; struct mpc_dma *mdma = dev_get_drvdata(dev); diff --git a/drivers/dma/ppc4xx/adma.c b/drivers/dma/ppc4xx/adma.c index 7c3747902a3..0d58a4a4487 100644 --- a/drivers/dma/ppc4xx/adma.c +++ b/drivers/dma/ppc4xx/adma.c @@ -4257,11 +4257,11 @@ static int ppc440spe_adma_setup_irqs(struct ppc440spe_adma_device *adev, struct ppc440spe_adma_chan *chan, int *initcode) { - struct of_device *ofdev; + struct platform_device *ofdev; struct device_node *np; int ret; - ofdev = container_of(adev->dev, struct of_device, dev); + ofdev = container_of(adev->dev, struct platform_device, dev); np = ofdev->dev.of_node; if (adev->id != PPC440SPE_XOR_ID) { adev->err_irq = irq_of_parse_and_map(np, 1); @@ -4393,7 +4393,7 @@ static void ppc440spe_adma_release_irqs(struct ppc440spe_adma_device *adev, /** * ppc440spe_adma_probe - probe the asynch device */ -static int __devinit ppc440spe_adma_probe(struct of_device *ofdev, +static int __devinit ppc440spe_adma_probe(struct platform_device *ofdev, const struct of_device_id *match) { struct device_node *np = ofdev->dev.of_node; @@ -4625,7 +4625,7 @@ out: /** * ppc440spe_adma_remove - remove the asynch device */ -static int __devexit ppc440spe_adma_remove(struct of_device *ofdev) +static int __devexit ppc440spe_adma_remove(struct platform_device *ofdev) { struct ppc440spe_adma_device *adev = dev_get_drvdata(&ofdev->dev); struct device_node *np = ofdev->dev.of_node; diff --git a/drivers/dma/shdma.c b/drivers/dma/shdma.c index a2a519fd2a2..fb64cf36ba6 100644 --- a/drivers/dma/shdma.c +++ b/drivers/dma/shdma.c @@ -816,7 +816,7 @@ static irqreturn_t sh_dmae_interrupt(int irq, void *data) return ret; } -#if defined(CONFIG_CPU_SH4) +#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE) static irqreturn_t sh_dmae_err(int irq, void *data) { struct sh_dmae_device *shdev = (struct sh_dmae_device *)data; @@ -1057,7 +1057,7 @@ static int __init sh_dmae_probe(struct platform_device *pdev) /* Default transfer size of 32 bytes requires 32-byte alignment */ shdev->common.copy_align = LOG2_DEFAULT_XFER_SIZE; -#if defined(CONFIG_CPU_SH4) +#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE) chanirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 1); if (!chanirq_res) @@ -1082,7 +1082,7 @@ static int __init sh_dmae_probe(struct platform_device *pdev) #else chanirq_res = errirq_res; -#endif /* CONFIG_CPU_SH4 */ +#endif /* CONFIG_CPU_SH4 || CONFIG_ARCH_SHMOBILE */ if (chanirq_res->start == chanirq_res->end && !platform_get_resource(pdev, IORESOURCE_IRQ, 1)) { @@ -1129,7 +1129,7 @@ static int __init sh_dmae_probe(struct platform_device *pdev) chan_probe_err: sh_dmae_chan_remove(shdev); eirqres: -#if defined(CONFIG_CPU_SH4) +#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE) free_irq(errirq, shdev); eirq_err: #endif diff --git a/drivers/edac/i5000_edac.c b/drivers/edac/i5000_edac.c index 996c1bdb5a3..a5cefab8d65 100644 --- a/drivers/edac/i5000_edac.c +++ b/drivers/edac/i5000_edac.c @@ -1482,7 +1482,7 @@ static int __devinit i5000_init_one(struct pci_dev *pdev, /* wake up device */ rc = pci_enable_device(pdev); - if (rc == -EIO) + if (rc) return rc; /* now probe and enable the device */ diff --git a/drivers/edac/i5400_edac.c b/drivers/edac/i5400_edac.c index 010c1d6526f..38a9be9e1c7 100644 --- a/drivers/edac/i5400_edac.c +++ b/drivers/edac/i5400_edac.c @@ -1348,7 +1348,7 @@ static int __devinit i5400_init_one(struct pci_dev *pdev, /* wake up device */ rc = pci_enable_device(pdev); - if (rc == -EIO) + if (rc) return rc; /* now probe and enable the device */ diff --git a/drivers/edac/mpc85xx_edac.c b/drivers/edac/mpc85xx_edac.c index 1052340e680..b123bb308a4 100644 --- a/drivers/edac/mpc85xx_edac.c +++ b/drivers/edac/mpc85xx_edac.c @@ -43,7 +43,7 @@ static u32 orig_pci_err_en; #endif static u32 orig_l2_err_disable; -#ifdef CONFIG_MPC85xx +#ifdef CONFIG_FSL_SOC_BOOKE static u32 orig_hid1[2]; #endif @@ -200,7 +200,7 @@ static irqreturn_t mpc85xx_pci_isr(int irq, void *dev_id) return IRQ_HANDLED; } -static int __devinit mpc85xx_pci_err_probe(struct of_device *op, +static int __devinit mpc85xx_pci_err_probe(struct platform_device *op, const struct of_device_id *match) { struct edac_pci_ctl_info *pci; @@ -305,7 +305,7 @@ err: return res; } -static int mpc85xx_pci_err_remove(struct of_device *op) +static int mpc85xx_pci_err_remove(struct platform_device *op) { struct edac_pci_ctl_info *pci = dev_get_drvdata(&op->dev); struct mpc85xx_pci_pdata *pdata = pci->pvt_info; @@ -503,7 +503,7 @@ static irqreturn_t mpc85xx_l2_isr(int irq, void *dev_id) return IRQ_HANDLED; } -static int __devinit mpc85xx_l2_err_probe(struct of_device *op, +static int __devinit mpc85xx_l2_err_probe(struct platform_device *op, const struct of_device_id *match) { struct edac_device_ctl_info *edac_dev; @@ -613,7 +613,7 @@ err: return res; } -static int mpc85xx_l2_err_remove(struct of_device *op) +static int mpc85xx_l2_err_remove(struct platform_device *op) { struct edac_device_ctl_info *edac_dev = dev_get_drvdata(&op->dev); struct mpc85xx_l2_pdata *pdata = edac_dev->pvt_info; @@ -647,7 +647,10 @@ static struct of_device_id mpc85xx_l2_err_of_match[] = { { .compatible = "fsl,mpc8555-l2-cache-controller", }, { .compatible = "fsl,mpc8560-l2-cache-controller", }, { .compatible = "fsl,mpc8568-l2-cache-controller", }, + { .compatible = "fsl,mpc8569-l2-cache-controller", }, { .compatible = "fsl,mpc8572-l2-cache-controller", }, + { .compatible = "fsl,p1020-l2-cache-controller", }, + { .compatible = "fsl,p1021-l2-cache-controller", }, { .compatible = "fsl,p2020-l2-cache-controller", }, {}, }; @@ -953,7 +956,7 @@ static void __devinit mpc85xx_init_csrows(struct mem_ctl_info *mci) } } -static int __devinit mpc85xx_mc_err_probe(struct of_device *op, +static int __devinit mpc85xx_mc_err_probe(struct platform_device *op, const struct of_device_id *match) { struct mem_ctl_info *mci; @@ -1085,7 +1088,7 @@ err: return res; } -static int mpc85xx_mc_err_remove(struct of_device *op) +static int mpc85xx_mc_err_remove(struct platform_device *op) { struct mem_ctl_info *mci = dev_get_drvdata(&op->dev); struct mpc85xx_mc_pdata *pdata = mci->pvt_info; @@ -1125,7 +1128,10 @@ static struct of_device_id mpc85xx_mc_err_of_match[] = { { .compatible = "fsl,mpc8569-memory-controller", }, { .compatible = "fsl,mpc8572-memory-controller", }, { .compatible = "fsl,mpc8349-memory-controller", }, + { .compatible = "fsl,p1020-memory-controller", }, + { .compatible = "fsl,p1021-memory-controller", }, { .compatible = "fsl,p2020-memory-controller", }, + { .compatible = "fsl,p4080-memory-controller", }, {}, }; MODULE_DEVICE_TABLE(of, mpc85xx_mc_err_of_match); @@ -1140,7 +1146,7 @@ static struct of_platform_driver mpc85xx_mc_err_driver = { }, }; -#ifdef CONFIG_MPC85xx +#ifdef CONFIG_FSL_SOC_BOOKE static void __init mpc85xx_mc_clear_rfxe(void *data) { orig_hid1[smp_processor_id()] = mfspr(SPRN_HID1); @@ -1179,7 +1185,7 @@ static int __init mpc85xx_mc_init(void) printk(KERN_WARNING EDAC_MOD_STR "PCI fails to register\n"); #endif -#ifdef CONFIG_MPC85xx +#ifdef CONFIG_FSL_SOC_BOOKE /* * need to clear HID1[RFXE] to disable machine check int * so we can catch it @@ -1193,7 +1199,7 @@ static int __init mpc85xx_mc_init(void) module_init(mpc85xx_mc_init); -#ifdef CONFIG_MPC85xx +#ifdef CONFIG_FSL_SOC_BOOKE static void __exit mpc85xx_mc_restore_hid1(void *data) { mtspr(SPRN_HID1, orig_hid1[smp_processor_id()]); @@ -1202,7 +1208,7 @@ static void __exit mpc85xx_mc_restore_hid1(void *data) static void __exit mpc85xx_mc_exit(void) { -#ifdef CONFIG_MPC85xx +#ifdef CONFIG_FSL_SOC_BOOKE on_each_cpu(mpc85xx_mc_restore_hid1, NULL, 0); #endif #ifdef CONFIG_PCI diff --git a/drivers/edac/ppc4xx_edac.c b/drivers/edac/ppc4xx_edac.c index e78839e89a0..070cea41b66 100644 --- a/drivers/edac/ppc4xx_edac.c +++ b/drivers/edac/ppc4xx_edac.c @@ -184,9 +184,9 @@ struct ppc4xx_ecc_status { /* Function Prototypes */ -static int ppc4xx_edac_probe(struct of_device *device, +static int ppc4xx_edac_probe(struct platform_device *device, const struct of_device_id *device_id); -static int ppc4xx_edac_remove(struct of_device *device); +static int ppc4xx_edac_remove(struct platform_device *device); /* Global Variables */ @@ -1014,7 +1014,7 @@ ppc4xx_edac_init_csrows(struct mem_ctl_info *mci, u32 mcopt1) */ static int __devinit ppc4xx_edac_mc_init(struct mem_ctl_info *mci, - struct of_device *op, + struct platform_device *op, const struct of_device_id *match, const dcr_host_t *dcr_host, u32 mcopt1) @@ -1108,7 +1108,7 @@ ppc4xx_edac_mc_init(struct mem_ctl_info *mci, * mapped and assigned. */ static int __devinit -ppc4xx_edac_register_irq(struct of_device *op, struct mem_ctl_info *mci) +ppc4xx_edac_register_irq(struct platform_device *op, struct mem_ctl_info *mci) { int status = 0; int ded_irq, sec_irq; @@ -1238,7 +1238,7 @@ ppc4xx_edac_map_dcrs(const struct device_node *np, dcr_host_t *dcr_host) * driver; otherwise, < 0 on error. */ static int __devinit -ppc4xx_edac_probe(struct of_device *op, const struct of_device_id *match) +ppc4xx_edac_probe(struct platform_device *op, const struct of_device_id *match) { int status = 0; u32 mcopt1, memcheck; @@ -1359,7 +1359,7 @@ ppc4xx_edac_probe(struct of_device *op, const struct of_device_id *match) * Unconditionally returns 0. */ static int -ppc4xx_edac_remove(struct of_device *op) +ppc4xx_edac_remove(struct platform_device *op) { struct mem_ctl_info *mci = dev_get_drvdata(&op->dev); struct ppc4xx_edac_pdata *pdata = mci->pvt_info; diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 7face915b96..510aa205454 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -195,6 +195,24 @@ config GPIO_PCF857X This driver provides an in-kernel interface to those GPIOs using platform-neutral GPIO calls. +config GPIO_SX150X + bool "Semtech SX150x I2C GPIO expander" + depends on I2C=y + default n + help + Say yes here to provide support for Semtech SX150-series I2C + GPIO expanders. Compatible models include: + + 8 bits: sx1508q + 16 bits: sx1509q + +config GPIO_STMPE + bool "STMPE GPIOs" + depends on MFD_STMPE + help + This enables support for the GPIOs found on the STMPE I/O + Expanders. + config GPIO_TC35892 bool "TC35892 GPIOs" depends on MFD_TC35892 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index e53dcff49b4..fc6019d9372 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_GPIO_MCP23S08) += mcp23s08.o obj-$(CONFIG_GPIO_PCA953X) += pca953x.o obj-$(CONFIG_GPIO_PCF857X) += pcf857x.o obj-$(CONFIG_GPIO_PL061) += pl061.o +obj-$(CONFIG_GPIO_STMPE) += stmpe-gpio.o obj-$(CONFIG_GPIO_TC35892) += tc35892-gpio.o obj-$(CONFIG_GPIO_TIMBERDALE) += timbgpio.o obj-$(CONFIG_GPIO_TWL4030) += twl4030-gpio.o @@ -35,3 +36,4 @@ obj-$(CONFIG_GPIO_WM8994) += wm8994-gpio.o obj-$(CONFIG_GPIO_SCH) += sch_gpio.o obj-$(CONFIG_GPIO_RDC321X) += rdc321x-gpio.o obj-$(CONFIG_GPIO_JANZ_TTL) += janz-ttl.o +obj-$(CONFIG_GPIO_SX150X) += sx150x.o diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 6a6bd569e1f..21da9c19a0c 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -57,9 +57,9 @@ struct gpio_desc { #define FLAG_TRIG_RISE 6 /* trigger on rising edge */ #define FLAG_ACTIVE_LOW 7 /* sysfs value has active low */ -#define PDESC_ID_SHIFT 16 /* add new flags before this one */ +#define ID_SHIFT 16 /* add new flags before this one */ -#define GPIO_FLAGS_MASK ((1 << PDESC_ID_SHIFT) - 1) +#define GPIO_FLAGS_MASK ((1 << ID_SHIFT) - 1) #define GPIO_TRIGGER_MASK (BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE)) #ifdef CONFIG_DEBUG_FS @@ -69,12 +69,7 @@ struct gpio_desc { static struct gpio_desc gpio_desc[ARCH_NR_GPIOS]; #ifdef CONFIG_GPIO_SYSFS -struct poll_desc { - struct work_struct work; - struct sysfs_dirent *value_sd; -}; - -static struct idr pdesc_idr; +static DEFINE_IDR(dirent_idr); #endif static inline void desc_set_label(struct gpio_desc *d, const char *label) @@ -325,24 +320,16 @@ static const DEVICE_ATTR(value, 0644, static irqreturn_t gpio_sysfs_irq(int irq, void *priv) { - struct work_struct *work = priv; + struct sysfs_dirent *value_sd = priv; - schedule_work(work); + sysfs_notify_dirent(value_sd); return IRQ_HANDLED; } -static void gpio_notify_sysfs(struct work_struct *work) -{ - struct poll_desc *pdesc; - - pdesc = container_of(work, struct poll_desc, work); - sysfs_notify_dirent(pdesc->value_sd); -} - static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev, unsigned long gpio_flags) { - struct poll_desc *pdesc; + struct sysfs_dirent *value_sd; unsigned long irq_flags; int ret, irq, id; @@ -353,18 +340,16 @@ static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev, if (irq < 0) return -EIO; - id = desc->flags >> PDESC_ID_SHIFT; - pdesc = idr_find(&pdesc_idr, id); - if (pdesc) { - free_irq(irq, &pdesc->work); - cancel_work_sync(&pdesc->work); - } + id = desc->flags >> ID_SHIFT; + value_sd = idr_find(&dirent_idr, id); + if (value_sd) + free_irq(irq, value_sd); desc->flags &= ~GPIO_TRIGGER_MASK; if (!gpio_flags) { ret = 0; - goto free_sd; + goto free_id; } irq_flags = IRQF_SHARED; @@ -375,55 +360,46 @@ static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev, irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ? IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING; - if (!pdesc) { - pdesc = kmalloc(sizeof(*pdesc), GFP_KERNEL); - if (!pdesc) { - ret = -ENOMEM; + if (!value_sd) { + value_sd = sysfs_get_dirent(dev->kobj.sd, NULL, "value"); + if (!value_sd) { + ret = -ENODEV; goto err_out; } do { ret = -ENOMEM; - if (idr_pre_get(&pdesc_idr, GFP_KERNEL)) - ret = idr_get_new_above(&pdesc_idr, - pdesc, 1, &id); + if (idr_pre_get(&dirent_idr, GFP_KERNEL)) + ret = idr_get_new_above(&dirent_idr, value_sd, + 1, &id); } while (ret == -EAGAIN); if (ret) - goto free_mem; + goto free_sd; desc->flags &= GPIO_FLAGS_MASK; - desc->flags |= (unsigned long)id << PDESC_ID_SHIFT; + desc->flags |= (unsigned long)id << ID_SHIFT; - if (desc->flags >> PDESC_ID_SHIFT != id) { + if (desc->flags >> ID_SHIFT != id) { ret = -ERANGE; goto free_id; } - - pdesc->value_sd = sysfs_get_dirent(dev->kobj.sd, NULL, "value"); - if (!pdesc->value_sd) { - ret = -ENODEV; - goto free_id; - } - INIT_WORK(&pdesc->work, gpio_notify_sysfs); } - ret = request_irq(irq, gpio_sysfs_irq, irq_flags, - "gpiolib", &pdesc->work); - if (ret) - goto free_sd; + ret = request_any_context_irq(irq, gpio_sysfs_irq, irq_flags, + "gpiolib", value_sd); + if (ret < 0) + goto free_id; desc->flags |= gpio_flags; return 0; -free_sd: - if (pdesc) - sysfs_put(pdesc->value_sd); free_id: - idr_remove(&pdesc_idr, id); + idr_remove(&dirent_idr, id); desc->flags &= GPIO_FLAGS_MASK; -free_mem: - kfree(pdesc); +free_sd: + if (value_sd) + sysfs_put(value_sd); err_out: return ret; } @@ -994,8 +970,6 @@ static int __init gpiolib_sysfs_init(void) unsigned long flags; unsigned gpio; - idr_init(&pdesc_idr); - status = class_register(&gpio_class); if (status < 0) return status; @@ -1272,7 +1246,7 @@ void gpio_free(unsigned gpio) if (chip && test_bit(FLAG_REQUESTED, &desc->flags)) { if (chip->free) { spin_unlock_irqrestore(&gpio_lock, flags); - might_sleep_if(extra_checks && chip->can_sleep); + might_sleep_if(chip->can_sleep); chip->free(chip, gpio - chip->base); spin_lock_irqsave(&gpio_lock, flags); } @@ -1410,7 +1384,7 @@ int gpio_direction_input(unsigned gpio) spin_unlock_irqrestore(&gpio_lock, flags); - might_sleep_if(extra_checks && chip->can_sleep); + might_sleep_if(chip->can_sleep); if (status) { status = chip->request(chip, gpio); @@ -1463,7 +1437,7 @@ int gpio_direction_output(unsigned gpio, int value) spin_unlock_irqrestore(&gpio_lock, flags); - might_sleep_if(extra_checks && chip->can_sleep); + might_sleep_if(chip->can_sleep); if (status) { status = chip->request(chip, gpio); @@ -1521,7 +1495,7 @@ int gpio_set_debounce(unsigned gpio, unsigned debounce) spin_unlock_irqrestore(&gpio_lock, flags); - might_sleep_if(extra_checks && chip->can_sleep); + might_sleep_if(chip->can_sleep); return chip->set_debounce(chip, gpio, debounce); @@ -1571,7 +1545,7 @@ int __gpio_get_value(unsigned gpio) struct gpio_chip *chip; chip = gpio_to_chip(gpio); - WARN_ON(extra_checks && chip->can_sleep); + WARN_ON(chip->can_sleep); return chip->get ? chip->get(chip, gpio - chip->base) : 0; } EXPORT_SYMBOL_GPL(__gpio_get_value); @@ -1590,7 +1564,7 @@ void __gpio_set_value(unsigned gpio, int value) struct gpio_chip *chip; chip = gpio_to_chip(gpio); - WARN_ON(extra_checks && chip->can_sleep); + WARN_ON(chip->can_sleep); chip->set(chip, gpio - chip->base, value); } EXPORT_SYMBOL_GPL(__gpio_set_value); diff --git a/drivers/gpio/max730x.c b/drivers/gpio/max730x.c index 7696a5625d5..94ce773f95f 100644 --- a/drivers/gpio/max730x.c +++ b/drivers/gpio/max730x.c @@ -54,7 +54,7 @@ static int max7301_direction_input(struct gpio_chip *chip, unsigned offset) { struct max7301 *ts = container_of(chip, struct max7301, chip); u8 *config; - u8 offset_bits; + u8 offset_bits, pin_config; int ret; /* First 4 pins are unused in the controller */ @@ -63,12 +63,15 @@ static int max7301_direction_input(struct gpio_chip *chip, unsigned offset) config = &ts->port_config[offset >> 2]; + if (ts->input_pullup_active & BIT(offset)) + pin_config = PIN_CONFIG_IN_PULLUP; + else + pin_config = PIN_CONFIG_IN_WO_PULLUP; + mutex_lock(&ts->lock); - /* Standard GPIO API doesn't support pull-ups, has to be extended. - * Hard-coding no pollup for now. */ *config = (*config & ~(PIN_CONFIG_MASK << offset_bits)) - | (PIN_CONFIG_IN_WO_PULLUP << offset_bits); + | (pin_config << offset_bits); ret = ts->write(ts->dev, 0x08 + (offset >> 2), *config); @@ -177,6 +180,7 @@ int __devinit __max730x_probe(struct max7301 *ts) /* Power up the chip and disable IRQ output */ ts->write(dev, 0x04, 0x01); + ts->input_pullup_active = pdata->input_pullup_active; ts->chip.label = dev->driver->name; ts->chip.direction_input = max7301_direction_input; @@ -191,13 +195,17 @@ int __devinit __max730x_probe(struct max7301 *ts) ts->chip.owner = THIS_MODULE; /* - * tristate all pins in hardware and cache the + * initialize pullups according to platform data and cache the * register values for later use. */ for (i = 1; i < 8; i++) { int j; - /* 0xAA means input with internal pullup disabled */ - ts->write(dev, 0x08 + i, 0xAA); + /* + * initialize port_config with "0xAA", which means + * input with internal pullup disabled. This is needed + * to avoid writing zeros (in the inner for loop), + * which is not allowed according to the datasheet. + */ ts->port_config[i] = 0xAA; for (j = 0; j < 4; j++) { int offset = (i - 1) * 4 + j; diff --git a/drivers/gpio/pcf857x.c b/drivers/gpio/pcf857x.c index 29f19ce3e80..879b473aab5 100644 --- a/drivers/gpio/pcf857x.c +++ b/drivers/gpio/pcf857x.c @@ -190,7 +190,6 @@ static int pcf857x_probe(struct i2c_client *client, pdata = client->dev.platform_data; if (!pdata) { dev_dbg(&client->dev, "no platform data\n"); - return -EINVAL; } /* Allocate, initialize, and register this gpio_chip. */ @@ -200,7 +199,7 @@ static int pcf857x_probe(struct i2c_client *client, mutex_init(&gpio->lock); - gpio->chip.base = pdata->gpio_base; + gpio->chip.base = pdata ? pdata->gpio_base : -1; gpio->chip.can_sleep = 1; gpio->chip.dev = &client->dev; gpio->chip.owner = THIS_MODULE; @@ -278,7 +277,7 @@ static int pcf857x_probe(struct i2c_client *client, * to zero, our software copy of the "latch" then matches the chip's * all-ones reset state. Otherwise it flags pins to be driven low. */ - gpio->out = ~pdata->n_latch; + gpio->out = pdata ? ~pdata->n_latch : ~0; status = gpiochip_add(&gpio->chip); if (status < 0) @@ -299,7 +298,7 @@ static int pcf857x_probe(struct i2c_client *client, /* Let platform code set up the GPIOs and their users. * Now is the first time anyone could use them. */ - if (pdata->setup) { + if (pdata && pdata->setup) { status = pdata->setup(client, gpio->chip.base, gpio->chip.ngpio, pdata->context); @@ -322,7 +321,7 @@ static int pcf857x_remove(struct i2c_client *client) struct pcf857x *gpio = i2c_get_clientdata(client); int status = 0; - if (pdata->teardown) { + if (pdata && pdata->teardown) { status = pdata->teardown(client, gpio->chip.base, gpio->chip.ngpio, pdata->context); diff --git a/drivers/gpio/stmpe-gpio.c b/drivers/gpio/stmpe-gpio.c new file mode 100644 index 00000000000..4e1f1b9d5e6 --- /dev/null +++ b/drivers/gpio/stmpe-gpio.c @@ -0,0 +1,399 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * License Terms: GNU General Public License, version 2 + * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/gpio.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/mfd/stmpe.h> + +/* + * These registers are modified under the irq bus lock and cached to avoid + * unnecessary writes in bus_sync_unlock. + */ +enum { REG_RE, REG_FE, REG_IE }; + +#define CACHE_NR_REGS 3 +#define CACHE_NR_BANKS (STMPE_NR_GPIOS / 8) + +struct stmpe_gpio { + struct gpio_chip chip; + struct stmpe *stmpe; + struct device *dev; + struct mutex irq_lock; + + int irq_base; + + /* Caches of interrupt control registers for bus_lock */ + u8 regs[CACHE_NR_REGS][CACHE_NR_BANKS]; + u8 oldregs[CACHE_NR_REGS][CACHE_NR_BANKS]; +}; + +static inline struct stmpe_gpio *to_stmpe_gpio(struct gpio_chip *chip) +{ + return container_of(chip, struct stmpe_gpio, chip); +} + +static int stmpe_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip); + struct stmpe *stmpe = stmpe_gpio->stmpe; + u8 reg = stmpe->regs[STMPE_IDX_GPMR_LSB] - (offset / 8); + u8 mask = 1 << (offset % 8); + int ret; + + ret = stmpe_reg_read(stmpe, reg); + if (ret < 0) + return ret; + + return ret & mask; +} + +static void stmpe_gpio_set(struct gpio_chip *chip, unsigned offset, int val) +{ + struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip); + struct stmpe *stmpe = stmpe_gpio->stmpe; + int which = val ? STMPE_IDX_GPSR_LSB : STMPE_IDX_GPCR_LSB; + u8 reg = stmpe->regs[which] - (offset / 8); + u8 mask = 1 << (offset % 8); + + stmpe_reg_write(stmpe, reg, mask); +} + +static int stmpe_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int val) +{ + struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip); + struct stmpe *stmpe = stmpe_gpio->stmpe; + u8 reg = stmpe->regs[STMPE_IDX_GPDR_LSB] - (offset / 8); + u8 mask = 1 << (offset % 8); + + stmpe_gpio_set(chip, offset, val); + + return stmpe_set_bits(stmpe, reg, mask, mask); +} + +static int stmpe_gpio_direction_input(struct gpio_chip *chip, + unsigned offset) +{ + struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip); + struct stmpe *stmpe = stmpe_gpio->stmpe; + u8 reg = stmpe->regs[STMPE_IDX_GPDR_LSB] - (offset / 8); + u8 mask = 1 << (offset % 8); + + return stmpe_set_bits(stmpe, reg, mask, 0); +} + +static int stmpe_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip); + + return stmpe_gpio->irq_base + offset; +} + +static int stmpe_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip); + struct stmpe *stmpe = stmpe_gpio->stmpe; + + return stmpe_set_altfunc(stmpe, 1 << offset, STMPE_BLOCK_GPIO); +} + +static struct gpio_chip template_chip = { + .label = "stmpe", + .owner = THIS_MODULE, + .direction_input = stmpe_gpio_direction_input, + .get = stmpe_gpio_get, + .direction_output = stmpe_gpio_direction_output, + .set = stmpe_gpio_set, + .to_irq = stmpe_gpio_to_irq, + .request = stmpe_gpio_request, + .can_sleep = 1, +}; + +static int stmpe_gpio_irq_set_type(unsigned int irq, unsigned int type) +{ + struct stmpe_gpio *stmpe_gpio = get_irq_chip_data(irq); + int offset = irq - stmpe_gpio->irq_base; + int regoffset = offset / 8; + int mask = 1 << (offset % 8); + + if (type == IRQ_TYPE_LEVEL_LOW || type == IRQ_TYPE_LEVEL_HIGH) + return -EINVAL; + + if (type == IRQ_TYPE_EDGE_RISING) + stmpe_gpio->regs[REG_RE][regoffset] |= mask; + else + stmpe_gpio->regs[REG_RE][regoffset] &= ~mask; + + if (type == IRQ_TYPE_EDGE_FALLING) + stmpe_gpio->regs[REG_FE][regoffset] |= mask; + else + stmpe_gpio->regs[REG_FE][regoffset] &= ~mask; + + return 0; +} + +static void stmpe_gpio_irq_lock(unsigned int irq) +{ + struct stmpe_gpio *stmpe_gpio = get_irq_chip_data(irq); + + mutex_lock(&stmpe_gpio->irq_lock); +} + +static void stmpe_gpio_irq_sync_unlock(unsigned int irq) +{ + struct stmpe_gpio *stmpe_gpio = get_irq_chip_data(irq); + struct stmpe *stmpe = stmpe_gpio->stmpe; + int num_banks = DIV_ROUND_UP(stmpe->num_gpios, 8); + static const u8 regmap[] = { + [REG_RE] = STMPE_IDX_GPRER_LSB, + [REG_FE] = STMPE_IDX_GPFER_LSB, + [REG_IE] = STMPE_IDX_IEGPIOR_LSB, + }; + int i, j; + + for (i = 0; i < CACHE_NR_REGS; i++) { + for (j = 0; j < num_banks; j++) { + u8 old = stmpe_gpio->oldregs[i][j]; + u8 new = stmpe_gpio->regs[i][j]; + + if (new == old) + continue; + + stmpe_gpio->oldregs[i][j] = new; + stmpe_reg_write(stmpe, stmpe->regs[regmap[i]] - j, new); + } + } + + mutex_unlock(&stmpe_gpio->irq_lock); +} + +static void stmpe_gpio_irq_mask(unsigned int irq) +{ + struct stmpe_gpio *stmpe_gpio = get_irq_chip_data(irq); + int offset = irq - stmpe_gpio->irq_base; + int regoffset = offset / 8; + int mask = 1 << (offset % 8); + + stmpe_gpio->regs[REG_IE][regoffset] &= ~mask; +} + +static void stmpe_gpio_irq_unmask(unsigned int irq) +{ + struct stmpe_gpio *stmpe_gpio = get_irq_chip_data(irq); + int offset = irq - stmpe_gpio->irq_base; + int regoffset = offset / 8; + int mask = 1 << (offset % 8); + + stmpe_gpio->regs[REG_IE][regoffset] |= mask; +} + +static struct irq_chip stmpe_gpio_irq_chip = { + .name = "stmpe-gpio", + .bus_lock = stmpe_gpio_irq_lock, + .bus_sync_unlock = stmpe_gpio_irq_sync_unlock, + .mask = stmpe_gpio_irq_mask, + .unmask = stmpe_gpio_irq_unmask, + .set_type = stmpe_gpio_irq_set_type, +}; + +static irqreturn_t stmpe_gpio_irq(int irq, void *dev) +{ + struct stmpe_gpio *stmpe_gpio = dev; + struct stmpe *stmpe = stmpe_gpio->stmpe; + u8 statmsbreg = stmpe->regs[STMPE_IDX_ISGPIOR_MSB]; + int num_banks = DIV_ROUND_UP(stmpe->num_gpios, 8); + u8 status[num_banks]; + int ret; + int i; + + ret = stmpe_block_read(stmpe, statmsbreg, num_banks, status); + if (ret < 0) + return IRQ_NONE; + + for (i = 0; i < num_banks; i++) { + int bank = num_banks - i - 1; + unsigned int enabled = stmpe_gpio->regs[REG_IE][bank]; + unsigned int stat = status[i]; + + stat &= enabled; + if (!stat) + continue; + + while (stat) { + int bit = __ffs(stat); + int line = bank * 8 + bit; + + handle_nested_irq(stmpe_gpio->irq_base + line); + stat &= ~(1 << bit); + } + + stmpe_reg_write(stmpe, statmsbreg + i, status[i]); + stmpe_reg_write(stmpe, stmpe->regs[STMPE_IDX_GPEDR_MSB] + i, + status[i]); + } + + return IRQ_HANDLED; +} + +static int __devinit stmpe_gpio_irq_init(struct stmpe_gpio *stmpe_gpio) +{ + int base = stmpe_gpio->irq_base; + int irq; + + for (irq = base; irq < base + stmpe_gpio->chip.ngpio; irq++) { + set_irq_chip_data(irq, stmpe_gpio); + set_irq_chip_and_handler(irq, &stmpe_gpio_irq_chip, + handle_simple_irq); + set_irq_nested_thread(irq, 1); +#ifdef CONFIG_ARM + set_irq_flags(irq, IRQF_VALID); +#else + set_irq_noprobe(irq); +#endif + } + + return 0; +} + +static void stmpe_gpio_irq_remove(struct stmpe_gpio *stmpe_gpio) +{ + int base = stmpe_gpio->irq_base; + int irq; + + for (irq = base; irq < base + stmpe_gpio->chip.ngpio; irq++) { +#ifdef CONFIG_ARM + set_irq_flags(irq, 0); +#endif + set_irq_chip_and_handler(irq, NULL, NULL); + set_irq_chip_data(irq, NULL); + } +} + +static int __devinit stmpe_gpio_probe(struct platform_device *pdev) +{ + struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent); + struct stmpe_gpio_platform_data *pdata; + struct stmpe_gpio *stmpe_gpio; + int ret; + int irq; + + pdata = stmpe->pdata->gpio; + if (!pdata) + return -ENODEV; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + stmpe_gpio = kzalloc(sizeof(struct stmpe_gpio), GFP_KERNEL); + if (!stmpe_gpio) + return -ENOMEM; + + mutex_init(&stmpe_gpio->irq_lock); + + stmpe_gpio->dev = &pdev->dev; + stmpe_gpio->stmpe = stmpe; + + stmpe_gpio->chip = template_chip; + stmpe_gpio->chip.ngpio = stmpe->num_gpios; + stmpe_gpio->chip.dev = &pdev->dev; + stmpe_gpio->chip.base = pdata ? pdata->gpio_base : -1; + + stmpe_gpio->irq_base = stmpe->irq_base + STMPE_INT_GPIO(0); + + ret = stmpe_enable(stmpe, STMPE_BLOCK_GPIO); + if (ret) + return ret; + + ret = stmpe_gpio_irq_init(stmpe_gpio); + if (ret) + goto out_free; + + ret = request_threaded_irq(irq, NULL, stmpe_gpio_irq, IRQF_ONESHOT, + "stmpe-gpio", stmpe_gpio); + if (ret) { + dev_err(&pdev->dev, "unable to get irq: %d\n", ret); + goto out_removeirq; + } + + ret = gpiochip_add(&stmpe_gpio->chip); + if (ret) { + dev_err(&pdev->dev, "unable to add gpiochip: %d\n", ret); + goto out_freeirq; + } + + if (pdata && pdata->setup) + pdata->setup(stmpe, stmpe_gpio->chip.base); + + platform_set_drvdata(pdev, stmpe_gpio); + + return 0; + +out_freeirq: + free_irq(irq, stmpe_gpio); +out_removeirq: + stmpe_gpio_irq_remove(stmpe_gpio); +out_free: + kfree(stmpe_gpio); + return ret; +} + +static int __devexit stmpe_gpio_remove(struct platform_device *pdev) +{ + struct stmpe_gpio *stmpe_gpio = platform_get_drvdata(pdev); + struct stmpe *stmpe = stmpe_gpio->stmpe; + struct stmpe_gpio_platform_data *pdata = stmpe->pdata->gpio; + int irq = platform_get_irq(pdev, 0); + int ret; + + if (pdata && pdata->remove) + pdata->remove(stmpe, stmpe_gpio->chip.base); + + ret = gpiochip_remove(&stmpe_gpio->chip); + if (ret < 0) { + dev_err(stmpe_gpio->dev, + "unable to remove gpiochip: %d\n", ret); + return ret; + } + + stmpe_disable(stmpe, STMPE_BLOCK_GPIO); + + free_irq(irq, stmpe_gpio); + stmpe_gpio_irq_remove(stmpe_gpio); + platform_set_drvdata(pdev, NULL); + kfree(stmpe_gpio); + + return 0; +} + +static struct platform_driver stmpe_gpio_driver = { + .driver.name = "stmpe-gpio", + .driver.owner = THIS_MODULE, + .probe = stmpe_gpio_probe, + .remove = __devexit_p(stmpe_gpio_remove), +}; + +static int __init stmpe_gpio_init(void) +{ + return platform_driver_register(&stmpe_gpio_driver); +} +subsys_initcall(stmpe_gpio_init); + +static void __exit stmpe_gpio_exit(void) +{ + platform_driver_unregister(&stmpe_gpio_driver); +} +module_exit(stmpe_gpio_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("STMPExxxx GPIO driver"); +MODULE_AUTHOR("Rabin Vincent <rabin.vincent@stericsson.com>"); diff --git a/drivers/gpio/sx150x.c b/drivers/gpio/sx150x.c new file mode 100644 index 00000000000..b42f42ca70c --- /dev/null +++ b/drivers/gpio/sx150x.c @@ -0,0 +1,645 @@ +/* Copyright (c) 2010, Code Aurora Forum. 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 version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/workqueue.h> +#include <linux/i2c/sx150x.h> + +struct sx150x_device_data { + u8 reg_pullup; + u8 reg_pulldn; + u8 reg_drain; + u8 reg_polarity; + u8 reg_dir; + u8 reg_data; + u8 reg_irq_mask; + u8 reg_irq_src; + u8 reg_sense; + u8 reg_clock; + u8 reg_misc; + u8 reg_reset; + u8 ngpios; +}; + +struct sx150x_chip { + struct gpio_chip gpio_chip; + struct i2c_client *client; + const struct sx150x_device_data *dev_cfg; + int irq_summary; + int irq_base; + u32 irq_sense; + unsigned long irq_set_type_pending; + struct irq_chip irq_chip; + struct mutex lock; +}; + +static const struct sx150x_device_data sx150x_devices[] = { + [0] = { /* sx1508q */ + .reg_pullup = 0x03, + .reg_pulldn = 0x04, + .reg_drain = 0x05, + .reg_polarity = 0x06, + .reg_dir = 0x07, + .reg_data = 0x08, + .reg_irq_mask = 0x09, + .reg_irq_src = 0x0c, + .reg_sense = 0x0b, + .reg_clock = 0x0f, + .reg_misc = 0x10, + .reg_reset = 0x7d, + .ngpios = 8 + }, + [1] = { /* sx1509q */ + .reg_pullup = 0x07, + .reg_pulldn = 0x09, + .reg_drain = 0x0b, + .reg_polarity = 0x0d, + .reg_dir = 0x0f, + .reg_data = 0x11, + .reg_irq_mask = 0x13, + .reg_irq_src = 0x19, + .reg_sense = 0x17, + .reg_clock = 0x1e, + .reg_misc = 0x1f, + .reg_reset = 0x7d, + .ngpios = 16 + }, +}; + +static const struct i2c_device_id sx150x_id[] = { + {"sx1508q", 0}, + {"sx1509q", 1}, + {} +}; +MODULE_DEVICE_TABLE(i2c, sx150x_id); + +static s32 sx150x_i2c_write(struct i2c_client *client, u8 reg, u8 val) +{ + s32 err = i2c_smbus_write_byte_data(client, reg, val); + + if (err < 0) + dev_warn(&client->dev, + "i2c write fail: can't write %02x to %02x: %d\n", + val, reg, err); + return err; +} + +static s32 sx150x_i2c_read(struct i2c_client *client, u8 reg, u8 *val) +{ + s32 err = i2c_smbus_read_byte_data(client, reg); + + if (err >= 0) + *val = err; + else + dev_warn(&client->dev, + "i2c read fail: can't read from %02x: %d\n", + reg, err); + return err; +} + +static inline bool offset_is_oscio(struct sx150x_chip *chip, unsigned offset) +{ + return (chip->dev_cfg->ngpios == offset); +} + +/* + * These utility functions solve the common problem of locating and setting + * configuration bits. Configuration bits are grouped into registers + * whose indexes increase downwards. For example, with eight-bit registers, + * sixteen gpios would have their config bits grouped in the following order: + * REGISTER N-1 [ f e d c b a 9 8 ] + * N [ 7 6 5 4 3 2 1 0 ] + * + * For multi-bit configurations, the pattern gets wider: + * REGISTER N-3 [ f f e e d d c c ] + * N-2 [ b b a a 9 9 8 8 ] + * N-1 [ 7 7 6 6 5 5 4 4 ] + * N [ 3 3 2 2 1 1 0 0 ] + * + * Given the address of the starting register 'N', the index of the gpio + * whose configuration we seek to change, and the width in bits of that + * configuration, these functions allow us to locate the correct + * register and mask the correct bits. + */ +static inline void sx150x_find_cfg(u8 offset, u8 width, + u8 *reg, u8 *mask, u8 *shift) +{ + *reg -= offset * width / 8; + *mask = (1 << width) - 1; + *shift = (offset * width) % 8; + *mask <<= *shift; +} + +static s32 sx150x_write_cfg(struct sx150x_chip *chip, + u8 offset, u8 width, u8 reg, u8 val) +{ + u8 mask; + u8 data; + u8 shift; + s32 err; + + sx150x_find_cfg(offset, width, ®, &mask, &shift); + err = sx150x_i2c_read(chip->client, reg, &data); + if (err < 0) + return err; + + data &= ~mask; + data |= (val << shift) & mask; + return sx150x_i2c_write(chip->client, reg, data); +} + +static int sx150x_get_io(struct sx150x_chip *chip, unsigned offset) +{ + u8 reg = chip->dev_cfg->reg_data; + u8 mask; + u8 data; + u8 shift; + s32 err; + + sx150x_find_cfg(offset, 1, ®, &mask, &shift); + err = sx150x_i2c_read(chip->client, reg, &data); + if (err >= 0) + err = (data & mask) != 0 ? 1 : 0; + + return err; +} + +static void sx150x_set_oscio(struct sx150x_chip *chip, int val) +{ + sx150x_i2c_write(chip->client, + chip->dev_cfg->reg_clock, + (val ? 0x1f : 0x10)); +} + +static void sx150x_set_io(struct sx150x_chip *chip, unsigned offset, int val) +{ + sx150x_write_cfg(chip, + offset, + 1, + chip->dev_cfg->reg_data, + (val ? 1 : 0)); +} + +static int sx150x_io_input(struct sx150x_chip *chip, unsigned offset) +{ + return sx150x_write_cfg(chip, + offset, + 1, + chip->dev_cfg->reg_dir, + 1); +} + +static int sx150x_io_output(struct sx150x_chip *chip, unsigned offset, int val) +{ + int err; + + err = sx150x_write_cfg(chip, + offset, + 1, + chip->dev_cfg->reg_data, + (val ? 1 : 0)); + if (err >= 0) + err = sx150x_write_cfg(chip, + offset, + 1, + chip->dev_cfg->reg_dir, + 0); + return err; +} + +static int sx150x_gpio_get(struct gpio_chip *gc, unsigned offset) +{ + struct sx150x_chip *chip; + int status = -EINVAL; + + chip = container_of(gc, struct sx150x_chip, gpio_chip); + + if (!offset_is_oscio(chip, offset)) { + mutex_lock(&chip->lock); + status = sx150x_get_io(chip, offset); + mutex_unlock(&chip->lock); + } + + return status; +} + +static void sx150x_gpio_set(struct gpio_chip *gc, unsigned offset, int val) +{ + struct sx150x_chip *chip; + + chip = container_of(gc, struct sx150x_chip, gpio_chip); + + mutex_lock(&chip->lock); + if (offset_is_oscio(chip, offset)) + sx150x_set_oscio(chip, val); + else + sx150x_set_io(chip, offset, val); + mutex_unlock(&chip->lock); +} + +static int sx150x_gpio_direction_input(struct gpio_chip *gc, unsigned offset) +{ + struct sx150x_chip *chip; + int status = -EINVAL; + + chip = container_of(gc, struct sx150x_chip, gpio_chip); + + if (!offset_is_oscio(chip, offset)) { + mutex_lock(&chip->lock); + status = sx150x_io_input(chip, offset); + mutex_unlock(&chip->lock); + } + return status; +} + +static int sx150x_gpio_direction_output(struct gpio_chip *gc, + unsigned offset, + int val) +{ + struct sx150x_chip *chip; + int status = 0; + + chip = container_of(gc, struct sx150x_chip, gpio_chip); + + if (!offset_is_oscio(chip, offset)) { + mutex_lock(&chip->lock); + status = sx150x_io_output(chip, offset, val); + mutex_unlock(&chip->lock); + } + return status; +} + +static int sx150x_gpio_to_irq(struct gpio_chip *gc, unsigned offset) +{ + struct sx150x_chip *chip; + + chip = container_of(gc, struct sx150x_chip, gpio_chip); + + if (offset >= chip->dev_cfg->ngpios) + return -EINVAL; + + if (chip->irq_base < 0) + return -EINVAL; + + return chip->irq_base + offset; +} + +static void sx150x_irq_mask(unsigned int irq) +{ + struct irq_chip *ic = get_irq_chip(irq); + struct sx150x_chip *chip; + unsigned n; + + chip = container_of(ic, struct sx150x_chip, irq_chip); + n = irq - chip->irq_base; + + sx150x_write_cfg(chip, n, 1, chip->dev_cfg->reg_irq_mask, 1); + sx150x_write_cfg(chip, n, 2, chip->dev_cfg->reg_sense, 0); +} + +static void sx150x_irq_unmask(unsigned int irq) +{ + struct irq_chip *ic = get_irq_chip(irq); + struct sx150x_chip *chip; + unsigned n; + + chip = container_of(ic, struct sx150x_chip, irq_chip); + n = irq - chip->irq_base; + + sx150x_write_cfg(chip, n, 1, chip->dev_cfg->reg_irq_mask, 0); + sx150x_write_cfg(chip, n, 2, chip->dev_cfg->reg_sense, + chip->irq_sense >> (n * 2)); +} + +static int sx150x_irq_set_type(unsigned int irq, unsigned int flow_type) +{ + struct irq_chip *ic = get_irq_chip(irq); + struct sx150x_chip *chip; + unsigned n, val = 0; + + if (flow_type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) + return -EINVAL; + + chip = container_of(ic, struct sx150x_chip, irq_chip); + n = irq - chip->irq_base; + + if (flow_type & IRQ_TYPE_EDGE_RISING) + val |= 0x1; + if (flow_type & IRQ_TYPE_EDGE_FALLING) + val |= 0x2; + + chip->irq_sense &= ~(3UL << (n * 2)); + chip->irq_sense |= val << (n * 2); + chip->irq_set_type_pending |= BIT(n); + return 0; +} + +static irqreturn_t sx150x_irq_thread_fn(int irq, void *dev_id) +{ + struct sx150x_chip *chip = (struct sx150x_chip *)dev_id; + unsigned nhandled = 0; + unsigned sub_irq; + unsigned n; + s32 err; + u8 val; + int i; + + for (i = (chip->dev_cfg->ngpios / 8) - 1; i >= 0; --i) { + err = sx150x_i2c_read(chip->client, + chip->dev_cfg->reg_irq_src - i, + &val); + if (err < 0) + continue; + + sx150x_i2c_write(chip->client, + chip->dev_cfg->reg_irq_src - i, + val); + for (n = 0; n < 8; ++n) { + if (val & (1 << n)) { + sub_irq = chip->irq_base + (i * 8) + n; + handle_nested_irq(sub_irq); + ++nhandled; + } + } + } + + return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE); +} + +static void sx150x_irq_bus_lock(unsigned int irq) +{ + struct irq_chip *ic = get_irq_chip(irq); + struct sx150x_chip *chip; + + chip = container_of(ic, struct sx150x_chip, irq_chip); + + mutex_lock(&chip->lock); +} + +static void sx150x_irq_bus_sync_unlock(unsigned int irq) +{ + struct irq_chip *ic = get_irq_chip(irq); + struct sx150x_chip *chip; + unsigned n; + + chip = container_of(ic, struct sx150x_chip, irq_chip); + + while (chip->irq_set_type_pending) { + n = __ffs(chip->irq_set_type_pending); + chip->irq_set_type_pending &= ~BIT(n); + if (!(irq_to_desc(n + chip->irq_base)->status & IRQ_MASKED)) + sx150x_write_cfg(chip, n, 2, + chip->dev_cfg->reg_sense, + chip->irq_sense >> (n * 2)); + } + + mutex_unlock(&chip->lock); +} + +static void sx150x_init_chip(struct sx150x_chip *chip, + struct i2c_client *client, + kernel_ulong_t driver_data, + struct sx150x_platform_data *pdata) +{ + mutex_init(&chip->lock); + + chip->client = client; + chip->dev_cfg = &sx150x_devices[driver_data]; + chip->gpio_chip.label = client->name; + chip->gpio_chip.direction_input = sx150x_gpio_direction_input; + chip->gpio_chip.direction_output = sx150x_gpio_direction_output; + chip->gpio_chip.get = sx150x_gpio_get; + chip->gpio_chip.set = sx150x_gpio_set; + chip->gpio_chip.to_irq = sx150x_gpio_to_irq; + chip->gpio_chip.base = pdata->gpio_base; + chip->gpio_chip.can_sleep = 1; + chip->gpio_chip.ngpio = chip->dev_cfg->ngpios; + if (pdata->oscio_is_gpo) + ++chip->gpio_chip.ngpio; + + chip->irq_chip.name = client->name; + chip->irq_chip.mask = sx150x_irq_mask; + chip->irq_chip.unmask = sx150x_irq_unmask; + chip->irq_chip.set_type = sx150x_irq_set_type; + chip->irq_chip.bus_lock = sx150x_irq_bus_lock; + chip->irq_chip.bus_sync_unlock = sx150x_irq_bus_sync_unlock; + chip->irq_summary = -1; + chip->irq_base = -1; + chip->irq_sense = 0; + chip->irq_set_type_pending = 0; +} + +static int sx150x_init_io(struct sx150x_chip *chip, u8 base, u16 cfg) +{ + int err = 0; + unsigned n; + + for (n = 0; err >= 0 && n < (chip->dev_cfg->ngpios / 8); ++n) + err = sx150x_i2c_write(chip->client, base - n, cfg >> (n * 8)); + return err; +} + +static int sx150x_init_hw(struct sx150x_chip *chip, + struct sx150x_platform_data *pdata) +{ + int err = 0; + + err = i2c_smbus_write_word_data(chip->client, + chip->dev_cfg->reg_reset, + 0x3412); + if (err < 0) + return err; + + err = sx150x_i2c_write(chip->client, + chip->dev_cfg->reg_misc, + 0x01); + if (err < 0) + return err; + + err = sx150x_init_io(chip, chip->dev_cfg->reg_pullup, + pdata->io_pullup_ena); + if (err < 0) + return err; + + err = sx150x_init_io(chip, chip->dev_cfg->reg_pulldn, + pdata->io_pulldn_ena); + if (err < 0) + return err; + + err = sx150x_init_io(chip, chip->dev_cfg->reg_drain, + pdata->io_open_drain_ena); + if (err < 0) + return err; + + err = sx150x_init_io(chip, chip->dev_cfg->reg_polarity, + pdata->io_polarity); + if (err < 0) + return err; + + if (pdata->oscio_is_gpo) + sx150x_set_oscio(chip, 0); + + return err; +} + +static int sx150x_install_irq_chip(struct sx150x_chip *chip, + int irq_summary, + int irq_base) +{ + int err; + unsigned n; + unsigned irq; + + chip->irq_summary = irq_summary; + chip->irq_base = irq_base; + + for (n = 0; n < chip->dev_cfg->ngpios; ++n) { + irq = irq_base + n; + set_irq_chip_and_handler(irq, &chip->irq_chip, handle_edge_irq); + set_irq_nested_thread(irq, 1); +#ifdef CONFIG_ARM + set_irq_flags(irq, IRQF_VALID); +#else + set_irq_noprobe(irq); +#endif + } + + err = request_threaded_irq(irq_summary, + NULL, + sx150x_irq_thread_fn, + IRQF_SHARED | IRQF_TRIGGER_FALLING, + chip->irq_chip.name, + chip); + if (err < 0) { + chip->irq_summary = -1; + chip->irq_base = -1; + } + + return err; +} + +static void sx150x_remove_irq_chip(struct sx150x_chip *chip) +{ + unsigned n; + unsigned irq; + + free_irq(chip->irq_summary, chip); + + for (n = 0; n < chip->dev_cfg->ngpios; ++n) { + irq = chip->irq_base + n; + set_irq_handler(irq, NULL); + set_irq_chip(irq, NULL); + } +} + +static int __devinit sx150x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + static const u32 i2c_funcs = I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WRITE_WORD_DATA; + struct sx150x_platform_data *pdata; + struct sx150x_chip *chip; + int rc; + + pdata = client->dev.platform_data; + if (!pdata) + return -EINVAL; + + if (!i2c_check_functionality(client->adapter, i2c_funcs)) + return -ENOSYS; + + chip = kzalloc(sizeof(struct sx150x_chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + sx150x_init_chip(chip, client, id->driver_data, pdata); + rc = sx150x_init_hw(chip, pdata); + if (rc < 0) + goto probe_fail_pre_gpiochip_add; + + rc = gpiochip_add(&chip->gpio_chip); + if (rc < 0) + goto probe_fail_pre_gpiochip_add; + + if (pdata->irq_summary >= 0) { + rc = sx150x_install_irq_chip(chip, + pdata->irq_summary, + pdata->irq_base); + if (rc < 0) + goto probe_fail_post_gpiochip_add; + } + + i2c_set_clientdata(client, chip); + + return 0; +probe_fail_post_gpiochip_add: + WARN_ON(gpiochip_remove(&chip->gpio_chip) < 0); +probe_fail_pre_gpiochip_add: + kfree(chip); + return rc; +} + +static int __devexit sx150x_remove(struct i2c_client *client) +{ + struct sx150x_chip *chip; + int rc; + + chip = i2c_get_clientdata(client); + rc = gpiochip_remove(&chip->gpio_chip); + if (rc < 0) + return rc; + + if (chip->irq_summary >= 0) + sx150x_remove_irq_chip(chip); + + kfree(chip); + + return 0; +} + +static struct i2c_driver sx150x_driver = { + .driver = { + .name = "sx150x", + .owner = THIS_MODULE + }, + .probe = sx150x_probe, + .remove = __devexit_p(sx150x_remove), + .id_table = sx150x_id, +}; + +static int __init sx150x_init(void) +{ + return i2c_add_driver(&sx150x_driver); +} +subsys_initcall(sx150x_init); + +static void __exit sx150x_exit(void) +{ + return i2c_del_driver(&sx150x_driver); +} +module_exit(sx150x_exit); + +MODULE_AUTHOR("Gregory Bean <gbean@codeaurora.org>"); +MODULE_DESCRIPTION("Driver for Semtech SX150X I2C GPIO Expanders"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("i2c:sx150x"); diff --git a/drivers/gpio/wm831x-gpio.c b/drivers/gpio/wm831x-gpio.c index 1fa449a1a4c..309644cf4d9 100644 --- a/drivers/gpio/wm831x-gpio.c +++ b/drivers/gpio/wm831x-gpio.c @@ -108,6 +108,37 @@ static int wm831x_gpio_to_irq(struct gpio_chip *chip, unsigned offset) return wm831x->irq_base + WM831X_IRQ_GPIO_1 + offset; } +static int wm831x_gpio_set_debounce(struct gpio_chip *chip, unsigned offset, + unsigned debounce) +{ + struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip); + struct wm831x *wm831x = wm831x_gpio->wm831x; + int reg = WM831X_GPIO1_CONTROL + offset; + int ret, fn; + + ret = wm831x_reg_read(wm831x, reg); + if (ret < 0) + return ret; + + switch (ret & WM831X_GPN_FN_MASK) { + case 0: + case 1: + break; + default: + /* Not in GPIO mode */ + return -EBUSY; + } + + if (debounce >= 32 && debounce <= 64) + fn = 0; + else if (debounce >= 4000 && debounce <= 8000) + fn = 1; + else + return -EINVAL; + + return wm831x_set_bits(wm831x, reg, WM831X_GPN_FN_MASK, fn); +} + #ifdef CONFIG_DEBUG_FS static void wm831x_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) { @@ -208,6 +239,7 @@ static struct gpio_chip template_chip = { .direction_output = wm831x_gpio_direction_out, .set = wm831x_gpio_set, .to_irq = wm831x_gpio_to_irq, + .set_debounce = wm831x_gpio_set_debounce, .dbg_show = wm831x_gpio_dbg_show, .can_sleep = 1, }; diff --git a/drivers/gpu/drm/ati_pcigart.c b/drivers/gpu/drm/ati_pcigart.c index 17be051b7aa..1c364924220 100644 --- a/drivers/gpu/drm/ati_pcigart.c +++ b/drivers/gpu/drm/ati_pcigart.c @@ -152,7 +152,7 @@ int drm_ati_pcigart_init(struct drm_device *dev, struct drm_ati_pcigart_info *ga /* we need to support large memory configurations */ entry->busaddr[i] = pci_map_page(dev->pdev, entry->pagelist[i], 0, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); - if (entry->busaddr[i] == 0) { + if (pci_dma_mapping_error(dev->pdev, entry->busaddr[i])) { DRM_ERROR("unable to map PCIGART pages!\n"); drm_ati_pcigart_cleanup(dev, gart_info); address = NULL; diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c index a5c9ce93bbc..3e257a50bf5 100644 --- a/drivers/gpu/drm/drm_bufs.c +++ b/drivers/gpu/drm/drm_bufs.c @@ -328,14 +328,13 @@ static int drm_addmap_core(struct drm_device * dev, resource_size_t offset, return -EINVAL; } - list = kmalloc(sizeof(*list), GFP_KERNEL); + list = kzalloc(sizeof(*list), GFP_KERNEL); if (!list) { if (map->type == _DRM_REGISTERS) iounmap(map->handle); kfree(map); return -EINVAL; } - memset(list, 0, sizeof(*list)); list->map = map; mutex_lock(&dev->struct_mutex); @@ -678,13 +677,12 @@ int drm_addbufs_agp(struct drm_device * dev, struct drm_buf_desc * request) return -EINVAL; } - entry->buflist = kmalloc(count * sizeof(*entry->buflist), GFP_KERNEL); + entry->buflist = kzalloc(count * sizeof(*entry->buflist), GFP_KERNEL); if (!entry->buflist) { mutex_unlock(&dev->struct_mutex); atomic_dec(&dev->buf_alloc); return -ENOMEM; } - memset(entry->buflist, 0, count * sizeof(*entry->buflist)); entry->buf_size = size; entry->page_order = page_order; @@ -708,7 +706,7 @@ int drm_addbufs_agp(struct drm_device * dev, struct drm_buf_desc * request) buf->file_priv = NULL; buf->dev_priv_size = dev->driver->dev_priv_size; - buf->dev_private = kmalloc(buf->dev_priv_size, GFP_KERNEL); + buf->dev_private = kzalloc(buf->dev_priv_size, GFP_KERNEL); if (!buf->dev_private) { /* Set count correctly so we free the proper amount. */ entry->buf_count = count; @@ -717,7 +715,6 @@ int drm_addbufs_agp(struct drm_device * dev, struct drm_buf_desc * request) atomic_dec(&dev->buf_alloc); return -ENOMEM; } - memset(buf->dev_private, 0, buf->dev_priv_size); DRM_DEBUG("buffer %d @ %p\n", entry->buf_count, buf->address); @@ -832,22 +829,20 @@ int drm_addbufs_pci(struct drm_device * dev, struct drm_buf_desc * request) return -EINVAL; } - entry->buflist = kmalloc(count * sizeof(*entry->buflist), GFP_KERNEL); + entry->buflist = kzalloc(count * sizeof(*entry->buflist), GFP_KERNEL); if (!entry->buflist) { mutex_unlock(&dev->struct_mutex); atomic_dec(&dev->buf_alloc); return -ENOMEM; } - memset(entry->buflist, 0, count * sizeof(*entry->buflist)); - entry->seglist = kmalloc(count * sizeof(*entry->seglist), GFP_KERNEL); + entry->seglist = kzalloc(count * sizeof(*entry->seglist), GFP_KERNEL); if (!entry->seglist) { kfree(entry->buflist); mutex_unlock(&dev->struct_mutex); atomic_dec(&dev->buf_alloc); return -ENOMEM; } - memset(entry->seglist, 0, count * sizeof(*entry->seglist)); /* Keep the original pagelist until we know all the allocations * have succeeded @@ -911,8 +906,8 @@ int drm_addbufs_pci(struct drm_device * dev, struct drm_buf_desc * request) buf->file_priv = NULL; buf->dev_priv_size = dev->driver->dev_priv_size; - buf->dev_private = kmalloc(buf->dev_priv_size, - GFP_KERNEL); + buf->dev_private = kzalloc(buf->dev_priv_size, + GFP_KERNEL); if (!buf->dev_private) { /* Set count correctly so we free the proper amount. */ entry->buf_count = count; @@ -923,7 +918,6 @@ int drm_addbufs_pci(struct drm_device * dev, struct drm_buf_desc * request) atomic_dec(&dev->buf_alloc); return -ENOMEM; } - memset(buf->dev_private, 0, buf->dev_priv_size); DRM_DEBUG("buffer %d @ %p\n", entry->buf_count, buf->address); @@ -1048,14 +1042,13 @@ static int drm_addbufs_sg(struct drm_device * dev, struct drm_buf_desc * request return -EINVAL; } - entry->buflist = kmalloc(count * sizeof(*entry->buflist), + entry->buflist = kzalloc(count * sizeof(*entry->buflist), GFP_KERNEL); if (!entry->buflist) { mutex_unlock(&dev->struct_mutex); atomic_dec(&dev->buf_alloc); return -ENOMEM; } - memset(entry->buflist, 0, count * sizeof(*entry->buflist)); entry->buf_size = size; entry->page_order = page_order; @@ -1080,7 +1073,7 @@ static int drm_addbufs_sg(struct drm_device * dev, struct drm_buf_desc * request buf->file_priv = NULL; buf->dev_priv_size = dev->driver->dev_priv_size; - buf->dev_private = kmalloc(buf->dev_priv_size, GFP_KERNEL); + buf->dev_private = kzalloc(buf->dev_priv_size, GFP_KERNEL); if (!buf->dev_private) { /* Set count correctly so we free the proper amount. */ entry->buf_count = count; @@ -1090,8 +1083,6 @@ static int drm_addbufs_sg(struct drm_device * dev, struct drm_buf_desc * request return -ENOMEM; } - memset(buf->dev_private, 0, buf->dev_priv_size); - DRM_DEBUG("buffer %d @ %p\n", entry->buf_count, buf->address); offset += alignment; @@ -1209,14 +1200,13 @@ static int drm_addbufs_fb(struct drm_device * dev, struct drm_buf_desc * request return -EINVAL; } - entry->buflist = kmalloc(count * sizeof(*entry->buflist), + entry->buflist = kzalloc(count * sizeof(*entry->buflist), GFP_KERNEL); if (!entry->buflist) { mutex_unlock(&dev->struct_mutex); atomic_dec(&dev->buf_alloc); return -ENOMEM; } - memset(entry->buflist, 0, count * sizeof(*entry->buflist)); entry->buf_size = size; entry->page_order = page_order; @@ -1240,7 +1230,7 @@ static int drm_addbufs_fb(struct drm_device * dev, struct drm_buf_desc * request buf->file_priv = NULL; buf->dev_priv_size = dev->driver->dev_priv_size; - buf->dev_private = kmalloc(buf->dev_priv_size, GFP_KERNEL); + buf->dev_private = kzalloc(buf->dev_priv_size, GFP_KERNEL); if (!buf->dev_private) { /* Set count correctly so we free the proper amount. */ entry->buf_count = count; @@ -1249,7 +1239,6 @@ static int drm_addbufs_fb(struct drm_device * dev, struct drm_buf_desc * request atomic_dec(&dev->buf_alloc); return -ENOMEM; } - memset(buf->dev_private, 0, buf->dev_priv_size); DRM_DEBUG("buffer %d @ %p\n", entry->buf_count, buf->address); diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 4c68f76993d..37e0b4fa482 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -1682,9 +1682,9 @@ int drm_mode_addfb(struct drm_device *dev, /* TODO setup destructor callback */ fb = dev->mode_config.funcs->fb_create(dev, file_priv, r); - if (!fb) { + if (IS_ERR(fb)) { DRM_ERROR("could not create framebuffer\n"); - ret = -EINVAL; + ret = PTR_ERR(fb); goto out; } @@ -2541,7 +2541,7 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev, goto out; } - crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, crtc->gamma_size); + crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size); out: mutex_unlock(&dev->mode_config.mutex); diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index b9e4dbfa053..7e31d434834 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -817,12 +817,12 @@ int drm_helper_resume_force_mode(struct drm_device *dev) if (encoder_funcs->dpms) (*encoder_funcs->dpms) (encoder, drm_helper_choose_encoder_dpms(encoder)); - - crtc_funcs = crtc->helper_private; - if (crtc_funcs->dpms) - (*crtc_funcs->dpms) (crtc, - drm_helper_choose_crtc_dpms(crtc)); } + + crtc_funcs = crtc->helper_private; + if (crtc_funcs->dpms) + (*crtc_funcs->dpms) (crtc, + drm_helper_choose_crtc_dpms(crtc)); } } /* disable the unused connectors while restoring the modesetting */ diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index dce5c4a97f8..96e96310822 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -33,6 +33,11 @@ #include <linux/i2c-algo-bit.h> #include "drmP.h" #include "drm_edid.h" +#include "drm_edid_modes.h" + +#define version_greater(edid, maj, min) \ + (((edid)->version > (maj)) || \ + ((edid)->version == (maj) && (edid)->revision > (min))) #define EDID_EST_TIMINGS 16 #define EDID_STD_TIMINGS 8 @@ -62,6 +67,13 @@ /* use +hsync +vsync for detailed mode */ #define EDID_QUIRK_DETAILED_SYNC_PP (1 << 6) +struct detailed_mode_closure { + struct drm_connector *connector; + struct edid *edid; + bool preferred; + u32 quirks; + int modes; +}; #define LEVEL_DMT 0 #define LEVEL_GTF 1 @@ -375,7 +387,6 @@ static u32 edid_get_quirks(struct edid *edid) #define MODE_SIZE(m) ((m)->hdisplay * (m)->vdisplay) #define MODE_REFRESH_DIFF(m,r) (abs((m)->vrefresh - target_refresh)) - /** * edid_fixup_preferred - set preferred modes based on quirk list * @connector: has mode list to fix up @@ -422,245 +433,6 @@ static void edid_fixup_preferred(struct drm_connector *connector, preferred_mode->type |= DRM_MODE_TYPE_PREFERRED; } -/* - * Add the Autogenerated from the DMT spec. - * This table is copied from xfree86/modes/xf86EdidModes.c. - * But the mode with Reduced blank feature is deleted. - */ -static struct drm_display_mode drm_dmt_modes[] = { - /* 640x350@85Hz */ - { DRM_MODE("640x350", DRM_MODE_TYPE_DRIVER, 31500, 640, 672, - 736, 832, 0, 350, 382, 385, 445, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 640x400@85Hz */ - { DRM_MODE("640x400", DRM_MODE_TYPE_DRIVER, 31500, 640, 672, - 736, 832, 0, 400, 401, 404, 445, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 720x400@85Hz */ - { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 35500, 720, 756, - 828, 936, 0, 400, 401, 404, 446, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 640x480@60Hz */ - { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656, - 752, 800, 0, 480, 489, 492, 525, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 640x480@72Hz */ - { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 664, - 704, 832, 0, 480, 489, 492, 520, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 640x480@75Hz */ - { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 656, - 720, 840, 0, 480, 481, 484, 500, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 640x480@85Hz */ - { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 36000, 640, 696, - 752, 832, 0, 480, 481, 484, 509, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 800x600@56Hz */ - { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 36000, 800, 824, - 896, 1024, 0, 600, 601, 603, 625, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 800x600@60Hz */ - { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840, - 968, 1056, 0, 600, 601, 605, 628, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 800x600@72Hz */ - { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 50000, 800, 856, - 976, 1040, 0, 600, 637, 643, 666, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 800x600@75Hz */ - { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 49500, 800, 816, - 896, 1056, 0, 600, 601, 604, 625, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 800x600@85Hz */ - { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 56250, 800, 832, - 896, 1048, 0, 600, 601, 604, 631, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 848x480@60Hz */ - { DRM_MODE("848x480", DRM_MODE_TYPE_DRIVER, 33750, 848, 864, - 976, 1088, 0, 480, 486, 494, 517, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1024x768@43Hz, interlace */ - { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 44900, 1024, 1032, - 1208, 1264, 0, 768, 768, 772, 817, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | - DRM_MODE_FLAG_INTERLACE) }, - /* 1024x768@60Hz */ - { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048, - 1184, 1344, 0, 768, 771, 777, 806, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 1024x768@70Hz */ - { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 75000, 1024, 1048, - 1184, 1328, 0, 768, 771, 777, 806, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 1024x768@75Hz */ - { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 78750, 1024, 1040, - 1136, 1312, 0, 768, 769, 772, 800, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1024x768@85Hz */ - { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 94500, 1024, 1072, - 1168, 1376, 0, 768, 769, 772, 808, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1152x864@75Hz */ - { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216, - 1344, 1600, 0, 864, 865, 868, 900, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1280x768@60Hz */ - { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 79500, 1280, 1344, - 1472, 1664, 0, 768, 771, 778, 798, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1280x768@75Hz */ - { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 102250, 1280, 1360, - 1488, 1696, 0, 768, 771, 778, 805, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 1280x768@85Hz */ - { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 117500, 1280, 1360, - 1496, 1712, 0, 768, 771, 778, 809, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1280x800@60Hz */ - { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 83500, 1280, 1352, - 1480, 1680, 0, 800, 803, 809, 831, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 1280x800@75Hz */ - { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 106500, 1280, 1360, - 1488, 1696, 0, 800, 803, 809, 838, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1280x800@85Hz */ - { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 122500, 1280, 1360, - 1496, 1712, 0, 800, 803, 809, 843, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1280x960@60Hz */ - { DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1376, - 1488, 1800, 0, 960, 961, 964, 1000, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1280x960@85Hz */ - { DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1344, - 1504, 1728, 0, 960, 961, 964, 1011, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1280x1024@60Hz */ - { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1328, - 1440, 1688, 0, 1024, 1025, 1028, 1066, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1280x1024@75Hz */ - { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 135000, 1280, 1296, - 1440, 1688, 0, 1024, 1025, 1028, 1066, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1280x1024@85Hz */ - { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 157500, 1280, 1344, - 1504, 1728, 0, 1024, 1025, 1028, 1072, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1360x768@60Hz */ - { DRM_MODE("1360x768", DRM_MODE_TYPE_DRIVER, 85500, 1360, 1424, - 1536, 1792, 0, 768, 771, 777, 795, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1440x1050@60Hz */ - { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 121750, 1400, 1488, - 1632, 1864, 0, 1050, 1053, 1057, 1089, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1440x1050@75Hz */ - { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 156000, 1400, 1504, - 1648, 1896, 0, 1050, 1053, 1057, 1099, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1440x1050@85Hz */ - { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 179500, 1400, 1504, - 1656, 1912, 0, 1050, 1053, 1057, 1105, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1440x900@60Hz */ - { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 106500, 1440, 1520, - 1672, 1904, 0, 900, 903, 909, 934, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1440x900@75Hz */ - { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 136750, 1440, 1536, - 1688, 1936, 0, 900, 903, 909, 942, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1440x900@85Hz */ - { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 157000, 1440, 1544, - 1696, 1952, 0, 900, 903, 909, 948, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1600x1200@60Hz */ - { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 162000, 1600, 1664, - 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1600x1200@65Hz */ - { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 175500, 1600, 1664, - 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1600x1200@70Hz */ - { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 189000, 1600, 1664, - 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1600x1200@75Hz */ - { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 202500, 1600, 1664, - 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1600x1200@85Hz */ - { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 229500, 1600, 1664, - 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1680x1050@60Hz */ - { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 146250, 1680, 1784, - 1960, 2240, 0, 1050, 1053, 1059, 1089, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1680x1050@75Hz */ - { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 187000, 1680, 1800, - 1976, 2272, 0, 1050, 1053, 1059, 1099, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1680x1050@85Hz */ - { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 214750, 1680, 1808, - 1984, 2288, 0, 1050, 1053, 1059, 1105, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1792x1344@60Hz */ - { DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 204750, 1792, 1920, - 2120, 2448, 0, 1344, 1345, 1348, 1394, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1729x1344@75Hz */ - { DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 261000, 1792, 1888, - 2104, 2456, 0, 1344, 1345, 1348, 1417, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1853x1392@60Hz */ - { DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 218250, 1856, 1952, - 2176, 2528, 0, 1392, 1393, 1396, 1439, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1856x1392@75Hz */ - { DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 288000, 1856, 1984, - 2208, 2560, 0, 1392, 1395, 1399, 1500, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1920x1200@60Hz */ - { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 193250, 1920, 2056, - 2256, 2592, 0, 1200, 1203, 1209, 1245, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1920x1200@75Hz */ - { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 245250, 1920, 2056, - 2264, 2608, 0, 1200, 1203, 1209, 1255, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1920x1200@85Hz */ - { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 281250, 1920, 2064, - 2272, 2624, 0, 1200, 1203, 1209, 1262, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1920x1440@60Hz */ - { DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 234000, 1920, 2048, - 2256, 2600, 0, 1440, 1441, 1444, 1500, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1920x1440@75Hz */ - { DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2064, - 2288, 2640, 0, 1440, 1441, 1444, 1500, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 2560x1600@60Hz */ - { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 348500, 2560, 2752, - 3032, 3504, 0, 1600, 1603, 1609, 1658, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 2560x1600@75HZ */ - { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 443250, 2560, 2768, - 3048, 3536, 0, 1600, 1603, 1609, 1672, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 2560x1600@85HZ */ - { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 505250, 2560, 2768, - 3048, 3536, 0, 1600, 1603, 1609, 1682, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, -}; -static const int drm_num_dmt_modes = - sizeof(drm_dmt_modes) / sizeof(struct drm_display_mode); - struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev, int hsize, int vsize, int fresh) { @@ -685,6 +457,46 @@ EXPORT_SYMBOL(drm_mode_find_dmt); typedef void detailed_cb(struct detailed_timing *timing, void *closure); static void +cea_for_each_detailed_block(u8 *ext, detailed_cb *cb, void *closure) +{ + int i, n = 0; + u8 rev = ext[0x01], d = ext[0x02]; + u8 *det_base = ext + d; + + switch (rev) { + case 0: + /* can't happen */ + return; + case 1: + /* have to infer how many blocks we have, check pixel clock */ + for (i = 0; i < 6; i++) + if (det_base[18*i] || det_base[18*i+1]) + n++; + break; + default: + /* explicit count */ + n = min(ext[0x03] & 0x0f, 6); + break; + } + + for (i = 0; i < n; i++) + cb((struct detailed_timing *)(det_base + 18 * i), closure); +} + +static void +vtb_for_each_detailed_block(u8 *ext, detailed_cb *cb, void *closure) +{ + unsigned int i, n = min((int)ext[0x02], 6); + u8 *det_base = ext + 5; + + if (ext[0x01] != 1) + return; /* unknown version */ + + for (i = 0; i < n; i++) + cb((struct detailed_timing *)(det_base + 18 * i), closure); +} + +static void drm_for_each_detailed_block(u8 *raw_edid, detailed_cb *cb, void *closure) { int i; @@ -696,7 +508,19 @@ drm_for_each_detailed_block(u8 *raw_edid, detailed_cb *cb, void *closure) for (i = 0; i < EDID_DETAILED_TIMINGS; i++) cb(&(edid->detailed_timings[i]), closure); - /* XXX extension block walk */ + for (i = 1; i <= raw_edid[0x7e]; i++) { + u8 *ext = raw_edid + (i * EDID_LENGTH); + switch (*ext) { + case CEA_EXT: + cea_for_each_detailed_block(ext, cb, closure); + break; + case VTB_EXT: + vtb_for_each_detailed_block(ext, cb, closure); + break; + default: + break; + } + } } static void @@ -1047,117 +871,6 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev, return mode; } -/* - * Detailed mode info for the EDID "established modes" data to use. - */ -static struct drm_display_mode edid_est_modes[] = { - { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840, - 968, 1056, 0, 600, 601, 605, 628, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@60Hz */ - { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 36000, 800, 824, - 896, 1024, 0, 600, 601, 603, 625, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@56Hz */ - { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 656, - 720, 840, 0, 480, 481, 484, 500, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@75Hz */ - { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 664, - 704, 832, 0, 480, 489, 491, 520, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@72Hz */ - { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 30240, 640, 704, - 768, 864, 0, 480, 483, 486, 525, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@67Hz */ - { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25200, 640, 656, - 752, 800, 0, 480, 490, 492, 525, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@60Hz */ - { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 35500, 720, 738, - 846, 900, 0, 400, 421, 423, 449, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 720x400@88Hz */ - { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 28320, 720, 738, - 846, 900, 0, 400, 412, 414, 449, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 720x400@70Hz */ - { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 135000, 1280, 1296, - 1440, 1688, 0, 1024, 1025, 1028, 1066, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1280x1024@75Hz */ - { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 78800, 1024, 1040, - 1136, 1312, 0, 768, 769, 772, 800, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1024x768@75Hz */ - { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 75000, 1024, 1048, - 1184, 1328, 0, 768, 771, 777, 806, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@70Hz */ - { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048, - 1184, 1344, 0, 768, 771, 777, 806, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@60Hz */ - { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER,44900, 1024, 1032, - 1208, 1264, 0, 768, 768, 776, 817, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_INTERLACE) }, /* 1024x768@43Hz */ - { DRM_MODE("832x624", DRM_MODE_TYPE_DRIVER, 57284, 832, 864, - 928, 1152, 0, 624, 625, 628, 667, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 832x624@75Hz */ - { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 49500, 800, 816, - 896, 1056, 0, 600, 601, 604, 625, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@75Hz */ - { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 50000, 800, 856, - 976, 1040, 0, 600, 637, 643, 666, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@72Hz */ - { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216, - 1344, 1600, 0, 864, 865, 868, 900, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1152x864@75Hz */ -}; - -/** - * add_established_modes - get est. modes from EDID and add them - * @edid: EDID block to scan - * - * Each EDID block contains a bitmap of the supported "established modes" list - * (defined above). Tease them out and add them to the global modes list. - */ -static int add_established_modes(struct drm_connector *connector, struct edid *edid) -{ - struct drm_device *dev = connector->dev; - unsigned long est_bits = edid->established_timings.t1 | - (edid->established_timings.t2 << 8) | - ((edid->established_timings.mfg_rsvd & 0x80) << 9); - int i, modes = 0; - - for (i = 0; i <= EDID_EST_TIMINGS; i++) - if (est_bits & (1<<i)) { - struct drm_display_mode *newmode; - newmode = drm_mode_duplicate(dev, &edid_est_modes[i]); - if (newmode) { - drm_mode_probed_add(connector, newmode); - modes++; - } - } - - return modes; -} - -/** - * add_standard_modes - get std. modes from EDID and add them - * @edid: EDID block to scan - * - * Standard modes can be calculated using the CVT standard. Grab them from - * @edid, calculate them, and add them to the list. - */ -static int add_standard_modes(struct drm_connector *connector, struct edid *edid) -{ - int i, modes = 0; - - for (i = 0; i < EDID_STD_TIMINGS; i++) { - struct drm_display_mode *newmode; - - newmode = drm_mode_std(connector, edid, - &edid->standard_timings[i], - edid->revision); - if (newmode) { - drm_mode_probed_add(connector, newmode); - modes++; - } - } - - return modes; -} - static bool mode_is_rb(struct drm_display_mode *mode) { @@ -1267,113 +980,33 @@ drm_gtf_modes_for_range(struct drm_connector *connector, struct edid *edid, return modes; } -static int drm_cvt_modes(struct drm_connector *connector, - struct detailed_timing *timing) +static void +do_inferred_modes(struct detailed_timing *timing, void *c) { - int i, j, modes = 0; - struct drm_display_mode *newmode; - struct drm_device *dev = connector->dev; - struct cvt_timing *cvt; - const int rates[] = { 60, 85, 75, 60, 50 }; - const u8 empty[3] = { 0, 0, 0 }; - - for (i = 0; i < 4; i++) { - int uninitialized_var(width), height; - cvt = &(timing->data.other_data.data.cvt[i]); + struct detailed_mode_closure *closure = c; + struct detailed_non_pixel *data = &timing->data.other_data; + int gtf = (closure->edid->features & DRM_EDID_FEATURE_DEFAULT_GTF); - if (!memcmp(cvt->code, empty, 3)) - continue; + if (gtf && data->type == EDID_DETAIL_MONITOR_RANGE) + closure->modes += drm_gtf_modes_for_range(closure->connector, + closure->edid, + timing); +} - height = (cvt->code[0] + ((cvt->code[1] & 0xf0) << 4) + 1) * 2; - switch (cvt->code[1] & 0x0c) { - case 0x00: - width = height * 4 / 3; - break; - case 0x04: - width = height * 16 / 9; - break; - case 0x08: - width = height * 16 / 10; - break; - case 0x0c: - width = height * 15 / 9; - break; - } +static int +add_inferred_modes(struct drm_connector *connector, struct edid *edid) +{ + struct detailed_mode_closure closure = { + connector, edid, 0, 0, 0 + }; - for (j = 1; j < 5; j++) { - if (cvt->code[2] & (1 << j)) { - newmode = drm_cvt_mode(dev, width, height, - rates[j], j == 0, - false, false); - if (newmode) { - drm_mode_probed_add(connector, newmode); - modes++; - } - } - } - } + if (version_greater(edid, 1, 0)) + drm_for_each_detailed_block((u8 *)edid, do_inferred_modes, + &closure); - return modes; + return closure.modes; } -static const struct { - short w; - short h; - short r; - short rb; -} est3_modes[] = { - /* byte 6 */ - { 640, 350, 85, 0 }, - { 640, 400, 85, 0 }, - { 720, 400, 85, 0 }, - { 640, 480, 85, 0 }, - { 848, 480, 60, 0 }, - { 800, 600, 85, 0 }, - { 1024, 768, 85, 0 }, - { 1152, 864, 75, 0 }, - /* byte 7 */ - { 1280, 768, 60, 1 }, - { 1280, 768, 60, 0 }, - { 1280, 768, 75, 0 }, - { 1280, 768, 85, 0 }, - { 1280, 960, 60, 0 }, - { 1280, 960, 85, 0 }, - { 1280, 1024, 60, 0 }, - { 1280, 1024, 85, 0 }, - /* byte 8 */ - { 1360, 768, 60, 0 }, - { 1440, 900, 60, 1 }, - { 1440, 900, 60, 0 }, - { 1440, 900, 75, 0 }, - { 1440, 900, 85, 0 }, - { 1400, 1050, 60, 1 }, - { 1400, 1050, 60, 0 }, - { 1400, 1050, 75, 0 }, - /* byte 9 */ - { 1400, 1050, 85, 0 }, - { 1680, 1050, 60, 1 }, - { 1680, 1050, 60, 0 }, - { 1680, 1050, 75, 0 }, - { 1680, 1050, 85, 0 }, - { 1600, 1200, 60, 0 }, - { 1600, 1200, 65, 0 }, - { 1600, 1200, 70, 0 }, - /* byte 10 */ - { 1600, 1200, 75, 0 }, - { 1600, 1200, 85, 0 }, - { 1792, 1344, 60, 0 }, - { 1792, 1344, 85, 0 }, - { 1856, 1392, 60, 0 }, - { 1856, 1392, 75, 0 }, - { 1920, 1200, 60, 1 }, - { 1920, 1200, 60, 0 }, - /* byte 11 */ - { 1920, 1200, 75, 0 }, - { 1920, 1200, 85, 0 }, - { 1920, 1440, 60, 0 }, - { 1920, 1440, 75, 0 }, -}; - static int drm_est3_modes(struct drm_connector *connector, struct detailed_timing *timing) { @@ -1403,37 +1036,63 @@ drm_est3_modes(struct drm_connector *connector, struct detailed_timing *timing) return modes; } -static int add_detailed_modes(struct drm_connector *connector, - struct detailed_timing *timing, - struct edid *edid, u32 quirks, int preferred) +static void +do_established_modes(struct detailed_timing *timing, void *c) { - int i, modes = 0; + struct detailed_mode_closure *closure = c; struct detailed_non_pixel *data = &timing->data.other_data; - int gtf = (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF); - struct drm_display_mode *newmode; - struct drm_device *dev = connector->dev; - if (timing->pixel_clock) { - newmode = drm_mode_detailed(dev, edid, timing, quirks); - if (!newmode) - return 0; + if (data->type == EDID_DETAIL_EST_TIMINGS) + closure->modes += drm_est3_modes(closure->connector, timing); +} - if (preferred) - newmode->type |= DRM_MODE_TYPE_PREFERRED; +/** + * add_established_modes - get est. modes from EDID and add them + * @edid: EDID block to scan + * + * Each EDID block contains a bitmap of the supported "established modes" list + * (defined above). Tease them out and add them to the global modes list. + */ +static int +add_established_modes(struct drm_connector *connector, struct edid *edid) +{ + struct drm_device *dev = connector->dev; + unsigned long est_bits = edid->established_timings.t1 | + (edid->established_timings.t2 << 8) | + ((edid->established_timings.mfg_rsvd & 0x80) << 9); + int i, modes = 0; + struct detailed_mode_closure closure = { + connector, edid, 0, 0, 0 + }; - drm_mode_probed_add(connector, newmode); - return 1; + for (i = 0; i <= EDID_EST_TIMINGS; i++) { + if (est_bits & (1<<i)) { + struct drm_display_mode *newmode; + newmode = drm_mode_duplicate(dev, &edid_est_modes[i]); + if (newmode) { + drm_mode_probed_add(connector, newmode); + modes++; + } + } } - /* other timing types */ - switch (data->type) { - case EDID_DETAIL_MONITOR_RANGE: - if (gtf) - modes += drm_gtf_modes_for_range(connector, edid, - timing); - break; - case EDID_DETAIL_STD_MODES: - /* Six modes per detailed section */ + if (version_greater(edid, 1, 0)) + drm_for_each_detailed_block((u8 *)edid, + do_established_modes, &closure); + + return modes + closure.modes; +} + +static void +do_standard_modes(struct detailed_timing *timing, void *c) +{ + struct detailed_mode_closure *closure = c; + struct detailed_non_pixel *data = &timing->data.other_data; + struct drm_connector *connector = closure->connector; + struct edid *edid = closure->edid; + + if (data->type == EDID_DETAIL_STD_MODES) { + int i; for (i = 0; i < 6; i++) { struct std_timing *std; struct drm_display_mode *newmode; @@ -1443,108 +1102,169 @@ static int add_detailed_modes(struct drm_connector *connector, edid->revision); if (newmode) { drm_mode_probed_add(connector, newmode); - modes++; + closure->modes++; } } - break; - case EDID_DETAIL_CVT_3BYTE: - modes += drm_cvt_modes(connector, timing); - break; - case EDID_DETAIL_EST_TIMINGS: - modes += drm_est3_modes(connector, timing); - break; - default: - break; } - - return modes; } /** - * add_detailed_info - get detailed mode info from EDID data - * @connector: attached connector + * add_standard_modes - get std. modes from EDID and add them * @edid: EDID block to scan - * @quirks: quirks to apply * - * Some of the detailed timing sections may contain mode information. Grab - * it and add it to the list. + * Standard modes can be calculated using the appropriate standard (DMT, + * GTF or CVT. Grab them from @edid and add them to the list. */ -static int add_detailed_info(struct drm_connector *connector, - struct edid *edid, u32 quirks) +static int +add_standard_modes(struct drm_connector *connector, struct edid *edid) { int i, modes = 0; + struct detailed_mode_closure closure = { + connector, edid, 0, 0, 0 + }; + + for (i = 0; i < EDID_STD_TIMINGS; i++) { + struct drm_display_mode *newmode; + + newmode = drm_mode_std(connector, edid, + &edid->standard_timings[i], + edid->revision); + if (newmode) { + drm_mode_probed_add(connector, newmode); + modes++; + } + } + + if (version_greater(edid, 1, 0)) + drm_for_each_detailed_block((u8 *)edid, do_standard_modes, + &closure); + + /* XXX should also look for standard codes in VTB blocks */ + + return modes + closure.modes; +} - for (i = 0; i < EDID_DETAILED_TIMINGS; i++) { - struct detailed_timing *timing = &edid->detailed_timings[i]; - int preferred = (i == 0); +static int drm_cvt_modes(struct drm_connector *connector, + struct detailed_timing *timing) +{ + int i, j, modes = 0; + struct drm_display_mode *newmode; + struct drm_device *dev = connector->dev; + struct cvt_timing *cvt; + const int rates[] = { 60, 85, 75, 60, 50 }; + const u8 empty[3] = { 0, 0, 0 }; - if (preferred && edid->version == 1 && edid->revision < 4) - preferred = (edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING); + for (i = 0; i < 4; i++) { + int uninitialized_var(width), height; + cvt = &(timing->data.other_data.data.cvt[i]); - /* In 1.0, only timings are allowed */ - if (!timing->pixel_clock && edid->version == 1 && - edid->revision == 0) + if (!memcmp(cvt->code, empty, 3)) continue; - modes += add_detailed_modes(connector, timing, edid, quirks, - preferred); + height = (cvt->code[0] + ((cvt->code[1] & 0xf0) << 4) + 1) * 2; + switch (cvt->code[1] & 0x0c) { + case 0x00: + width = height * 4 / 3; + break; + case 0x04: + width = height * 16 / 9; + break; + case 0x08: + width = height * 16 / 10; + break; + case 0x0c: + width = height * 15 / 9; + break; + } + + for (j = 1; j < 5; j++) { + if (cvt->code[2] & (1 << j)) { + newmode = drm_cvt_mode(dev, width, height, + rates[j], j == 0, + false, false); + if (newmode) { + drm_mode_probed_add(connector, newmode); + modes++; + } + } + } } return modes; } -/** - * add_detailed_mode_eedid - get detailed mode info from addtional timing - * EDID block - * @connector: attached connector - * @edid: EDID block to scan(It is only to get addtional timing EDID block) - * @quirks: quirks to apply - * - * Some of the detailed timing sections may contain mode information. Grab - * it and add it to the list. - */ -static int add_detailed_info_eedid(struct drm_connector *connector, - struct edid *edid, u32 quirks) +static void +do_cvt_mode(struct detailed_timing *timing, void *c) { - int i, modes = 0; - char *edid_ext = NULL; - struct detailed_timing *timing; - int start_offset, end_offset; + struct detailed_mode_closure *closure = c; + struct detailed_non_pixel *data = &timing->data.other_data; - if (edid->version == 1 && edid->revision < 3) - return 0; - if (!edid->extensions) - return 0; + if (data->type == EDID_DETAIL_CVT_3BYTE) + closure->modes += drm_cvt_modes(closure->connector, timing); +} - /* Find CEA extension */ - for (i = 0; i < edid->extensions; i++) { - edid_ext = (char *)edid + EDID_LENGTH * (i + 1); - if (edid_ext[0] == 0x02) - break; - } +static int +add_cvt_modes(struct drm_connector *connector, struct edid *edid) +{ + struct detailed_mode_closure closure = { + connector, edid, 0, 0, 0 + }; - if (i == edid->extensions) - return 0; + if (version_greater(edid, 1, 2)) + drm_for_each_detailed_block((u8 *)edid, do_cvt_mode, &closure); - /* Get the start offset of detailed timing block */ - start_offset = edid_ext[2]; - if (start_offset == 0) { - /* If the start_offset is zero, it means that neither detailed - * info nor data block exist. In such case it is also - * unnecessary to parse the detailed timing info. - */ - return 0; - } + /* XXX should also look for CVT codes in VTB blocks */ - end_offset = EDID_LENGTH; - end_offset -= sizeof(struct detailed_timing); - for (i = start_offset; i < end_offset; - i += sizeof(struct detailed_timing)) { - timing = (struct detailed_timing *)(edid_ext + i); - modes += add_detailed_modes(connector, timing, edid, quirks, 0); + return closure.modes; +} + +static void +do_detailed_mode(struct detailed_timing *timing, void *c) +{ + struct detailed_mode_closure *closure = c; + struct drm_display_mode *newmode; + + if (timing->pixel_clock) { + newmode = drm_mode_detailed(closure->connector->dev, + closure->edid, timing, + closure->quirks); + if (!newmode) + return; + + if (closure->preferred) + newmode->type |= DRM_MODE_TYPE_PREFERRED; + + drm_mode_probed_add(closure->connector, newmode); + closure->modes++; + closure->preferred = 0; } +} - return modes; +/* + * add_detailed_modes - Add modes from detailed timings + * @connector: attached connector + * @edid: EDID block to scan + * @quirks: quirks to apply + */ +static int +add_detailed_modes(struct drm_connector *connector, struct edid *edid, + u32 quirks) +{ + struct detailed_mode_closure closure = { + connector, + edid, + 1, + quirks, + 0 + }; + + if (closure.preferred && !version_greater(edid, 1, 3)) + closure.preferred = + (edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING); + + drm_for_each_detailed_block((u8 *)edid, do_detailed_mode, &closure); + + return closure.modes; } #define HDMI_IDENTIFIER 0x000C03 @@ -1640,35 +1360,21 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) * - established timing codes * - modes inferred from GTF or CVT range information * - * We don't quite implement this yet, but we're close. + * We get this pretty much right. * * XXX order for additional mode types in extension blocks? */ - num_modes += add_detailed_info(connector, edid, quirks); - num_modes += add_detailed_info_eedid(connector, edid, quirks); + num_modes += add_detailed_modes(connector, edid, quirks); + num_modes += add_cvt_modes(connector, edid); num_modes += add_standard_modes(connector, edid); num_modes += add_established_modes(connector, edid); + num_modes += add_inferred_modes(connector, edid); if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75)) edid_fixup_preferred(connector, quirks); - connector->display_info.serration_vsync = (edid->input & DRM_EDID_INPUT_SERRATION_VSYNC) ? 1 : 0; - connector->display_info.sync_on_green = (edid->input & DRM_EDID_INPUT_SYNC_ON_GREEN) ? 1 : 0; - connector->display_info.composite_sync = (edid->input & DRM_EDID_INPUT_COMPOSITE_SYNC) ? 1 : 0; - connector->display_info.separate_syncs = (edid->input & DRM_EDID_INPUT_SEPARATE_SYNCS) ? 1 : 0; - connector->display_info.blank_to_black = (edid->input & DRM_EDID_INPUT_BLANK_TO_BLACK) ? 1 : 0; - connector->display_info.video_level = (edid->input & DRM_EDID_INPUT_VIDEO_LEVEL) >> 5; - connector->display_info.digital = (edid->input & DRM_EDID_INPUT_DIGITAL) ? 1 : 0; connector->display_info.width_mm = edid->width_cm * 10; connector->display_info.height_mm = edid->height_cm * 10; - connector->display_info.gamma = edid->gamma; - connector->display_info.gtf_supported = (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF) ? 1 : 0; - connector->display_info.standard_color = (edid->features & DRM_EDID_FEATURE_STANDARD_COLOR) ? 1 : 0; - connector->display_info.display_type = (edid->features & DRM_EDID_FEATURE_DISPLAY_TYPE) >> 3; - connector->display_info.active_off_supported = (edid->features & DRM_EDID_FEATURE_PM_ACTIVE_OFF) ? 1 : 0; - connector->display_info.suspend_supported = (edid->features & DRM_EDID_FEATURE_PM_SUSPEND) ? 1 : 0; - connector->display_info.standby_supported = (edid->features & DRM_EDID_FEATURE_PM_STANDBY) ? 1 : 0; - connector->display_info.gamma = edid->gamma; return num_modes; } diff --git a/drivers/gpu/drm/drm_edid_modes.h b/drivers/gpu/drm/drm_edid_modes.h new file mode 100644 index 00000000000..6eb7592e152 --- /dev/null +++ b/drivers/gpu/drm/drm_edid_modes.h @@ -0,0 +1,380 @@ +/* + * Copyright (c) 2007-2008 Intel Corporation + * Jesse Barnes <jesse.barnes@intel.com> + * Copyright 2010 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sub license, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include <linux/kernel.h> +#include "drmP.h" +#include "drm_edid.h" + +/* + * Autogenerated from the DMT spec. + * This table is copied from xfree86/modes/xf86EdidModes.c. + * But the mode with Reduced blank feature is deleted. + */ +static struct drm_display_mode drm_dmt_modes[] = { + /* 640x350@85Hz */ + { DRM_MODE("640x350", DRM_MODE_TYPE_DRIVER, 31500, 640, 672, + 736, 832, 0, 350, 382, 385, 445, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, + /* 640x400@85Hz */ + { DRM_MODE("640x400", DRM_MODE_TYPE_DRIVER, 31500, 640, 672, + 736, 832, 0, 400, 401, 404, 445, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 720x400@85Hz */ + { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 35500, 720, 756, + 828, 936, 0, 400, 401, 404, 446, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 640x480@60Hz */ + { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656, + 752, 800, 0, 480, 489, 492, 525, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, + /* 640x480@72Hz */ + { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 664, + 704, 832, 0, 480, 489, 492, 520, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, + /* 640x480@75Hz */ + { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 656, + 720, 840, 0, 480, 481, 484, 500, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, + /* 640x480@85Hz */ + { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 36000, 640, 696, + 752, 832, 0, 480, 481, 484, 509, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, + /* 800x600@56Hz */ + { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 36000, 800, 824, + 896, 1024, 0, 600, 601, 603, 625, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 800x600@60Hz */ + { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840, + 968, 1056, 0, 600, 601, 605, 628, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 800x600@72Hz */ + { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 50000, 800, 856, + 976, 1040, 0, 600, 637, 643, 666, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 800x600@75Hz */ + { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 49500, 800, 816, + 896, 1056, 0, 600, 601, 604, 625, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 800x600@85Hz */ + { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 56250, 800, 832, + 896, 1048, 0, 600, 601, 604, 631, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 848x480@60Hz */ + { DRM_MODE("848x480", DRM_MODE_TYPE_DRIVER, 33750, 848, 864, + 976, 1088, 0, 480, 486, 494, 517, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1024x768@43Hz, interlace */ + { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 44900, 1024, 1032, + 1208, 1264, 0, 768, 768, 772, 817, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | + DRM_MODE_FLAG_INTERLACE) }, + /* 1024x768@60Hz */ + { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048, + 1184, 1344, 0, 768, 771, 777, 806, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, + /* 1024x768@70Hz */ + { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 75000, 1024, 1048, + 1184, 1328, 0, 768, 771, 777, 806, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, + /* 1024x768@75Hz */ + { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 78750, 1024, 1040, + 1136, 1312, 0, 768, 769, 772, 800, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1024x768@85Hz */ + { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 94500, 1024, 1072, + 1168, 1376, 0, 768, 769, 772, 808, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1152x864@75Hz */ + { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216, + 1344, 1600, 0, 864, 865, 868, 900, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1280x768@60Hz */ + { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 79500, 1280, 1344, + 1472, 1664, 0, 768, 771, 778, 798, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1280x768@75Hz */ + { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 102250, 1280, 1360, + 1488, 1696, 0, 768, 771, 778, 805, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, + /* 1280x768@85Hz */ + { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 117500, 1280, 1360, + 1496, 1712, 0, 768, 771, 778, 809, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1280x800@60Hz */ + { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 83500, 1280, 1352, + 1480, 1680, 0, 800, 803, 809, 831, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, + /* 1280x800@75Hz */ + { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 106500, 1280, 1360, + 1488, 1696, 0, 800, 803, 809, 838, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1280x800@85Hz */ + { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 122500, 1280, 1360, + 1496, 1712, 0, 800, 803, 809, 843, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1280x960@60Hz */ + { DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1376, + 1488, 1800, 0, 960, 961, 964, 1000, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1280x960@85Hz */ + { DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1344, + 1504, 1728, 0, 960, 961, 964, 1011, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1280x1024@60Hz */ + { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1328, + 1440, 1688, 0, 1024, 1025, 1028, 1066, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1280x1024@75Hz */ + { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 135000, 1280, 1296, + 1440, 1688, 0, 1024, 1025, 1028, 1066, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1280x1024@85Hz */ + { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 157500, 1280, 1344, + 1504, 1728, 0, 1024, 1025, 1028, 1072, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1360x768@60Hz */ + { DRM_MODE("1360x768", DRM_MODE_TYPE_DRIVER, 85500, 1360, 1424, + 1536, 1792, 0, 768, 771, 777, 795, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1440x1050@60Hz */ + { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 121750, 1400, 1488, + 1632, 1864, 0, 1050, 1053, 1057, 1089, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1440x1050@75Hz */ + { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 156000, 1400, 1504, + 1648, 1896, 0, 1050, 1053, 1057, 1099, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1440x1050@85Hz */ + { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 179500, 1400, 1504, + 1656, 1912, 0, 1050, 1053, 1057, 1105, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1440x900@60Hz */ + { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 106500, 1440, 1520, + 1672, 1904, 0, 900, 903, 909, 934, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1440x900@75Hz */ + { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 136750, 1440, 1536, + 1688, 1936, 0, 900, 903, 909, 942, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1440x900@85Hz */ + { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 157000, 1440, 1544, + 1696, 1952, 0, 900, 903, 909, 948, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1600x1200@60Hz */ + { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 162000, 1600, 1664, + 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1600x1200@65Hz */ + { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 175500, 1600, 1664, + 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1600x1200@70Hz */ + { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 189000, 1600, 1664, + 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1600x1200@75Hz */ + { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 202500, 1600, 1664, + 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1600x1200@85Hz */ + { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 229500, 1600, 1664, + 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1680x1050@60Hz */ + { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 146250, 1680, 1784, + 1960, 2240, 0, 1050, 1053, 1059, 1089, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1680x1050@75Hz */ + { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 187000, 1680, 1800, + 1976, 2272, 0, 1050, 1053, 1059, 1099, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1680x1050@85Hz */ + { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 214750, 1680, 1808, + 1984, 2288, 0, 1050, 1053, 1059, 1105, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1792x1344@60Hz */ + { DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 204750, 1792, 1920, + 2120, 2448, 0, 1344, 1345, 1348, 1394, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1729x1344@75Hz */ + { DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 261000, 1792, 1888, + 2104, 2456, 0, 1344, 1345, 1348, 1417, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1853x1392@60Hz */ + { DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 218250, 1856, 1952, + 2176, 2528, 0, 1392, 1393, 1396, 1439, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1856x1392@75Hz */ + { DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 288000, 1856, 1984, + 2208, 2560, 0, 1392, 1395, 1399, 1500, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1920x1200@60Hz */ + { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 193250, 1920, 2056, + 2256, 2592, 0, 1200, 1203, 1209, 1245, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1920x1200@75Hz */ + { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 245250, 1920, 2056, + 2264, 2608, 0, 1200, 1203, 1209, 1255, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1920x1200@85Hz */ + { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 281250, 1920, 2064, + 2272, 2624, 0, 1200, 1203, 1209, 1262, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1920x1440@60Hz */ + { DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 234000, 1920, 2048, + 2256, 2600, 0, 1440, 1441, 1444, 1500, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1920x1440@75Hz */ + { DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2064, + 2288, 2640, 0, 1440, 1441, 1444, 1500, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 2560x1600@60Hz */ + { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 348500, 2560, 2752, + 3032, 3504, 0, 1600, 1603, 1609, 1658, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 2560x1600@75HZ */ + { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 443250, 2560, 2768, + 3048, 3536, 0, 1600, 1603, 1609, 1672, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 2560x1600@85HZ */ + { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 505250, 2560, 2768, + 3048, 3536, 0, 1600, 1603, 1609, 1682, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, +}; +static const int drm_num_dmt_modes = + sizeof(drm_dmt_modes) / sizeof(struct drm_display_mode); + +static struct drm_display_mode edid_est_modes[] = { + { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840, + 968, 1056, 0, 600, 601, 605, 628, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@60Hz */ + { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 36000, 800, 824, + 896, 1024, 0, 600, 601, 603, 625, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@56Hz */ + { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 656, + 720, 840, 0, 480, 481, 484, 500, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@75Hz */ + { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 664, + 704, 832, 0, 480, 489, 491, 520, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@72Hz */ + { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 30240, 640, 704, + 768, 864, 0, 480, 483, 486, 525, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@67Hz */ + { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25200, 640, 656, + 752, 800, 0, 480, 490, 492, 525, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@60Hz */ + { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 35500, 720, 738, + 846, 900, 0, 400, 421, 423, 449, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 720x400@88Hz */ + { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 28320, 720, 738, + 846, 900, 0, 400, 412, 414, 449, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 720x400@70Hz */ + { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 135000, 1280, 1296, + 1440, 1688, 0, 1024, 1025, 1028, 1066, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1280x1024@75Hz */ + { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 78800, 1024, 1040, + 1136, 1312, 0, 768, 769, 772, 800, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1024x768@75Hz */ + { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 75000, 1024, 1048, + 1184, 1328, 0, 768, 771, 777, 806, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@70Hz */ + { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048, + 1184, 1344, 0, 768, 771, 777, 806, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@60Hz */ + { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER,44900, 1024, 1032, + 1208, 1264, 0, 768, 768, 776, 817, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_INTERLACE) }, /* 1024x768@43Hz */ + { DRM_MODE("832x624", DRM_MODE_TYPE_DRIVER, 57284, 832, 864, + 928, 1152, 0, 624, 625, 628, 667, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 832x624@75Hz */ + { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 49500, 800, 816, + 896, 1056, 0, 600, 601, 604, 625, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@75Hz */ + { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 50000, 800, 856, + 976, 1040, 0, 600, 637, 643, 666, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@72Hz */ + { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216, + 1344, 1600, 0, 864, 865, 868, 900, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1152x864@75Hz */ +}; + +static const struct { + short w; + short h; + short r; + short rb; +} est3_modes[] = { + /* byte 6 */ + { 640, 350, 85, 0 }, + { 640, 400, 85, 0 }, + { 720, 400, 85, 0 }, + { 640, 480, 85, 0 }, + { 848, 480, 60, 0 }, + { 800, 600, 85, 0 }, + { 1024, 768, 85, 0 }, + { 1152, 864, 75, 0 }, + /* byte 7 */ + { 1280, 768, 60, 1 }, + { 1280, 768, 60, 0 }, + { 1280, 768, 75, 0 }, + { 1280, 768, 85, 0 }, + { 1280, 960, 60, 0 }, + { 1280, 960, 85, 0 }, + { 1280, 1024, 60, 0 }, + { 1280, 1024, 85, 0 }, + /* byte 8 */ + { 1360, 768, 60, 0 }, + { 1440, 900, 60, 1 }, + { 1440, 900, 60, 0 }, + { 1440, 900, 75, 0 }, + { 1440, 900, 85, 0 }, + { 1400, 1050, 60, 1 }, + { 1400, 1050, 60, 0 }, + { 1400, 1050, 75, 0 }, + /* byte 9 */ + { 1400, 1050, 85, 0 }, + { 1680, 1050, 60, 1 }, + { 1680, 1050, 60, 0 }, + { 1680, 1050, 75, 0 }, + { 1680, 1050, 85, 0 }, + { 1600, 1200, 60, 0 }, + { 1600, 1200, 65, 0 }, + { 1600, 1200, 70, 0 }, + /* byte 10 */ + { 1600, 1200, 75, 0 }, + { 1600, 1200, 85, 0 }, + { 1792, 1344, 60, 0 }, + { 1792, 1344, 85, 0 }, + { 1856, 1392, 60, 0 }, + { 1856, 1392, 75, 0 }, + { 1920, 1200, 60, 1 }, + { 1920, 1200, 60, 0 }, + /* byte 11 */ + { 1920, 1200, 75, 0 }, + { 1920, 1200, 85, 0 }, + { 1920, 1440, 60, 0 }, + { 1920, 1440, 75, 0 }, +}; +static const int num_est3_modes = sizeof(est3_modes) / sizeof(est3_modes[0]); diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 2ca8df8b610..3a652a65546 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -135,15 +135,9 @@ int drm_open(struct inode *inode, struct file *filp) retcode = drm_open_helper(inode, filp, dev); if (!retcode) { atomic_inc(&dev->counts[_DRM_STAT_OPENS]); - spin_lock(&dev->count_lock); - if (!dev->open_count++) { - spin_unlock(&dev->count_lock); + if (!dev->open_count++) retcode = drm_setup(dev); - goto out; - } - spin_unlock(&dev->count_lock); } -out: if (!retcode) { mutex_lock(&dev->struct_mutex); if (minor->type == DRM_MINOR_LEGACY) { @@ -570,18 +564,14 @@ int drm_release(struct inode *inode, struct file *filp) */ atomic_inc(&dev->counts[_DRM_STAT_CLOSES]); - spin_lock(&dev->count_lock); if (!--dev->open_count) { if (atomic_read(&dev->ioctl_count)) { DRM_ERROR("Device busy: %d\n", atomic_read(&dev->ioctl_count)); retcode = -EBUSY; - goto out; - } - retcode = drm_lastclose(dev); + } else + retcode = drm_lastclose(dev); } -out: - spin_unlock(&dev->count_lock); mutex_unlock(&drm_global_mutex); return retcode; diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 4f1b8671448..bf92d07510d 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -322,7 +322,7 @@ drm_gem_flink_ioctl(struct drm_device *dev, void *data, obj = drm_gem_object_lookup(dev, file_priv, args->handle); if (obj == NULL) - return -EBADF; + return -ENOENT; again: if (idr_pre_get(&dev->object_name_idr, GFP_KERNEL) == 0) { diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index 7b03b197fc0..47db4df37a6 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -392,6 +392,7 @@ int drm_setversion(struct drm_device *dev, void *data, struct drm_file *file_pri if (sv->drm_di_minor >= 1) { /* * Version 1.1 includes tying of DRM to specific device + * Version 1.4 has proper PCI domain support */ retcode = drm_set_busid(dev, file_priv); if (retcode) diff --git a/drivers/gpu/drm/i2c/ch7006_drv.c b/drivers/gpu/drm/i2c/ch7006_drv.c index 833b35f44a7..08792a740f1 100644 --- a/drivers/gpu/drm/i2c/ch7006_drv.c +++ b/drivers/gpu/drm/i2c/ch7006_drv.c @@ -470,6 +470,7 @@ static int ch7006_encoder_init(struct i2c_client *client, priv->hmargin = 50; priv->vmargin = 50; priv->last_dpms = -1; + priv->chip_version = ch7006_read(client, CH7006_VERSION_ID); if (ch7006_tv_norm) { for (i = 0; i < NUM_TV_NORMS; i++) { diff --git a/drivers/gpu/drm/i2c/ch7006_mode.c b/drivers/gpu/drm/i2c/ch7006_mode.c index e447dfb6389..c860f24a5af 100644 --- a/drivers/gpu/drm/i2c/ch7006_mode.c +++ b/drivers/gpu/drm/i2c/ch7006_mode.c @@ -316,7 +316,10 @@ void ch7006_setup_power_state(struct drm_encoder *encoder) } } else { - *power |= bitfs(CH7006_POWER_LEVEL, FULL_POWER_OFF); + if (priv->chip_version >= 0x20) + *power |= bitfs(CH7006_POWER_LEVEL, FULL_POWER_OFF); + else + *power |= bitfs(CH7006_POWER_LEVEL, POWER_OFF); } } diff --git a/drivers/gpu/drm/i2c/ch7006_priv.h b/drivers/gpu/drm/i2c/ch7006_priv.h index 1c6d2e3bd96..17667b7d57e 100644 --- a/drivers/gpu/drm/i2c/ch7006_priv.h +++ b/drivers/gpu/drm/i2c/ch7006_priv.h @@ -95,6 +95,7 @@ struct ch7006_priv { int flicker; int scale; + int chip_version; int last_dpms; }; diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 2a4ed7ca8b4..0758c7802e6 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -456,7 +456,7 @@ i915_gem_pread_ioctl(struct drm_device *dev, void *data, obj = drm_gem_object_lookup(dev, file_priv, args->handle); if (obj == NULL) - return -EBADF; + return -ENOENT; obj_priv = to_intel_bo(obj); /* Bounds check source. @@ -919,7 +919,7 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data, obj = drm_gem_object_lookup(dev, file_priv, args->handle); if (obj == NULL) - return -EBADF; + return -ENOENT; obj_priv = to_intel_bo(obj); /* Bounds check destination. @@ -1002,7 +1002,7 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data, obj = drm_gem_object_lookup(dev, file_priv, args->handle); if (obj == NULL) - return -EBADF; + return -ENOENT; obj_priv = to_intel_bo(obj); mutex_lock(&dev->struct_mutex); @@ -1060,7 +1060,7 @@ i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data, obj = drm_gem_object_lookup(dev, file_priv, args->handle); if (obj == NULL) { mutex_unlock(&dev->struct_mutex); - return -EBADF; + return -ENOENT; } #if WATCH_BUF @@ -1099,7 +1099,7 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data, obj = drm_gem_object_lookup(dev, file_priv, args->handle); if (obj == NULL) - return -EBADF; + return -ENOENT; offset = args->offset; @@ -1373,7 +1373,7 @@ i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data, obj = drm_gem_object_lookup(dev, file_priv, args->handle); if (obj == NULL) - return -EBADF; + return -ENOENT; mutex_lock(&dev->struct_mutex); @@ -3364,7 +3364,7 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj, reloc->target_handle); if (target_obj == NULL) { i915_gem_object_unpin(obj); - return -EBADF; + return -ENOENT; } target_obj_priv = to_intel_bo(target_obj); @@ -3781,7 +3781,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, exec_list[i].handle, i); /* prevent error path from reading uninitialized data */ args->buffer_count = i + 1; - ret = -EBADF; + ret = -ENOENT; goto err; } @@ -3791,7 +3791,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, object_list[i]); /* prevent error path from reading uninitialized data */ args->buffer_count = i + 1; - ret = -EBADF; + ret = -EINVAL; goto err; } obj_priv->in_execbuffer = true; @@ -4265,7 +4265,7 @@ i915_gem_pin_ioctl(struct drm_device *dev, void *data, DRM_ERROR("Bad handle in i915_gem_pin_ioctl(): %d\n", args->handle); mutex_unlock(&dev->struct_mutex); - return -EBADF; + return -ENOENT; } obj_priv = to_intel_bo(obj); @@ -4321,7 +4321,7 @@ i915_gem_unpin_ioctl(struct drm_device *dev, void *data, DRM_ERROR("Bad handle in i915_gem_unpin_ioctl(): %d\n", args->handle); mutex_unlock(&dev->struct_mutex); - return -EBADF; + return -ENOENT; } obj_priv = to_intel_bo(obj); @@ -4355,7 +4355,7 @@ i915_gem_busy_ioctl(struct drm_device *dev, void *data, if (obj == NULL) { DRM_ERROR("Bad handle in i915_gem_busy_ioctl(): %d\n", args->handle); - return -EBADF; + return -ENOENT; } mutex_lock(&dev->struct_mutex); @@ -4408,7 +4408,7 @@ i915_gem_madvise_ioctl(struct drm_device *dev, void *data, if (obj == NULL) { DRM_ERROR("Bad handle in i915_gem_madvise_ioctl(): %d\n", args->handle); - return -EBADF; + return -ENOENT; } mutex_lock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c index 155719e4d16..710eca70b32 100644 --- a/drivers/gpu/drm/i915/i915_gem_tiling.c +++ b/drivers/gpu/drm/i915/i915_gem_tiling.c @@ -275,7 +275,7 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, obj = drm_gem_object_lookup(dev, file_priv, args->handle); if (obj == NULL) - return -EINVAL; + return -ENOENT; obj_priv = to_intel_bo(obj); if (!i915_tiling_ok(dev, args->stride, obj->size, args->tiling_mode)) { @@ -362,7 +362,7 @@ i915_gem_get_tiling(struct drm_device *dev, void *data, obj = drm_gem_object_lookup(dev, file_priv, args->handle); if (obj == NULL) - return -EINVAL; + return -ENOENT; obj_priv = to_intel_bo(obj); mutex_lock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 1e5e0d379fa..5ec10e02341 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4429,15 +4429,12 @@ void intel_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, } static void intel_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, - u16 *blue, uint32_t size) + u16 *blue, uint32_t start, uint32_t size) { + int end = (start + size > 256) ? 256 : start + size, i; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - int i; - - if (size != 256) - return; - for (i = 0; i < 256; i++) { + for (i = start; i < end; i++) { intel_crtc->lut_r[i] = red[i] >> 8; intel_crtc->lut_g[i] = green[i] >> 8; intel_crtc->lut_b[i] = blue[i] >> 8; @@ -5412,18 +5409,18 @@ intel_user_framebuffer_create(struct drm_device *dev, obj = drm_gem_object_lookup(dev, filp, mode_cmd->handle); if (!obj) - return NULL; + return ERR_PTR(-ENOENT); intel_fb = kzalloc(sizeof(*intel_fb), GFP_KERNEL); if (!intel_fb) - return NULL; + return ERR_PTR(-ENOMEM); ret = intel_framebuffer_init(dev, intel_fb, mode_cmd, obj); if (ret) { drm_gem_object_unreference_unlocked(obj); kfree(intel_fb); - return NULL; + return ERR_PTR(ret); } return &intel_fb->base; diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index a79525f434a..7bdc96256bf 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -121,7 +121,9 @@ static int intelfb_create(struct intel_fbdev *ifbdev, info->par = ifbdev; - intel_framebuffer_init(dev, &ifbdev->ifb, &mode_cmd, fbo); + ret = intel_framebuffer_init(dev, &ifbdev->ifb, &mode_cmd, fbo); + if (ret) + goto out_unpin; fb = &ifbdev->ifb.base; diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile index 2405d5ef0ca..e9b06e4ef2a 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile @@ -12,12 +12,12 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \ nouveau_dp.o \ nv04_timer.o \ nv04_mc.o nv40_mc.o nv50_mc.o \ - nv04_fb.o nv10_fb.o nv30_fb.o nv40_fb.o nv50_fb.o \ - nv04_fifo.o nv10_fifo.o nv40_fifo.o nv50_fifo.o \ + nv04_fb.o nv10_fb.o nv30_fb.o nv40_fb.o nv50_fb.o nvc0_fb.o \ + nv04_fifo.o nv10_fifo.o nv40_fifo.o nv50_fifo.o nvc0_fifo.o \ nv04_graph.o nv10_graph.o nv20_graph.o \ - nv40_graph.o nv50_graph.o \ + nv40_graph.o nv50_graph.o nvc0_graph.o \ nv40_grctx.o nv50_grctx.o \ - nv04_instmem.o nv50_instmem.o \ + nv04_instmem.o nv50_instmem.o nvc0_instmem.o \ nv50_crtc.o nv50_dac.o nv50_sor.o \ nv50_cursor.o nv50_display.o nv50_fbcon.o \ nv04_dac.o nv04_dfp.o nv04_tv.o nv17_tv.o nv17_tv_modes.o \ diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index 7369b5e7364..0b69a9628c9 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -1928,6 +1928,31 @@ init_condition_time(struct nvbios *bios, uint16_t offset, } static int +init_ltime(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) +{ + /* + * INIT_LTIME opcode: 0x57 ('V') + * + * offset (8 bit): opcode + * offset + 1 (16 bit): time + * + * Sleep for "time" miliseconds. + */ + + unsigned time = ROM16(bios->data[offset + 1]); + + if (!iexec->execute) + return 3; + + BIOSLOG(bios, "0x%04X: Sleeping for 0x%04X miliseconds\n", + offset, time); + + msleep(time); + + return 3; +} + +static int init_zm_reg_sequence(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) { @@ -1995,6 +2020,64 @@ init_sub_direct(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) } static int +init_i2c_if(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) +{ + /* + * INIT_I2C_IF opcode: 0x5E ('^') + * + * offset (8 bit): opcode + * offset + 1 (8 bit): DCB I2C table entry index + * offset + 2 (8 bit): I2C slave address + * offset + 3 (8 bit): I2C register + * offset + 4 (8 bit): mask + * offset + 5 (8 bit): data + * + * Read the register given by "I2C register" on the device addressed + * by "I2C slave address" on the I2C bus given by "DCB I2C table + * entry index". Compare the result AND "mask" to "data". + * If they're not equal, skip subsequent opcodes until condition is + * inverted (INIT_NOT), or we hit INIT_RESUME + */ + + uint8_t i2c_index = bios->data[offset + 1]; + uint8_t i2c_address = bios->data[offset + 2] >> 1; + uint8_t reg = bios->data[offset + 3]; + uint8_t mask = bios->data[offset + 4]; + uint8_t data = bios->data[offset + 5]; + struct nouveau_i2c_chan *chan; + union i2c_smbus_data val; + int ret; + + /* no execute check by design */ + + BIOSLOG(bios, "0x%04X: DCBI2CIndex: 0x%02X, I2CAddress: 0x%02X\n", + offset, i2c_index, i2c_address); + + chan = init_i2c_device_find(bios->dev, i2c_index); + if (!chan) + return -ENODEV; + + ret = i2c_smbus_xfer(&chan->adapter, i2c_address, 0, + I2C_SMBUS_READ, reg, + I2C_SMBUS_BYTE_DATA, &val); + if (ret < 0) { + BIOSLOG(bios, "0x%04X: I2CReg: 0x%02X, Value: [no device], " + "Mask: 0x%02X, Data: 0x%02X\n", + offset, reg, mask, data); + iexec->execute = 0; + return 6; + } + + BIOSLOG(bios, "0x%04X: I2CReg: 0x%02X, Value: 0x%02X, " + "Mask: 0x%02X, Data: 0x%02X\n", + offset, reg, val.byte, mask, data); + + iexec->execute = ((val.byte & mask) == data); + + return 6; +} + +static int init_copy_nv_reg(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) { /* @@ -2083,9 +2166,10 @@ peek_fb(struct drm_device *dev, struct io_mapping *fb, uint32_t val = 0; if (off < pci_resource_len(dev->pdev, 1)) { - uint32_t __iomem *p = io_mapping_map_atomic_wc(fb, off, KM_USER0); + uint32_t __iomem *p = + io_mapping_map_atomic_wc(fb, off & PAGE_MASK, KM_USER0); - val = ioread32(p); + val = ioread32(p + (off & ~PAGE_MASK)); io_mapping_unmap_atomic(p, KM_USER0); } @@ -2098,9 +2182,10 @@ poke_fb(struct drm_device *dev, struct io_mapping *fb, uint32_t off, uint32_t val) { if (off < pci_resource_len(dev->pdev, 1)) { - uint32_t __iomem *p = io_mapping_map_atomic_wc(fb, off, KM_USER0); + uint32_t __iomem *p = + io_mapping_map_atomic_wc(fb, off & PAGE_MASK, KM_USER0); - iowrite32(val, p); + iowrite32(val, p + (off & ~PAGE_MASK)); wmb(); io_mapping_unmap_atomic(p, KM_USER0); @@ -2165,7 +2250,7 @@ nv04_init_compute_mem(struct nvbios *bios) NV04_PFB_BOOT_0_RAM_AMOUNT, NV04_PFB_BOOT_0_RAM_AMOUNT_4MB); - } else if (peek_fb(dev, fb, 0) == patt) { + } else if (peek_fb(dev, fb, 0) != patt) { if (read_back_fb(dev, fb, 0x800000, patt)) bios_md32(bios, NV04_PFB_BOOT_0, NV04_PFB_BOOT_0_RAM_AMOUNT, @@ -2593,7 +2678,7 @@ init_configure_preinit(struct nvbios *bios, uint16_t offset, /* no iexec->execute check by design */ uint32_t straps = bios_rd32(bios, NV_PEXTDEV_BOOT_0); - uint8_t cr3c = ((straps << 2) & 0xf0) | (straps & (1 << 6)); + uint8_t cr3c = ((straps << 2) & 0xf0) | (straps & 0x40) >> 6; if (bios->major_version > 2) return 0; @@ -3140,7 +3225,7 @@ init_gpio(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) const uint32_t nv50_gpio_ctl[2] = { 0xe100, 0xe28c }; int i; - if (dev_priv->card_type != NV_50) { + if (dev_priv->card_type < NV_50) { NV_ERROR(bios->dev, "INIT_GPIO on unsupported chipset\n"); return 1; } @@ -3490,6 +3575,69 @@ init_zm_auxch(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) return len; } +static int +init_i2c_long_if(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) +{ + /* + * INIT_I2C_LONG_IF opcode: 0x9A ('') + * + * offset (8 bit): opcode + * offset + 1 (8 bit): DCB I2C table entry index + * offset + 2 (8 bit): I2C slave address + * offset + 3 (16 bit): I2C register + * offset + 5 (8 bit): mask + * offset + 6 (8 bit): data + * + * Read the register given by "I2C register" on the device addressed + * by "I2C slave address" on the I2C bus given by "DCB I2C table + * entry index". Compare the result AND "mask" to "data". + * If they're not equal, skip subsequent opcodes until condition is + * inverted (INIT_NOT), or we hit INIT_RESUME + */ + + uint8_t i2c_index = bios->data[offset + 1]; + uint8_t i2c_address = bios->data[offset + 2] >> 1; + uint8_t reglo = bios->data[offset + 3]; + uint8_t reghi = bios->data[offset + 4]; + uint8_t mask = bios->data[offset + 5]; + uint8_t data = bios->data[offset + 6]; + struct nouveau_i2c_chan *chan; + uint8_t buf0[2] = { reghi, reglo }; + uint8_t buf1[1]; + struct i2c_msg msg[2] = { + { i2c_address, 0, 1, buf0 }, + { i2c_address, I2C_M_RD, 1, buf1 }, + }; + int ret; + + /* no execute check by design */ + + BIOSLOG(bios, "0x%04X: DCBI2CIndex: 0x%02X, I2CAddress: 0x%02X\n", + offset, i2c_index, i2c_address); + + chan = init_i2c_device_find(bios->dev, i2c_index); + if (!chan) + return -ENODEV; + + + ret = i2c_transfer(&chan->adapter, msg, 2); + if (ret < 0) { + BIOSLOG(bios, "0x%04X: I2CReg: 0x%02X:0x%02X, Value: [no device], " + "Mask: 0x%02X, Data: 0x%02X\n", + offset, reghi, reglo, mask, data); + iexec->execute = 0; + return 7; + } + + BIOSLOG(bios, "0x%04X: I2CReg: 0x%02X:0x%02X, Value: 0x%02X, " + "Mask: 0x%02X, Data: 0x%02X\n", + offset, reghi, reglo, buf1[0], mask, data); + + iexec->execute = ((buf1[0] & mask) == data); + + return 7; +} + static struct init_tbl_entry itbl_entry[] = { /* command name , id , length , offset , mult , command handler */ /* INIT_PROG (0x31, 15, 10, 4) removed due to no example of use */ @@ -3516,9 +3664,11 @@ static struct init_tbl_entry itbl_entry[] = { { "INIT_ZM_CR" , 0x53, init_zm_cr }, { "INIT_ZM_CR_GROUP" , 0x54, init_zm_cr_group }, { "INIT_CONDITION_TIME" , 0x56, init_condition_time }, + { "INIT_LTIME" , 0x57, init_ltime }, { "INIT_ZM_REG_SEQUENCE" , 0x58, init_zm_reg_sequence }, /* INIT_INDIRECT_REG (0x5A, 7, 0, 0) removed due to no example of use */ { "INIT_SUB_DIRECT" , 0x5B, init_sub_direct }, + { "INIT_I2C_IF" , 0x5E, init_i2c_if }, { "INIT_COPY_NV_REG" , 0x5F, init_copy_nv_reg }, { "INIT_ZM_INDEX_IO" , 0x62, init_zm_index_io }, { "INIT_COMPUTE_MEM" , 0x63, init_compute_mem }, @@ -3552,6 +3702,7 @@ static struct init_tbl_entry itbl_entry[] = { { "INIT_97" , 0x97, init_97 }, { "INIT_AUXCH" , 0x98, init_auxch }, { "INIT_ZM_AUXCH" , 0x99, init_zm_auxch }, + { "INIT_I2C_LONG_IF" , 0x9A, init_i2c_long_if }, { NULL , 0 , NULL } }; @@ -4410,7 +4561,7 @@ nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent, bios->display.script_table_ptr, table[2], table[3], table[0] >= 0x21); if (!otable) { - NV_ERROR(dev, "Couldn't find matching output script table\n"); + NV_DEBUG_KMS(dev, "failed to match any output table\n"); return 1; } @@ -4467,7 +4618,7 @@ nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent, if (script) script = clkcmptable(bios, script, pxclk); if (!script) { - NV_ERROR(dev, "clock script 0 not found\n"); + NV_DEBUG_KMS(dev, "clock script 0 not found\n"); return 1; } @@ -4826,7 +4977,7 @@ int get_pll_limits(struct drm_device *dev, uint32_t limit_match, struct pll_lims pll_lim->min_p = record[12]; pll_lim->max_p = record[13]; /* where did this go to?? */ - if (limit_match == 0x00614100 || limit_match == 0x00614900) + if ((entry[0] & 0xf0) == 0x80) pll_lim->refclk = 27000; else pll_lim->refclk = 100000; @@ -5852,7 +6003,7 @@ static void fabricate_vga_output(struct dcb_table *dcb, int i2c, int heads) entry->i2c_index = i2c; entry->heads = heads; entry->location = DCB_LOC_ON_CHIP; - /* "or" mostly unused in early gen crt modesetting, 0 is fine */ + entry->or = 1; } static void fabricate_dvi_i_output(struct dcb_table *dcb, bool twoHeads) @@ -5980,7 +6131,13 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb, } break; case OUTPUT_TMDS: - entry->tmdsconf.sor.link = (conf & 0x00000030) >> 4; + if (dcb->version >= 0x40) + entry->tmdsconf.sor.link = (conf & 0x00000030) >> 4; + else if (dcb->version >= 0x30) + entry->tmdsconf.slave_addr = (conf & 0x00000700) >> 8; + else if (dcb->version >= 0x22) + entry->tmdsconf.slave_addr = (conf & 0x00000070) >> 4; + break; case 0xe: /* weird g80 mobile type that "nv" treats as a terminator */ @@ -6270,6 +6427,19 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios, bool twoHeads) dcb->i2c_table = &bios->data[i2ctabptr]; if (dcb->version >= 0x30) dcb->i2c_default_indices = dcb->i2c_table[4]; + + /* + * Parse the "management" I2C bus, used for hardware + * monitoring and some external TMDS transmitters. + */ + if (dcb->version >= 0x22) { + int idx = (dcb->version >= 0x40 ? + dcb->i2c_default_indices & 0xf : + 2); + + read_dcb_i2c_entry(dev, dcb->version, dcb->i2c_table, + idx, &dcb->i2c[idx]); + } } if (entries > DCB_MAX_NUM_ENTRIES) diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h index 024458a8d06..fd14dfd3d78 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.h +++ b/drivers/gpu/drm/nouveau/nouveau_bios.h @@ -131,6 +131,7 @@ struct dcb_entry { } dpconf; struct { struct sor_conf sor; + int slave_addr; } tmdsconf; }; bool i2c_upper_default; diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index 3ca8343c15d..84f85183d04 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -51,9 +51,6 @@ nouveau_bo_del_ttm(struct ttm_buffer_object *bo) if (nvbo->tile) nv10_mem_expire_tiling(dev, nvbo->tile, NULL); - spin_lock(&dev_priv->ttm.bo_list_lock); - list_del(&nvbo->head); - spin_unlock(&dev_priv->ttm.bo_list_lock); kfree(nvbo); } @@ -166,9 +163,6 @@ nouveau_bo_new(struct drm_device *dev, struct nouveau_channel *chan, } nvbo->channel = NULL; - spin_lock(&dev_priv->ttm.bo_list_lock); - list_add_tail(&nvbo->head, &dev_priv->ttm.bo_list); - spin_unlock(&dev_priv->ttm.bo_list_lock); *pnvbo = nvbo; return 0; } diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 734e92635e8..b1b22baf142 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -37,12 +37,6 @@ #include "nouveau_connector.h" #include "nouveau_hw.h" -static inline struct drm_encoder_slave_funcs * -get_slave_funcs(struct nouveau_encoder *enc) -{ - return to_encoder_slave(to_drm_encoder(enc))->slave_funcs; -} - static struct nouveau_encoder * find_encoder_by_type(struct drm_connector *connector, int type) { @@ -360,6 +354,7 @@ nouveau_connector_set_property(struct drm_connector *connector, { struct nouveau_connector *nv_connector = nouveau_connector(connector); struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder; + struct drm_encoder *encoder = to_drm_encoder(nv_encoder); struct drm_device *dev = connector->dev; int ret; @@ -432,8 +427,8 @@ nouveau_connector_set_property(struct drm_connector *connector, } if (nv_encoder && nv_encoder->dcb->type == OUTPUT_TV) - return get_slave_funcs(nv_encoder)-> - set_property(to_drm_encoder(nv_encoder), connector, property, value); + return get_slave_funcs(encoder)->set_property( + encoder, connector, property, value); return -EINVAL; } @@ -545,6 +540,7 @@ nouveau_connector_get_modes(struct drm_connector *connector) struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_connector *nv_connector = nouveau_connector(connector); struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder; + struct drm_encoder *encoder = to_drm_encoder(nv_encoder); int ret = 0; /* destroy the native mode, the attached monitor could have changed. @@ -580,8 +576,7 @@ nouveau_connector_get_modes(struct drm_connector *connector) } if (nv_encoder->dcb->type == OUTPUT_TV) - ret = get_slave_funcs(nv_encoder)-> - get_modes(to_drm_encoder(nv_encoder), connector); + ret = get_slave_funcs(encoder)->get_modes(encoder, connector); if (nv_connector->dcb->type == DCB_CONNECTOR_LVDS || nv_connector->dcb->type == DCB_CONNECTOR_eDP) @@ -597,6 +592,7 @@ nouveau_connector_mode_valid(struct drm_connector *connector, struct drm_nouveau_private *dev_priv = connector->dev->dev_private; struct nouveau_connector *nv_connector = nouveau_connector(connector); struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder; + struct drm_encoder *encoder = to_drm_encoder(nv_encoder); unsigned min_clock = 25000, max_clock = min_clock; unsigned clock = mode->clock; @@ -623,8 +619,7 @@ nouveau_connector_mode_valid(struct drm_connector *connector, max_clock = 350000; break; case OUTPUT_TV: - return get_slave_funcs(nv_encoder)-> - mode_valid(to_drm_encoder(nv_encoder), mode); + return get_slave_funcs(encoder)->mode_valid(encoder, mode); case OUTPUT_DP: if (nv_encoder->dp.link_bw == DP_LINK_BW_2_7) max_clock = nv_encoder->dp.link_nr * 270000; diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 74e6b4ed12c..2e11fd65b4d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -84,16 +84,16 @@ nouveau_user_framebuffer_create(struct drm_device *dev, gem = drm_gem_object_lookup(dev, file_priv, mode_cmd->handle); if (!gem) - return NULL; + return ERR_PTR(-ENOENT); nouveau_fb = kzalloc(sizeof(struct nouveau_framebuffer), GFP_KERNEL); if (!nouveau_fb) - return NULL; + return ERR_PTR(-ENOMEM); ret = nouveau_framebuffer_init(dev, nouveau_fb, mode_cmd, nouveau_gem_object(gem)); if (ret) { drm_gem_object_unreference(gem); - return NULL; + return ERR_PTR(ret); } return &nouveau_fb->base; diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 33742b11188..8a1b188b4cd 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -572,47 +572,64 @@ out: return ret ? ret : (stat & NV50_AUXCH_STAT_REPLY); } -int -nouveau_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode, - uint8_t write_byte, uint8_t *read_byte) +static int +nouveau_dp_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { - struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; - struct nouveau_i2c_chan *auxch = (struct nouveau_i2c_chan *)adapter; + struct nouveau_i2c_chan *auxch = (struct nouveau_i2c_chan *)adap; struct drm_device *dev = auxch->dev; - int ret = 0, cmd, addr = algo_data->address; - uint8_t *buf; - - if (mode == MODE_I2C_READ) { - cmd = AUX_I2C_READ; - buf = read_byte; - } else { - cmd = (mode & MODE_I2C_READ) ? AUX_I2C_READ : AUX_I2C_WRITE; - buf = &write_byte; - } + struct i2c_msg *msg = msgs; + int ret, mcnt = num; - if (!(mode & MODE_I2C_STOP)) - cmd |= AUX_I2C_MOT; + while (mcnt--) { + u8 remaining = msg->len; + u8 *ptr = msg->buf; - if (mode & MODE_I2C_START) - return 1; + while (remaining) { + u8 cnt = (remaining > 16) ? 16 : remaining; + u8 cmd; - for (;;) { - ret = nouveau_dp_auxch(auxch, cmd, addr, buf, 1); - if (ret < 0) - return ret; - - switch (ret & NV50_AUXCH_STAT_REPLY_I2C) { - case NV50_AUXCH_STAT_REPLY_I2C_ACK: - return 1; - case NV50_AUXCH_STAT_REPLY_I2C_NACK: - return -EREMOTEIO; - case NV50_AUXCH_STAT_REPLY_I2C_DEFER: - udelay(100); - break; - default: - NV_ERROR(dev, "invalid auxch status: 0x%08x\n", ret); - return -EREMOTEIO; + if (msg->flags & I2C_M_RD) + cmd = AUX_I2C_READ; + else + cmd = AUX_I2C_WRITE; + + if (mcnt || remaining > 16) + cmd |= AUX_I2C_MOT; + + ret = nouveau_dp_auxch(auxch, cmd, msg->addr, ptr, cnt); + if (ret < 0) + return ret; + + switch (ret & NV50_AUXCH_STAT_REPLY_I2C) { + case NV50_AUXCH_STAT_REPLY_I2C_ACK: + break; + case NV50_AUXCH_STAT_REPLY_I2C_NACK: + return -EREMOTEIO; + case NV50_AUXCH_STAT_REPLY_I2C_DEFER: + udelay(100); + continue; + default: + NV_ERROR(dev, "bad auxch reply: 0x%08x\n", ret); + return -EREMOTEIO; + } + + ptr += cnt; + remaining -= cnt; } + + msg++; } + + return num; +} + +static u32 +nouveau_dp_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; } +const struct i2c_algorithm nouveau_dp_i2c_algo = { + .master_xfer = nouveau_dp_i2c_xfer, + .functionality = nouveau_dp_i2c_func +}; diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index e15db15dca7..e424bf74d70 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -410,7 +410,7 @@ enum nv04_fp_display_regs { struct nv04_crtc_reg { unsigned char MiscOutReg; /* */ - uint8_t CRTC[0x9f]; + uint8_t CRTC[0xa0]; uint8_t CR58[0x10]; uint8_t Sequencer[5]; uint8_t Graphics[9]; @@ -509,6 +509,7 @@ enum nouveau_card_type { NV_30 = 0x30, NV_40 = 0x40, NV_50 = 0x50, + NV_C0 = 0xc0, }; struct drm_nouveau_private { @@ -536,8 +537,6 @@ struct drm_nouveau_private { struct drm_global_reference mem_global_ref; struct ttm_bo_global_ref bo_global_ref; struct ttm_bo_device bdev; - spinlock_t bo_list_lock; - struct list_head bo_list; atomic_t validate_sequence; } ttm; @@ -931,6 +930,10 @@ extern void nv40_fb_set_region_tiling(struct drm_device *, int, uint32_t, extern int nv50_fb_init(struct drm_device *); extern void nv50_fb_takedown(struct drm_device *); +/* nvc0_fb.c */ +extern int nvc0_fb_init(struct drm_device *); +extern void nvc0_fb_takedown(struct drm_device *); + /* nv04_fifo.c */ extern int nv04_fifo_init(struct drm_device *); extern void nv04_fifo_disable(struct drm_device *); @@ -968,6 +971,20 @@ extern void nv50_fifo_destroy_context(struct nouveau_channel *); extern int nv50_fifo_load_context(struct nouveau_channel *); extern int nv50_fifo_unload_context(struct drm_device *); +/* nvc0_fifo.c */ +extern int nvc0_fifo_init(struct drm_device *); +extern void nvc0_fifo_takedown(struct drm_device *); +extern void nvc0_fifo_disable(struct drm_device *); +extern void nvc0_fifo_enable(struct drm_device *); +extern bool nvc0_fifo_reassign(struct drm_device *, bool); +extern bool nvc0_fifo_cache_flush(struct drm_device *); +extern bool nvc0_fifo_cache_pull(struct drm_device *, bool); +extern int nvc0_fifo_channel_id(struct drm_device *); +extern int nvc0_fifo_create_context(struct nouveau_channel *); +extern void nvc0_fifo_destroy_context(struct nouveau_channel *); +extern int nvc0_fifo_load_context(struct nouveau_channel *); +extern int nvc0_fifo_unload_context(struct drm_device *); + /* nv04_graph.c */ extern struct nouveau_pgraph_object_class nv04_graph_grclass[]; extern int nv04_graph_init(struct drm_device *); @@ -1032,6 +1049,16 @@ extern int nv50_graph_unload_context(struct drm_device *); extern void nv50_graph_context_switch(struct drm_device *); extern int nv50_grctx_init(struct nouveau_grctx *); +/* nvc0_graph.c */ +extern int nvc0_graph_init(struct drm_device *); +extern void nvc0_graph_takedown(struct drm_device *); +extern void nvc0_graph_fifo_access(struct drm_device *, bool); +extern struct nouveau_channel *nvc0_graph_channel(struct drm_device *); +extern int nvc0_graph_create_context(struct nouveau_channel *); +extern void nvc0_graph_destroy_context(struct nouveau_channel *); +extern int nvc0_graph_load_context(struct nouveau_channel *); +extern int nvc0_graph_unload_context(struct drm_device *); + /* nv04_instmem.c */ extern int nv04_instmem_init(struct drm_device *); extern void nv04_instmem_takedown(struct drm_device *); @@ -1058,6 +1085,18 @@ extern void nv50_instmem_flush(struct drm_device *); extern void nv84_instmem_flush(struct drm_device *); extern void nv50_vm_flush(struct drm_device *, int engine); +/* nvc0_instmem.c */ +extern int nvc0_instmem_init(struct drm_device *); +extern void nvc0_instmem_takedown(struct drm_device *); +extern int nvc0_instmem_suspend(struct drm_device *); +extern void nvc0_instmem_resume(struct drm_device *); +extern int nvc0_instmem_populate(struct drm_device *, struct nouveau_gpuobj *, + uint32_t *size); +extern void nvc0_instmem_clear(struct drm_device *, struct nouveau_gpuobj *); +extern int nvc0_instmem_bind(struct drm_device *, struct nouveau_gpuobj *); +extern int nvc0_instmem_unbind(struct drm_device *, struct nouveau_gpuobj *); +extern void nvc0_instmem_flush(struct drm_device *); + /* nv04_mc.c */ extern int nv04_mc_init(struct drm_device *); extern void nv04_mc_takedown(struct drm_device *); diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h index a1a0d48ae70..7c82d68bc15 100644 --- a/drivers/gpu/drm/nouveau/nouveau_encoder.h +++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h @@ -71,6 +71,12 @@ static inline struct drm_encoder *to_drm_encoder(struct nouveau_encoder *enc) return &enc->base.base; } +static inline struct drm_encoder_slave_funcs * +get_slave_funcs(struct drm_encoder *enc) +{ + return to_encoder_slave(enc)->slave_funcs; +} + struct nouveau_connector * nouveau_encoder_connector_get(struct nouveau_encoder *encoder); int nv50_sor_create(struct drm_connector *, struct dcb_entry *); diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c index 099f637264a..dbd30b2e43f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c @@ -281,6 +281,8 @@ nouveau_fbcon_create(struct nouveau_fbdev *nfbdev, if (dev_priv->channel && !nouveau_nofbaccel) { switch (dev_priv->card_type) { + case NV_C0: + break; case NV_50: nv50_fbcon_accel_init(info); info->fbops = &nv50_fbcon_ops; diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c index 547f2c24c1e..0f417ac1b69 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gem.c +++ b/drivers/gpu/drm/nouveau/nouveau_gem.c @@ -284,7 +284,7 @@ retry: if (!gem) { NV_ERROR(dev, "Unknown handle 0x%08x\n", b->handle); validate_fini(op, NULL); - return -EINVAL; + return -ENOENT; } nvbo = gem->driver_private; @@ -759,7 +759,7 @@ nouveau_gem_ioctl_cpu_prep(struct drm_device *dev, void *data, gem = drm_gem_object_lookup(dev, file_priv, req->handle); if (!gem) - return ret; + return -ENOENT; nvbo = nouveau_gem_object(gem); if (nvbo->cpu_filp) { @@ -797,7 +797,7 @@ nouveau_gem_ioctl_cpu_fini(struct drm_device *dev, void *data, gem = drm_gem_object_lookup(dev, file_priv, req->handle); if (!gem) - return ret; + return -ENOENT; nvbo = nouveau_gem_object(gem); if (nvbo->cpu_filp != file_priv) @@ -822,7 +822,7 @@ nouveau_gem_ioctl_info(struct drm_device *dev, void *data, gem = drm_gem_object_lookup(dev, file_priv, req->handle); if (!gem) - return -EINVAL; + return -ENOENT; ret = nouveau_gem_info(gem, req); drm_gem_object_unreference_unlocked(gem); diff --git a/drivers/gpu/drm/nouveau/nouveau_hw.c b/drivers/gpu/drm/nouveau/nouveau_hw.c index 7855b35effc..7b613682e40 100644 --- a/drivers/gpu/drm/nouveau/nouveau_hw.c +++ b/drivers/gpu/drm/nouveau/nouveau_hw.c @@ -865,8 +865,12 @@ nv_save_state_ext(struct drm_device *dev, int head, rd_cio_state(dev, head, regp, NV_CIO_CRE_FF_INDEX); rd_cio_state(dev, head, regp, NV_CIO_CRE_FFLWM__INDEX); rd_cio_state(dev, head, regp, NV_CIO_CRE_21); - if (dev_priv->card_type >= NV_30) + + if (dev_priv->card_type >= NV_30) { rd_cio_state(dev, head, regp, NV_CIO_CRE_47); + rd_cio_state(dev, head, regp, 0x9f); + } + rd_cio_state(dev, head, regp, NV_CIO_CRE_49); rd_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX); rd_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR1_INDEX); @@ -971,8 +975,11 @@ nv_load_state_ext(struct drm_device *dev, int head, wr_cio_state(dev, head, regp, NV_CIO_CRE_ENH_INDEX); wr_cio_state(dev, head, regp, NV_CIO_CRE_FF_INDEX); wr_cio_state(dev, head, regp, NV_CIO_CRE_FFLWM__INDEX); - if (dev_priv->card_type >= NV_30) + + if (dev_priv->card_type >= NV_30) { wr_cio_state(dev, head, regp, NV_CIO_CRE_47); + wr_cio_state(dev, head, regp, 0x9f); + } wr_cio_state(dev, head, regp, NV_CIO_CRE_49); wr_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX); diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.c b/drivers/gpu/drm/nouveau/nouveau_i2c.c index cb0cb34440c..0bd407ca3d4 100644 --- a/drivers/gpu/drm/nouveau/nouveau_i2c.c +++ b/drivers/gpu/drm/nouveau/nouveau_i2c.c @@ -163,7 +163,7 @@ nouveau_i2c_init(struct drm_device *dev, struct dcb_i2c_entry *entry, int index) if (entry->chan) return -EEXIST; - if (dev_priv->card_type == NV_50 && entry->read >= NV50_I2C_PORTS) { + if (dev_priv->card_type == NV_C0 && entry->read >= NV50_I2C_PORTS) { NV_ERROR(dev, "unknown i2c port %d\n", entry->read); return -EINVAL; } @@ -174,26 +174,26 @@ nouveau_i2c_init(struct drm_device *dev, struct dcb_i2c_entry *entry, int index) switch (entry->port_type) { case 0: - i2c->algo.bit.setsda = nv04_i2c_setsda; - i2c->algo.bit.setscl = nv04_i2c_setscl; - i2c->algo.bit.getsda = nv04_i2c_getsda; - i2c->algo.bit.getscl = nv04_i2c_getscl; + i2c->bit.setsda = nv04_i2c_setsda; + i2c->bit.setscl = nv04_i2c_setscl; + i2c->bit.getsda = nv04_i2c_getsda; + i2c->bit.getscl = nv04_i2c_getscl; i2c->rd = entry->read; i2c->wr = entry->write; break; case 4: - i2c->algo.bit.setsda = nv4e_i2c_setsda; - i2c->algo.bit.setscl = nv4e_i2c_setscl; - i2c->algo.bit.getsda = nv4e_i2c_getsda; - i2c->algo.bit.getscl = nv4e_i2c_getscl; + i2c->bit.setsda = nv4e_i2c_setsda; + i2c->bit.setscl = nv4e_i2c_setscl; + i2c->bit.getsda = nv4e_i2c_getsda; + i2c->bit.getscl = nv4e_i2c_getscl; i2c->rd = 0x600800 + entry->read; i2c->wr = 0x600800 + entry->write; break; case 5: - i2c->algo.bit.setsda = nv50_i2c_setsda; - i2c->algo.bit.setscl = nv50_i2c_setscl; - i2c->algo.bit.getsda = nv50_i2c_getsda; - i2c->algo.bit.getscl = nv50_i2c_getscl; + i2c->bit.setsda = nv50_i2c_setsda; + i2c->bit.setscl = nv50_i2c_setscl; + i2c->bit.getsda = nv50_i2c_getsda; + i2c->bit.getscl = nv50_i2c_getscl; i2c->rd = nv50_i2c_port[entry->read]; i2c->wr = i2c->rd; break; @@ -216,17 +216,14 @@ nouveau_i2c_init(struct drm_device *dev, struct dcb_i2c_entry *entry, int index) i2c_set_adapdata(&i2c->adapter, i2c); if (entry->port_type < 6) { - i2c->adapter.algo_data = &i2c->algo.bit; - i2c->algo.bit.udelay = 40; - i2c->algo.bit.timeout = usecs_to_jiffies(5000); - i2c->algo.bit.data = i2c; + i2c->adapter.algo_data = &i2c->bit; + i2c->bit.udelay = 40; + i2c->bit.timeout = usecs_to_jiffies(5000); + i2c->bit.data = i2c; ret = i2c_bit_add_bus(&i2c->adapter); } else { - i2c->adapter.algo_data = &i2c->algo.dp; - i2c->algo.dp.running = false; - i2c->algo.dp.address = 0; - i2c->algo.dp.aux_ch = nouveau_dp_i2c_aux_ch; - ret = i2c_dp_aux_add_bus(&i2c->adapter); + i2c->adapter.algo = &nouveau_dp_i2c_algo; + ret = i2c_add_adapter(&i2c->adapter); } if (ret) { diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.h b/drivers/gpu/drm/nouveau/nouveau_i2c.h index 6dd2f8713cd..f71cb32f757 100644 --- a/drivers/gpu/drm/nouveau/nouveau_i2c.h +++ b/drivers/gpu/drm/nouveau/nouveau_i2c.h @@ -33,10 +33,7 @@ struct dcb_i2c_entry; struct nouveau_i2c_chan { struct i2c_adapter adapter; struct drm_device *dev; - union { - struct i2c_algo_bit_data bit; - struct i2c_algo_dp_aux_data dp; - } algo; + struct i2c_algo_bit_data bit; unsigned rd; unsigned wr; unsigned data; @@ -49,7 +46,6 @@ bool nouveau_probe_i2c_addr(struct nouveau_i2c_chan *i2c, int addr); int nouveau_i2c_identify(struct drm_device *dev, const char *what, struct i2c_board_info *info, int index); -int nouveau_dp_i2c_aux_ch(struct i2c_adapter *, int mode, uint8_t write_byte, - uint8_t *read_byte); +extern const struct i2c_algorithm nouveau_dp_i2c_algo; #endif /* __NOUVEAU_I2C_H__ */ diff --git a/drivers/gpu/drm/nouveau/nouveau_irq.c b/drivers/gpu/drm/nouveau/nouveau_irq.c index 53360f15606..794b0ee30cf 100644 --- a/drivers/gpu/drm/nouveau/nouveau_irq.c +++ b/drivers/gpu/drm/nouveau/nouveau_irq.c @@ -49,7 +49,7 @@ nouveau_irq_preinstall(struct drm_device *dev) /* Master disable */ nv_wr32(dev, NV03_PMC_INTR_EN_0, 0); - if (dev_priv->card_type == NV_50) { + if (dev_priv->card_type >= NV_50) { INIT_WORK(&dev_priv->irq_work, nv50_display_irq_handler_bh); INIT_WORK(&dev_priv->hpd_work, nv50_display_irq_hotplug_bh); INIT_LIST_HEAD(&dev_priv->vbl_waiting); @@ -586,11 +586,11 @@ nouveau_pgraph_irq_handler(struct drm_device *dev) } if (status & NV_PGRAPH_INTR_CONTEXT_SWITCH) { - nouveau_pgraph_intr_context_switch(dev); - status &= ~NV_PGRAPH_INTR_CONTEXT_SWITCH; nv_wr32(dev, NV03_PGRAPH_INTR, NV_PGRAPH_INTR_CONTEXT_SWITCH); + + nouveau_pgraph_intr_context_switch(dev); } if (status) { diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c index a9f36ab256b..9689d414768 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mem.c +++ b/drivers/gpu/drm/nouveau/nouveau_mem.c @@ -320,7 +320,8 @@ nouveau_mem_detect(struct drm_device *dev) if (dev_priv->card_type < NV_50) { dev_priv->vram_size = nv_rd32(dev, NV04_PFB_FIFO_DATA); dev_priv->vram_size &= NV10_PFB_FIFO_DATA_RAM_AMOUNT_MB_MASK; - } else { + } else + if (dev_priv->card_type < NV_C0) { dev_priv->vram_size = nv_rd32(dev, NV04_PFB_FIFO_DATA); dev_priv->vram_size |= (dev_priv->vram_size & 0xff) << 32; dev_priv->vram_size &= 0xffffffff00ll; @@ -328,6 +329,9 @@ nouveau_mem_detect(struct drm_device *dev) dev_priv->vram_sys_base = nv_rd32(dev, 0x100e10); dev_priv->vram_sys_base <<= 12; } + } else { + dev_priv->vram_size = nv_rd32(dev, 0x10f20c) << 20; + dev_priv->vram_size *= nv_rd32(dev, 0x121c74); } NV_INFO(dev, "Detected %dMiB VRAM\n", (int)(dev_priv->vram_size >> 20)); @@ -351,7 +355,7 @@ nouveau_mem_reset_agp(struct drm_device *dev) /* First of all, disable fast writes, otherwise if it's * already enabled in the AGP bridge and we disable the card's * AGP controller we might be locking ourselves out of it. */ - if (dev->agp->acquired) { + if (nv_rd32(dev, NV04_PBUS_PCI_NV_19) & PCI_AGP_COMMAND_FW) { struct drm_agp_info info; struct drm_agp_mode mode; @@ -359,7 +363,7 @@ nouveau_mem_reset_agp(struct drm_device *dev) if (ret) return ret; - mode.mode = info.mode & ~0x10; + mode.mode = info.mode & ~PCI_AGP_COMMAND_FW; ret = drm_agp_enable(dev, mode); if (ret) return ret; @@ -405,6 +409,8 @@ nouveau_mem_init_agp(struct drm_device *dev) } } + nouveau_mem_reset_agp(dev); + ret = drm_agp_info(dev, &info); if (ret) { NV_ERROR(dev, "Unable to get AGP info: %d\n", ret); @@ -459,8 +465,6 @@ nouveau_mem_init(struct drm_device *dev) return ret; } - INIT_LIST_HEAD(&dev_priv->ttm.bo_list); - spin_lock_init(&dev_priv->ttm.bo_list_lock); spin_lock_init(&dev_priv->tile.lock); dev_priv->fb_available_size = dev_priv->vram_size; @@ -494,7 +498,6 @@ nouveau_mem_init(struct drm_device *dev) /* GART */ #if !defined(__powerpc__) && !defined(__ia64__) if (drm_device_is_agp(dev) && dev->agp && !nouveau_noagp) { - nouveau_mem_reset_agp(dev); ret = nouveau_mem_init_agp(dev); if (ret) NV_ERROR(dev, "Error initialising AGP: %d\n", ret); diff --git a/drivers/gpu/drm/nouveau/nouveau_reg.h b/drivers/gpu/drm/nouveau/nouveau_reg.h index 9c1056cb8a9..21a6e453b97 100644 --- a/drivers/gpu/drm/nouveau/nouveau_reg.h +++ b/drivers/gpu/drm/nouveau/nouveau_reg.h @@ -220,28 +220,21 @@ # define NV_PGRAPH_INTR_ERROR (1<<20) #define NV10_PGRAPH_CTX_CONTROL 0x00400144 #define NV10_PGRAPH_CTX_USER 0x00400148 -#define NV10_PGRAPH_CTX_SWITCH1 0x0040014C -#define NV10_PGRAPH_CTX_SWITCH2 0x00400150 -#define NV10_PGRAPH_CTX_SWITCH3 0x00400154 -#define NV10_PGRAPH_CTX_SWITCH4 0x00400158 -#define NV10_PGRAPH_CTX_SWITCH5 0x0040015C +#define NV10_PGRAPH_CTX_SWITCH(i) (0x0040014C + 0x4*(i)) #define NV04_PGRAPH_CTX_SWITCH1 0x00400160 -#define NV10_PGRAPH_CTX_CACHE1 0x00400160 +#define NV10_PGRAPH_CTX_CACHE(i, j) (0x00400160 \ + + 0x4*(i) + 0x20*(j)) #define NV04_PGRAPH_CTX_SWITCH2 0x00400164 #define NV04_PGRAPH_CTX_SWITCH3 0x00400168 #define NV04_PGRAPH_CTX_SWITCH4 0x0040016C #define NV04_PGRAPH_CTX_CONTROL 0x00400170 #define NV04_PGRAPH_CTX_USER 0x00400174 #define NV04_PGRAPH_CTX_CACHE1 0x00400180 -#define NV10_PGRAPH_CTX_CACHE2 0x00400180 #define NV03_PGRAPH_CTX_CONTROL 0x00400190 #define NV03_PGRAPH_CTX_USER 0x00400194 #define NV04_PGRAPH_CTX_CACHE2 0x004001A0 -#define NV10_PGRAPH_CTX_CACHE3 0x004001A0 #define NV04_PGRAPH_CTX_CACHE3 0x004001C0 -#define NV10_PGRAPH_CTX_CACHE4 0x004001C0 #define NV04_PGRAPH_CTX_CACHE4 0x004001E0 -#define NV10_PGRAPH_CTX_CACHE5 0x004001E0 #define NV40_PGRAPH_CTXCTL_0304 0x00400304 #define NV40_PGRAPH_CTXCTL_0304_XFER_CTX 0x00000001 #define NV40_PGRAPH_CTXCTL_UCODE_STAT 0x00400308 @@ -356,9 +349,12 @@ #define NV04_PGRAPH_FFINTFC_ST2 0x00400754 #define NV10_PGRAPH_RDI_DATA 0x00400754 #define NV04_PGRAPH_DMA_PITCH 0x00400760 -#define NV10_PGRAPH_FFINTFC_ST2 0x00400764 +#define NV10_PGRAPH_FFINTFC_FIFO_PTR 0x00400760 #define NV04_PGRAPH_DVD_COLORFMT 0x00400764 +#define NV10_PGRAPH_FFINTFC_ST2 0x00400764 #define NV04_PGRAPH_SCALED_FORMAT 0x00400768 +#define NV10_PGRAPH_FFINTFC_ST2_DL 0x00400768 +#define NV10_PGRAPH_FFINTFC_ST2_DH 0x0040076c #define NV10_PGRAPH_DMA_PITCH 0x00400770 #define NV10_PGRAPH_DVD_COLORFMT 0x00400774 #define NV10_PGRAPH_SCALED_FORMAT 0x00400778 diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index ee3729e7823..989322be372 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -359,6 +359,54 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->gpio.set = nv50_gpio_set; engine->gpio.irq_enable = nv50_gpio_irq_enable; break; + case 0xC0: + engine->instmem.init = nvc0_instmem_init; + engine->instmem.takedown = nvc0_instmem_takedown; + engine->instmem.suspend = nvc0_instmem_suspend; + engine->instmem.resume = nvc0_instmem_resume; + engine->instmem.populate = nvc0_instmem_populate; + engine->instmem.clear = nvc0_instmem_clear; + engine->instmem.bind = nvc0_instmem_bind; + engine->instmem.unbind = nvc0_instmem_unbind; + engine->instmem.flush = nvc0_instmem_flush; + engine->mc.init = nv50_mc_init; + engine->mc.takedown = nv50_mc_takedown; + engine->timer.init = nv04_timer_init; + engine->timer.read = nv04_timer_read; + engine->timer.takedown = nv04_timer_takedown; + engine->fb.init = nvc0_fb_init; + engine->fb.takedown = nvc0_fb_takedown; + engine->graph.grclass = NULL; //nvc0_graph_grclass; + engine->graph.init = nvc0_graph_init; + engine->graph.takedown = nvc0_graph_takedown; + engine->graph.fifo_access = nvc0_graph_fifo_access; + engine->graph.channel = nvc0_graph_channel; + engine->graph.create_context = nvc0_graph_create_context; + engine->graph.destroy_context = nvc0_graph_destroy_context; + engine->graph.load_context = nvc0_graph_load_context; + engine->graph.unload_context = nvc0_graph_unload_context; + engine->fifo.channels = 128; + engine->fifo.init = nvc0_fifo_init; + engine->fifo.takedown = nvc0_fifo_takedown; + engine->fifo.disable = nvc0_fifo_disable; + engine->fifo.enable = nvc0_fifo_enable; + engine->fifo.reassign = nvc0_fifo_reassign; + engine->fifo.channel_id = nvc0_fifo_channel_id; + engine->fifo.create_context = nvc0_fifo_create_context; + engine->fifo.destroy_context = nvc0_fifo_destroy_context; + engine->fifo.load_context = nvc0_fifo_load_context; + engine->fifo.unload_context = nvc0_fifo_unload_context; + engine->display.early_init = nv50_display_early_init; + engine->display.late_takedown = nv50_display_late_takedown; + engine->display.create = nv50_display_create; + engine->display.init = nv50_display_init; + engine->display.destroy = nv50_display_destroy; + engine->gpio.init = nv50_gpio_init; + engine->gpio.takedown = nouveau_stub_takedown; + engine->gpio.get = nv50_gpio_get; + engine->gpio.set = nv50_gpio_set; + engine->gpio.irq_enable = nv50_gpio_irq_enable; + break; default: NV_ERROR(dev, "NV%02x unsupported\n", dev_priv->chipset); return 1; @@ -739,8 +787,10 @@ int nouveau_load(struct drm_device *dev, unsigned long flags) int ret; dev_priv = kzalloc(sizeof(*dev_priv), GFP_KERNEL); - if (!dev_priv) - return -ENOMEM; + if (!dev_priv) { + ret = -ENOMEM; + goto err_out; + } dev->dev_private = dev_priv; dev_priv->dev = dev; @@ -750,8 +800,10 @@ int nouveau_load(struct drm_device *dev, unsigned long flags) dev->pci_vendor, dev->pci_device, dev->pdev->class); dev_priv->wq = create_workqueue("nouveau"); - if (!dev_priv->wq) - return -EINVAL; + if (!dev_priv->wq) { + ret = -EINVAL; + goto err_priv; + } /* resource 0 is mmio regs */ /* resource 1 is linear FB */ @@ -764,7 +816,8 @@ int nouveau_load(struct drm_device *dev, unsigned long flags) if (!dev_priv->mmio) { NV_ERROR(dev, "Unable to initialize the mmio mapping. " "Please report your setup to " DRIVER_EMAIL "\n"); - return -EINVAL; + ret = -EINVAL; + goto err_wq; } NV_DEBUG(dev, "regs mapped ok at 0x%llx\n", (unsigned long long)mmio_start_offs); @@ -810,9 +863,13 @@ int nouveau_load(struct drm_device *dev, unsigned long flags) case 0xa0: dev_priv->card_type = NV_50; break; + case 0xc0: + dev_priv->card_type = NV_C0; + break; default: NV_INFO(dev, "Unsupported chipset 0x%08x\n", reg0); - return -EINVAL; + ret = -EINVAL; + goto err_mmio; } NV_INFO(dev, "Detected an NV%2x generation card (0x%08x)\n", @@ -820,7 +877,7 @@ int nouveau_load(struct drm_device *dev, unsigned long flags) ret = nouveau_remove_conflicting_drivers(dev); if (ret) - return ret; + goto err_mmio; /* Map PRAMIN BAR, or on older cards, the aperture withing BAR0 */ if (dev_priv->card_type >= NV_40) { @@ -834,7 +891,8 @@ int nouveau_load(struct drm_device *dev, unsigned long flags) dev_priv->ramin_size); if (!dev_priv->ramin) { NV_ERROR(dev, "Failed to PRAMIN BAR"); - return -ENOMEM; + ret = -ENOMEM; + goto err_mmio; } } else { dev_priv->ramin_size = 1 * 1024 * 1024; @@ -842,7 +900,8 @@ int nouveau_load(struct drm_device *dev, unsigned long flags) dev_priv->ramin_size); if (!dev_priv->ramin) { NV_ERROR(dev, "Failed to map BAR0 PRAMIN.\n"); - return -ENOMEM; + ret = -ENOMEM; + goto err_mmio; } } @@ -857,9 +916,21 @@ int nouveau_load(struct drm_device *dev, unsigned long flags) /* For kernel modesetting, init card now and bring up fbcon */ ret = nouveau_card_init(dev); if (ret) - return ret; + goto err_ramin; return 0; + +err_ramin: + iounmap(dev_priv->ramin); +err_mmio: + iounmap(dev_priv->mmio); +err_wq: + destroy_workqueue(dev_priv->wq); +err_priv: + kfree(dev_priv); + dev->dev_private = NULL; +err_out: + return ret; } void nouveau_lastclose(struct drm_device *dev) diff --git a/drivers/gpu/drm/nouveau/nv04_crtc.c b/drivers/gpu/drm/nouveau/nv04_crtc.c index 1c20c08ce67..497df8765f2 100644 --- a/drivers/gpu/drm/nouveau/nv04_crtc.c +++ b/drivers/gpu/drm/nouveau/nv04_crtc.c @@ -542,6 +542,9 @@ nv_crtc_mode_set_regs(struct drm_crtc *crtc, struct drm_display_mode * mode) * 1 << 30 on 0x60.830), for no apparent reason */ regp->CRTC[NV_CIO_CRE_59] = off_chip_digital; + if (dev_priv->card_type >= NV_30) + regp->CRTC[0x9f] = off_chip_digital ? 0x11 : 0x1; + regp->crtc_830 = mode->crtc_vdisplay - 3; regp->crtc_834 = mode->crtc_vdisplay - 1; @@ -739,15 +742,13 @@ nv_crtc_gamma_load(struct drm_crtc *crtc) } static void -nv_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, uint32_t size) +nv_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, uint32_t start, + uint32_t size) { + int end = (start + size > 256) ? 256 : start + size, i; struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - int i; - if (size != 256) - return; - - for (i = 0; i < 256; i++) { + for (i = start; i < end; i++) { nv_crtc->lut.r[i] = r[i]; nv_crtc->lut.g[i] = g[i]; nv_crtc->lut.b[i] = b[i]; @@ -914,7 +915,7 @@ nv04_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, gem = drm_gem_object_lookup(dev, file_priv, buffer_handle); if (!gem) - return -EINVAL; + return -ENOENT; cursor = nouveau_gem_object(gem); ret = nouveau_bo_map(cursor); diff --git a/drivers/gpu/drm/nouveau/nv04_dfp.c b/drivers/gpu/drm/nouveau/nv04_dfp.c index 3311f3a8c81..a5dcf768580 100644 --- a/drivers/gpu/drm/nouveau/nv04_dfp.c +++ b/drivers/gpu/drm/nouveau/nv04_dfp.c @@ -34,6 +34,8 @@ #include "nouveau_hw.h" #include "nvreg.h" +#include "i2c/sil164.h" + #define FP_TG_CONTROL_ON (NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS | \ NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS | \ NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS) @@ -144,6 +146,36 @@ void nv04_dfp_update_fp_control(struct drm_encoder *encoder, int mode) } } +static struct drm_encoder *get_tmds_slave(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb; + struct drm_encoder *slave; + + if (dcb->type != OUTPUT_TMDS || dcb->location == DCB_LOC_ON_CHIP) + return NULL; + + /* Some BIOSes (e.g. the one in a Quadro FX1000) report several + * TMDS transmitters at the same I2C address, in the same I2C + * bus. This can still work because in that case one of them is + * always hard-wired to a reasonable configuration using straps, + * and the other one needs to be programmed. + * + * I don't think there's a way to know which is which, even the + * blob programs the one exposed via I2C for *both* heads, so + * let's do the same. + */ + list_for_each_entry(slave, &dev->mode_config.encoder_list, head) { + struct dcb_entry *slave_dcb = nouveau_encoder(slave)->dcb; + + if (slave_dcb->type == OUTPUT_TMDS && get_slave_funcs(slave) && + slave_dcb->tmdsconf.slave_addr == dcb->tmdsconf.slave_addr) + return slave; + } + + return NULL; +} + static bool nv04_dfp_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) @@ -429,6 +461,11 @@ static void nv04_dfp_commit(struct drm_encoder *encoder) else NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0x00100000); + /* Init external transmitters */ + if (get_tmds_slave(encoder)) + get_slave_funcs(get_tmds_slave(encoder))->mode_set( + encoder, &nv_encoder->mode, &nv_encoder->mode); + helper->dpms(encoder, DRM_MODE_DPMS_ON); NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n", @@ -550,10 +587,42 @@ static void nv04_dfp_destroy(struct drm_encoder *encoder) NV_DEBUG_KMS(encoder->dev, "\n"); + if (get_slave_funcs(encoder)) + get_slave_funcs(encoder)->destroy(encoder); + drm_encoder_cleanup(encoder); kfree(nv_encoder); } +static void nv04_tmds_slave_init(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb; + struct nouveau_i2c_chan *i2c = nouveau_i2c_find(dev, 2); + struct i2c_board_info info[] = { + { + .type = "sil164", + .addr = (dcb->tmdsconf.slave_addr == 0x7 ? 0x3a : 0x38), + .platform_data = &(struct sil164_encoder_params) { + SIL164_INPUT_EDGE_RISING + } + }, + { } + }; + int type; + + if (!nv_gf4_disp_arch(dev) || !i2c || + get_tmds_slave(encoder)) + return; + + type = nouveau_i2c_identify(dev, "TMDS transmitter", info, 2); + if (type < 0) + return; + + drm_i2c_encoder_init(dev, to_encoder_slave(encoder), + &i2c->adapter, &info[type]); +} + static const struct drm_encoder_helper_funcs nv04_lvds_helper_funcs = { .dpms = nv04_lvds_dpms, .save = nv04_dfp_save, @@ -616,6 +685,10 @@ nv04_dfp_create(struct drm_connector *connector, struct dcb_entry *entry) encoder->possible_crtcs = entry->heads; encoder->possible_clones = 0; + if (entry->type == OUTPUT_TMDS && + entry->location != DCB_LOC_ON_CHIP) + nv04_tmds_slave_init(encoder); + drm_mode_connector_attach_encoder(connector, encoder); return 0; } diff --git a/drivers/gpu/drm/nouveau/nv04_tv.c b/drivers/gpu/drm/nouveau/nv04_tv.c index 94e299cef0b..0b5d012d7c2 100644 --- a/drivers/gpu/drm/nouveau/nv04_tv.c +++ b/drivers/gpu/drm/nouveau/nv04_tv.c @@ -89,7 +89,7 @@ static void nv04_tv_dpms(struct drm_encoder *encoder, int mode) NVWriteRAMDAC(dev, 0, NV_PRAMDAC_PLL_COEFF_SELECT, state->pllsel); - to_encoder_slave(encoder)->slave_funcs->dpms(encoder, mode); + get_slave_funcs(encoder)->dpms(encoder, mode); } static void nv04_tv_bind(struct drm_device *dev, int head, bool bind) @@ -152,7 +152,7 @@ static void nv04_tv_mode_set(struct drm_encoder *encoder, regp->tv_vskew = 1; regp->tv_vsync_delay = 1; - to_encoder_slave(encoder)->slave_funcs->mode_set(encoder, mode, adjusted_mode); + get_slave_funcs(encoder)->mode_set(encoder, mode, adjusted_mode); } static void nv04_tv_commit(struct drm_encoder *encoder) @@ -171,8 +171,7 @@ static void nv04_tv_commit(struct drm_encoder *encoder) static void nv04_tv_destroy(struct drm_encoder *encoder) { - to_encoder_slave(encoder)->slave_funcs->destroy(encoder); - + get_slave_funcs(encoder)->destroy(encoder); drm_encoder_cleanup(encoder); kfree(encoder->helper_private); @@ -229,7 +228,7 @@ nv04_tv_create(struct drm_connector *connector, struct dcb_entry *entry) goto fail_cleanup; /* Fill the function pointers */ - sfuncs = to_encoder_slave(encoder)->slave_funcs; + sfuncs = get_slave_funcs(encoder); *hfuncs = (struct drm_encoder_helper_funcs) { .dpms = nv04_tv_dpms, @@ -243,7 +242,6 @@ nv04_tv_create(struct drm_connector *connector, struct dcb_entry *entry) }; /* Attach it to the specified connector. */ - sfuncs->set_config(encoder, nv04_tv_encoder_info[type].platform_data); sfuncs->create_resources(encoder, connector); drm_mode_connector_attach_encoder(connector, encoder); diff --git a/drivers/gpu/drm/nouveau/nv10_graph.c b/drivers/gpu/drm/nouveau/nv10_graph.c index fcf2cdd1949..b2f6a57c0cc 100644 --- a/drivers/gpu/drm/nouveau/nv10_graph.c +++ b/drivers/gpu/drm/nouveau/nv10_graph.c @@ -43,51 +43,51 @@ struct pipe_state { }; static int nv10_graph_ctx_regs[] = { - NV10_PGRAPH_CTX_SWITCH1, - NV10_PGRAPH_CTX_SWITCH2, - NV10_PGRAPH_CTX_SWITCH3, - NV10_PGRAPH_CTX_SWITCH4, - NV10_PGRAPH_CTX_SWITCH5, - NV10_PGRAPH_CTX_CACHE1, /* 8 values from 0x400160 to 0x40017c */ - NV10_PGRAPH_CTX_CACHE2, /* 8 values from 0x400180 to 0x40019c */ - NV10_PGRAPH_CTX_CACHE3, /* 8 values from 0x4001a0 to 0x4001bc */ - NV10_PGRAPH_CTX_CACHE4, /* 8 values from 0x4001c0 to 0x4001dc */ - NV10_PGRAPH_CTX_CACHE5, /* 8 values from 0x4001e0 to 0x4001fc */ - 0x00400164, - 0x00400184, - 0x004001a4, - 0x004001c4, - 0x004001e4, - 0x00400168, - 0x00400188, - 0x004001a8, - 0x004001c8, - 0x004001e8, - 0x0040016c, - 0x0040018c, - 0x004001ac, - 0x004001cc, - 0x004001ec, - 0x00400170, - 0x00400190, - 0x004001b0, - 0x004001d0, - 0x004001f0, - 0x00400174, - 0x00400194, - 0x004001b4, - 0x004001d4, - 0x004001f4, - 0x00400178, - 0x00400198, - 0x004001b8, - 0x004001d8, - 0x004001f8, - 0x0040017c, - 0x0040019c, - 0x004001bc, - 0x004001dc, - 0x004001fc, + NV10_PGRAPH_CTX_SWITCH(0), + NV10_PGRAPH_CTX_SWITCH(1), + NV10_PGRAPH_CTX_SWITCH(2), + NV10_PGRAPH_CTX_SWITCH(3), + NV10_PGRAPH_CTX_SWITCH(4), + NV10_PGRAPH_CTX_CACHE(0, 0), + NV10_PGRAPH_CTX_CACHE(0, 1), + NV10_PGRAPH_CTX_CACHE(0, 2), + NV10_PGRAPH_CTX_CACHE(0, 3), + NV10_PGRAPH_CTX_CACHE(0, 4), + NV10_PGRAPH_CTX_CACHE(1, 0), + NV10_PGRAPH_CTX_CACHE(1, 1), + NV10_PGRAPH_CTX_CACHE(1, 2), + NV10_PGRAPH_CTX_CACHE(1, 3), + NV10_PGRAPH_CTX_CACHE(1, 4), + NV10_PGRAPH_CTX_CACHE(2, 0), + NV10_PGRAPH_CTX_CACHE(2, 1), + NV10_PGRAPH_CTX_CACHE(2, 2), + NV10_PGRAPH_CTX_CACHE(2, 3), + NV10_PGRAPH_CTX_CACHE(2, 4), + NV10_PGRAPH_CTX_CACHE(3, 0), + NV10_PGRAPH_CTX_CACHE(3, 1), + NV10_PGRAPH_CTX_CACHE(3, 2), + NV10_PGRAPH_CTX_CACHE(3, 3), + NV10_PGRAPH_CTX_CACHE(3, 4), + NV10_PGRAPH_CTX_CACHE(4, 0), + NV10_PGRAPH_CTX_CACHE(4, 1), + NV10_PGRAPH_CTX_CACHE(4, 2), + NV10_PGRAPH_CTX_CACHE(4, 3), + NV10_PGRAPH_CTX_CACHE(4, 4), + NV10_PGRAPH_CTX_CACHE(5, 0), + NV10_PGRAPH_CTX_CACHE(5, 1), + NV10_PGRAPH_CTX_CACHE(5, 2), + NV10_PGRAPH_CTX_CACHE(5, 3), + NV10_PGRAPH_CTX_CACHE(5, 4), + NV10_PGRAPH_CTX_CACHE(6, 0), + NV10_PGRAPH_CTX_CACHE(6, 1), + NV10_PGRAPH_CTX_CACHE(6, 2), + NV10_PGRAPH_CTX_CACHE(6, 3), + NV10_PGRAPH_CTX_CACHE(6, 4), + NV10_PGRAPH_CTX_CACHE(7, 0), + NV10_PGRAPH_CTX_CACHE(7, 1), + NV10_PGRAPH_CTX_CACHE(7, 2), + NV10_PGRAPH_CTX_CACHE(7, 3), + NV10_PGRAPH_CTX_CACHE(7, 4), NV10_PGRAPH_CTX_USER, NV04_PGRAPH_DMA_START_0, NV04_PGRAPH_DMA_START_1, @@ -653,6 +653,78 @@ static int nv17_graph_ctx_regs_find_offset(struct drm_device *dev, int reg) return -1; } +static void nv10_graph_load_dma_vtxbuf(struct nouveau_channel *chan, + uint32_t inst) +{ + struct drm_device *dev = chan->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; + uint32_t st2, st2_dl, st2_dh, fifo_ptr, fifo[0x60/4]; + uint32_t ctx_user, ctx_switch[5]; + int i, subchan = -1; + + /* NV10TCL_DMA_VTXBUF (method 0x18c) modifies hidden state + * that cannot be restored via MMIO. Do it through the FIFO + * instead. + */ + + /* Look for a celsius object */ + for (i = 0; i < 8; i++) { + int class = nv_rd32(dev, NV10_PGRAPH_CTX_CACHE(i, 0)) & 0xfff; + + if (class == 0x56 || class == 0x96 || class == 0x99) { + subchan = i; + break; + } + } + + if (subchan < 0 || !inst) + return; + + /* Save the current ctx object */ + ctx_user = nv_rd32(dev, NV10_PGRAPH_CTX_USER); + for (i = 0; i < 5; i++) + ctx_switch[i] = nv_rd32(dev, NV10_PGRAPH_CTX_SWITCH(i)); + + /* Save the FIFO state */ + st2 = nv_rd32(dev, NV10_PGRAPH_FFINTFC_ST2); + st2_dl = nv_rd32(dev, NV10_PGRAPH_FFINTFC_ST2_DL); + st2_dh = nv_rd32(dev, NV10_PGRAPH_FFINTFC_ST2_DH); + fifo_ptr = nv_rd32(dev, NV10_PGRAPH_FFINTFC_FIFO_PTR); + + for (i = 0; i < ARRAY_SIZE(fifo); i++) + fifo[i] = nv_rd32(dev, 0x4007a0 + 4 * i); + + /* Switch to the celsius subchannel */ + for (i = 0; i < 5; i++) + nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH(i), + nv_rd32(dev, NV10_PGRAPH_CTX_CACHE(subchan, i))); + nv_mask(dev, NV10_PGRAPH_CTX_USER, 0xe000, subchan << 13); + + /* Inject NV10TCL_DMA_VTXBUF */ + nv_wr32(dev, NV10_PGRAPH_FFINTFC_FIFO_PTR, 0); + nv_wr32(dev, NV10_PGRAPH_FFINTFC_ST2, + 0x2c000000 | chan->id << 20 | subchan << 16 | 0x18c); + nv_wr32(dev, NV10_PGRAPH_FFINTFC_ST2_DL, inst); + nv_mask(dev, NV10_PGRAPH_CTX_CONTROL, 0, 0x10000); + pgraph->fifo_access(dev, true); + pgraph->fifo_access(dev, false); + + /* Restore the FIFO state */ + for (i = 0; i < ARRAY_SIZE(fifo); i++) + nv_wr32(dev, 0x4007a0 + 4 * i, fifo[i]); + + nv_wr32(dev, NV10_PGRAPH_FFINTFC_FIFO_PTR, fifo_ptr); + nv_wr32(dev, NV10_PGRAPH_FFINTFC_ST2, st2); + nv_wr32(dev, NV10_PGRAPH_FFINTFC_ST2_DL, st2_dl); + nv_wr32(dev, NV10_PGRAPH_FFINTFC_ST2_DH, st2_dh); + + /* Restore the current ctx object */ + for (i = 0; i < 5; i++) + nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH(i), ctx_switch[i]); + nv_wr32(dev, NV10_PGRAPH_CTX_USER, ctx_user); +} + int nv10_graph_load_context(struct nouveau_channel *chan) { struct drm_device *dev = chan->dev; @@ -670,6 +742,8 @@ int nv10_graph_load_context(struct nouveau_channel *chan) } nv10_graph_load_pipe(chan); + nv10_graph_load_dma_vtxbuf(chan, (nv_rd32(dev, NV10_PGRAPH_GLOBALSTATE1) + & 0xffff)); nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10010100); tmp = nv_rd32(dev, NV10_PGRAPH_CTX_USER); @@ -856,11 +930,12 @@ int nv10_graph_init(struct drm_device *dev) for (i = 0; i < NV10_PFB_TILE__SIZE; i++) nv10_graph_set_region_tiling(dev, i, 0, 0, 0); - nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH1, 0x00000000); - nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH2, 0x00000000); - nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH3, 0x00000000); - nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH4, 0x00000000); - nv_wr32(dev, NV10_PGRAPH_STATE , 0xFFFFFFFF); + nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH(0), 0x00000000); + nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH(1), 0x00000000); + nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH(2), 0x00000000); + nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH(3), 0x00000000); + nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH(4), 0x00000000); + nv_wr32(dev, NV10_PGRAPH_STATE, 0xFFFFFFFF); tmp = nv_rd32(dev, NV10_PGRAPH_CTX_USER) & 0x00ffffff; tmp |= (dev_priv->engine.fifo.channels - 1) << 24; diff --git a/drivers/gpu/drm/nouveau/nv30_fb.c b/drivers/gpu/drm/nouveau/nv30_fb.c index 9d35c8b3b83..4a3f2f09512 100644 --- a/drivers/gpu/drm/nouveau/nv30_fb.c +++ b/drivers/gpu/drm/nouveau/nv30_fb.c @@ -30,15 +30,25 @@ #include "nouveau_drm.h" static int -calc_ref(int b, int l, int i) +calc_bias(struct drm_device *dev, int k, int i, int j) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + int b = (dev_priv->chipset > 0x30 ? + nv_rd32(dev, 0x122c + 0x10 * k + 0x4 * j) >> (4 * (i ^ 1)) : + 0) & 0xf; + + return 2 * (b & 0x8 ? b - 0x10 : b); +} + +static int +calc_ref(struct drm_device *dev, int l, int k, int i) { int j, x = 0; for (j = 0; j < 4; j++) { - int n = (b >> (8 * j) & 0xf); - int m = (l >> (8 * i) & 0xff) + 2 * (n & 0x8 ? n - 0x10 : n); + int m = (l >> (8 * i) & 0xff) + calc_bias(dev, k, i, j); - x |= (0x80 | (m & 0x1f)) << (8 * j); + x |= (0x80 | clamp(m, 0, 0x1f)) << (8 * j); } return x; @@ -63,18 +73,16 @@ nv30_fb_init(struct drm_device *dev) dev_priv->chipset == 0x35) { /* Related to ROP count */ int n = (dev_priv->chipset == 0x31 ? 2 : 4); - int b = (dev_priv->chipset > 0x30 ? - nv_rd32(dev, 0x122c) & 0xf : 0); int l = nv_rd32(dev, 0x1003d0); for (i = 0; i < n; i++) { for (j = 0; j < 3; j++) nv_wr32(dev, 0x10037c + 0xc * i + 0x4 * j, - calc_ref(b, l, j)); + calc_ref(dev, l, 0, j)); for (j = 0; j < 2; j++) nv_wr32(dev, 0x1003ac + 0x8 * i + 0x4 * j, - calc_ref(b, l, j)); + calc_ref(dev, l, 1, j)); } } diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c index 5d11ea10166..bfd4ca2fe7e 100644 --- a/drivers/gpu/drm/nouveau/nv50_crtc.c +++ b/drivers/gpu/drm/nouveau/nv50_crtc.c @@ -264,11 +264,16 @@ nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, int scaling_mode, bool update) int nv50_crtc_set_clock(struct drm_device *dev, int head, int pclk) { - uint32_t reg = NV50_PDISPLAY_CRTC_CLK_CTRL1(head); + struct drm_nouveau_private *dev_priv = dev->dev_private; struct pll_lims pll; - uint32_t reg1, reg2; + uint32_t reg, reg1, reg2; int ret, N1, M1, N2, M2, P; + if (dev_priv->chipset < NV_C0) + reg = NV50_PDISPLAY_CRTC_CLK_CTRL1(head); + else + reg = 0x614140 + (head * 0x800); + ret = get_pll_limits(dev, reg, &pll); if (ret) return ret; @@ -286,7 +291,8 @@ nv50_crtc_set_clock(struct drm_device *dev, int head, int pclk) nv_wr32(dev, reg, 0x10000611); nv_wr32(dev, reg + 4, reg1 | (M1 << 16) | N1); nv_wr32(dev, reg + 8, reg2 | (P << 28) | (M2 << 16) | N2); - } else { + } else + if (dev_priv->chipset < NV_C0) { ret = nv50_calc_pll2(dev, &pll, pclk, &N1, &N2, &M1, &P); if (ret <= 0) return 0; @@ -298,6 +304,17 @@ nv50_crtc_set_clock(struct drm_device *dev, int head, int pclk) nv_wr32(dev, reg, 0x50000610); nv_wr32(dev, reg + 4, reg1 | (P << 16) | (M1 << 8) | N1); nv_wr32(dev, reg + 8, N2); + } else { + ret = nv50_calc_pll2(dev, &pll, pclk, &N1, &N2, &M1, &P); + if (ret <= 0) + return 0; + + NV_DEBUG(dev, "pclk %d out %d N %d fN 0x%04x M %d P %d\n", + pclk, ret, N1, N2, M1, P); + + nv_mask(dev, reg + 0x0c, 0x00000000, 0x00000100); + nv_wr32(dev, reg + 0x04, (P << 16) | (N1 << 8) | M1); + nv_wr32(dev, reg + 0x10, N2 << 16); } return 0; @@ -348,7 +365,7 @@ nv50_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, gem = drm_gem_object_lookup(dev, file_priv, buffer_handle); if (!gem) - return -EINVAL; + return -ENOENT; cursor = nouveau_gem_object(gem); ret = nouveau_bo_map(cursor); @@ -381,15 +398,12 @@ nv50_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) static void nv50_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, - uint32_t size) + uint32_t start, uint32_t size) { + int end = (start + size > 256) ? 256 : start + size, i; struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - int i; - - if (size != 256) - return; - for (i = 0; i < 256; i++) { + for (i = start; i < end; i++) { nv_crtc->lut.r[i] = r[i]; nv_crtc->lut.g[i] = g[i]; nv_crtc->lut.b[i] = b[i]; diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index f13ad0de9c8..612fa6d6a0c 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -76,7 +76,10 @@ nv50_evo_dmaobj_new(struct nouveau_channel *evo, uint32_t class, uint32_t name, nv_wo32(dev, obj, 2, offset); nv_wo32(dev, obj, 3, 0x00000000); nv_wo32(dev, obj, 4, 0x00000000); - nv_wo32(dev, obj, 5, 0x00010000); + if (dev_priv->card_type < NV_C0) + nv_wo32(dev, obj, 5, 0x00010000); + else + nv_wo32(dev, obj, 5, 0x00020000); dev_priv->engine.instmem.flush(dev); return 0; diff --git a/drivers/gpu/drm/nouveau/nvc0_fb.c b/drivers/gpu/drm/nouveau/nvc0_fb.c new file mode 100644 index 00000000000..26a996025dd --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvc0_fb.c @@ -0,0 +1,38 @@ +/* + * Copyright 2010 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ + +#include "drmP.h" + +#include "nouveau_drv.h" + +int +nvc0_fb_init(struct drm_device *dev) +{ + return 0; +} + +void +nvc0_fb_takedown(struct drm_device *dev) +{ +} diff --git a/drivers/gpu/drm/nouveau/nvc0_fifo.c b/drivers/gpu/drm/nouveau/nvc0_fifo.c new file mode 100644 index 00000000000..d6437587197 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvc0_fifo.c @@ -0,0 +1,96 @@ +/* + * Copyright 2010 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ + +#include "drmP.h" + +#include "nouveau_drv.h" + +void +nvc0_fifo_disable(struct drm_device *dev) +{ +} + +void +nvc0_fifo_enable(struct drm_device *dev) +{ +} + +bool +nvc0_fifo_reassign(struct drm_device *dev, bool enable) +{ + return false; +} + +bool +nvc0_fifo_cache_flush(struct drm_device *dev) +{ + return true; +} + +bool +nvc0_fifo_cache_pull(struct drm_device *dev, bool enable) +{ + return false; +} + +int +nvc0_fifo_channel_id(struct drm_device *dev) +{ + return 127; +} + +int +nvc0_fifo_create_context(struct nouveau_channel *chan) +{ + return 0; +} + +void +nvc0_fifo_destroy_context(struct nouveau_channel *chan) +{ +} + +int +nvc0_fifo_load_context(struct nouveau_channel *chan) +{ + return 0; +} + +int +nvc0_fifo_unload_context(struct drm_device *dev) +{ + return 0; +} + +void +nvc0_fifo_takedown(struct drm_device *dev) +{ +} + +int +nvc0_fifo_init(struct drm_device *dev) +{ + return 0; +} + diff --git a/drivers/gpu/drm/nouveau/nvc0_graph.c b/drivers/gpu/drm/nouveau/nvc0_graph.c new file mode 100644 index 00000000000..717a5177a8d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvc0_graph.c @@ -0,0 +1,75 @@ +/* + * Copyright 2010 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ + +#include "drmP.h" + +#include "nouveau_drv.h" + +void +nvc0_graph_fifo_access(struct drm_device *dev, bool enabled) +{ +} + +struct nouveau_channel * +nvc0_graph_channel(struct drm_device *dev) +{ + return NULL; +} + +int +nvc0_graph_create_context(struct nouveau_channel *chan) +{ + return 0; +} + +void +nvc0_graph_destroy_context(struct nouveau_channel *chan) +{ +} + +int +nvc0_graph_load_context(struct nouveau_channel *chan) +{ + return 0; +} + +int +nvc0_graph_unload_context(struct drm_device *dev) +{ + return 0; +} + +void +nvc0_graph_takedown(struct drm_device *dev) +{ +} + +int +nvc0_graph_init(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + dev_priv->engine.graph.accel_blocked = true; + return 0; +} + diff --git a/drivers/gpu/drm/nouveau/nvc0_instmem.c b/drivers/gpu/drm/nouveau/nvc0_instmem.c new file mode 100644 index 00000000000..3ab3cdc4217 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvc0_instmem.c @@ -0,0 +1,232 @@ +/* + * Copyright 2010 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ + +#include "drmP.h" + +#include "nouveau_drv.h" + +int +nvc0_instmem_populate(struct drm_device *dev, struct nouveau_gpuobj *gpuobj, + uint32_t *size) +{ + int ret; + + *size = ALIGN(*size, 4096); + if (*size == 0) + return -EINVAL; + + ret = nouveau_bo_new(dev, NULL, *size, 0, TTM_PL_FLAG_VRAM, 0, 0x0000, + true, false, &gpuobj->im_backing); + if (ret) { + NV_ERROR(dev, "error getting PRAMIN backing pages: %d\n", ret); + return ret; + } + + ret = nouveau_bo_pin(gpuobj->im_backing, TTM_PL_FLAG_VRAM); + if (ret) { + NV_ERROR(dev, "error pinning PRAMIN backing VRAM: %d\n", ret); + nouveau_bo_ref(NULL, &gpuobj->im_backing); + return ret; + } + + gpuobj->im_backing_start = gpuobj->im_backing->bo.mem.mm_node->start; + gpuobj->im_backing_start <<= PAGE_SHIFT; + return 0; +} + +void +nvc0_instmem_clear(struct drm_device *dev, struct nouveau_gpuobj *gpuobj) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + + if (gpuobj && gpuobj->im_backing) { + if (gpuobj->im_bound) + dev_priv->engine.instmem.unbind(dev, gpuobj); + nouveau_bo_unpin(gpuobj->im_backing); + nouveau_bo_ref(NULL, &gpuobj->im_backing); + gpuobj->im_backing = NULL; + } +} + +int +nvc0_instmem_bind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + uint32_t pte, pte_end; + uint64_t vram; + + if (!gpuobj->im_backing || !gpuobj->im_pramin || gpuobj->im_bound) + return -EINVAL; + + NV_DEBUG(dev, "st=0x%lx sz=0x%lx\n", + gpuobj->im_pramin->start, gpuobj->im_pramin->size); + + pte = gpuobj->im_pramin->start >> 12; + pte_end = (gpuobj->im_pramin->size >> 12) + pte; + vram = gpuobj->im_backing_start; + + NV_DEBUG(dev, "pramin=0x%lx, pte=%d, pte_end=%d\n", + gpuobj->im_pramin->start, pte, pte_end); + NV_DEBUG(dev, "first vram page: 0x%08x\n", gpuobj->im_backing_start); + + while (pte < pte_end) { + nv_wr32(dev, 0x702000 + (pte * 8), (vram >> 8) | 1); + nv_wr32(dev, 0x702004 + (pte * 8), 0); + vram += 4096; + pte++; + } + dev_priv->engine.instmem.flush(dev); + + if (1) { + u32 chan = nv_rd32(dev, 0x1700) << 16; + nv_wr32(dev, 0x100cb8, (chan + 0x1000) >> 8); + nv_wr32(dev, 0x100cbc, 0x80000005); + } + + gpuobj->im_bound = 1; + return 0; +} + +int +nvc0_instmem_unbind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + uint32_t pte, pte_end; + + if (gpuobj->im_bound == 0) + return -EINVAL; + + pte = gpuobj->im_pramin->start >> 12; + pte_end = (gpuobj->im_pramin->size >> 12) + pte; + while (pte < pte_end) { + nv_wr32(dev, 0x702000 + (pte * 8), 0); + nv_wr32(dev, 0x702004 + (pte * 8), 0); + pte++; + } + dev_priv->engine.instmem.flush(dev); + + gpuobj->im_bound = 0; + return 0; +} + +void +nvc0_instmem_flush(struct drm_device *dev) +{ + nv_wr32(dev, 0x070000, 1); + if (!nv_wait(0x070000, 0x00000002, 0x00000000)) + NV_ERROR(dev, "PRAMIN flush timeout\n"); +} + +int +nvc0_instmem_suspend(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + int i; + + dev_priv->susres.ramin_copy = vmalloc(65536); + if (!dev_priv->susres.ramin_copy) + return -ENOMEM; + + for (i = 0x700000; i < 0x710000; i += 4) + dev_priv->susres.ramin_copy[i/4] = nv_rd32(dev, i); + return 0; +} + +void +nvc0_instmem_resume(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + u64 chan; + int i; + + chan = dev_priv->vram_size - dev_priv->ramin_rsvd_vram; + nv_wr32(dev, 0x001700, chan >> 16); + + for (i = 0x700000; i < 0x710000; i += 4) + nv_wr32(dev, i, dev_priv->susres.ramin_copy[i/4]); + vfree(dev_priv->susres.ramin_copy); + dev_priv->susres.ramin_copy = NULL; + + nv_wr32(dev, 0x001714, 0xc0000000 | (chan >> 12)); +} + +int +nvc0_instmem_init(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + u64 chan, pgt3, imem, lim3 = dev_priv->ramin_size - 1; + int ret, i; + + dev_priv->ramin_rsvd_vram = 1 * 1024 * 1024; + chan = dev_priv->vram_size - dev_priv->ramin_rsvd_vram; + imem = 4096 + 4096 + 32768; + + nv_wr32(dev, 0x001700, chan >> 16); + + /* channel setup */ + nv_wr32(dev, 0x700200, lower_32_bits(chan + 0x1000)); + nv_wr32(dev, 0x700204, upper_32_bits(chan + 0x1000)); + nv_wr32(dev, 0x700208, lower_32_bits(lim3)); + nv_wr32(dev, 0x70020c, upper_32_bits(lim3)); + + /* point pgd -> pgt */ + nv_wr32(dev, 0x701000, 0); + nv_wr32(dev, 0x701004, ((chan + 0x2000) >> 8) | 1); + + /* point pgt -> physical vram for channel */ + pgt3 = 0x2000; + for (i = 0; i < dev_priv->ramin_rsvd_vram; i += 4096, pgt3 += 8) { + nv_wr32(dev, 0x700000 + pgt3, ((chan + i) >> 8) | 1); + nv_wr32(dev, 0x700004 + pgt3, 0); + } + + /* clear rest of pgt */ + for (; i < dev_priv->ramin_size; i += 4096, pgt3 += 8) { + nv_wr32(dev, 0x700000 + pgt3, 0); + nv_wr32(dev, 0x700004 + pgt3, 0); + } + + /* point bar3 at the channel */ + nv_wr32(dev, 0x001714, 0xc0000000 | (chan >> 12)); + + /* Global PRAMIN heap */ + ret = drm_mm_init(&dev_priv->ramin_heap, imem, + dev_priv->ramin_size - imem); + if (ret) { + NV_ERROR(dev, "Failed to init RAMIN heap\n"); + return -ENOMEM; + } + + /*XXX: incorrect, but needed to make hash func "work" */ + dev_priv->ramht_offset = 0x10000; + dev_priv->ramht_bits = 9; + dev_priv->ramht_size = (1 << dev_priv->ramht_bits); + return 0; +} + +void +nvc0_instmem_takedown(struct drm_device *dev) +{ +} + diff --git a/drivers/gpu/drm/radeon/r600_cp.c b/drivers/gpu/drm/radeon/r600_cp.c index 68e6f434930..4f4cd8b286d 100644 --- a/drivers/gpu/drm/radeon/r600_cp.c +++ b/drivers/gpu/drm/radeon/r600_cp.c @@ -200,7 +200,7 @@ int r600_page_table_init(struct drm_device *dev) entry->pagelist[i], 0, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); - if (entry->busaddr[i] == 0) { + if (pci_dma_mapping_error(dev->pdev, entry->busaddr[i])) { DRM_ERROR("unable to map PCIGART pages!\n"); r600_page_table_cleanup(dev, gart_info); goto done; diff --git a/drivers/gpu/drm/radeon/r600_cs.c b/drivers/gpu/drm/radeon/r600_cs.c index c3ea212e0c3..d8864949e38 100644 --- a/drivers/gpu/drm/radeon/r600_cs.c +++ b/drivers/gpu/drm/radeon/r600_cs.c @@ -133,6 +133,7 @@ static inline int r600_bpe_from_format(u32 *bpe, u32 format) case V_038004_FMT_GB_GR: case V_038004_FMT_BG_RG: case V_038004_COLOR_INVALID: + default: *bpe = 16; return -EINVAL; } @@ -174,7 +175,7 @@ static inline int r600_cs_track_validate_cb(struct radeon_cs_parser *p, int i) dev_warn(p->dev, "FMASK or CMASK buffer are not supported by this kernel\n"); return -EINVAL; } - size = radeon_bo_size(track->cb_color_bo[i]); + size = radeon_bo_size(track->cb_color_bo[i]) - track->cb_color_bo_offset[i]; if (r600_bpe_from_format(&bpe, G_0280A0_FORMAT(track->cb_color_info[i]))) { dev_warn(p->dev, "%s:%d cb invalid format %d for %d (0x%08X)\n", __func__, __LINE__, G_0280A0_FORMAT(track->cb_color_info[i]), @@ -327,7 +328,6 @@ static int r600_cs_track_check(struct radeon_cs_parser *p) dev_warn(p->dev, "z/stencil buffer size not set\n"); return -EINVAL; } - printk_once(KERN_WARNING "You have old & broken userspace please consider updating mesa\n"); tmp = radeon_bo_size(track->db_bo) - track->db_offset; tmp = (tmp / bpe) >> 6; if (!tmp) { @@ -882,8 +882,6 @@ static inline int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx return -EINVAL; } ib[idx] = track->cb_color_base_last[tmp]; - printk_once(KERN_WARNING "You have old & broken userspace " - "please consider updating mesa & xf86-video-ati\n"); track->cb_color_frag_bo[tmp] = track->cb_color_bo[tmp]; } else { r = r600_cs_packet_next_reloc(p, &reloc); @@ -910,8 +908,6 @@ static inline int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx return -EINVAL; } ib[idx] = track->cb_color_base_last[tmp]; - printk_once(KERN_WARNING "You have old & broken userspace " - "please consider updating mesa & xf86-video-ati\n"); track->cb_color_tile_bo[tmp] = track->cb_color_bo[tmp]; } else { r = r600_cs_packet_next_reloc(p, &reloc); @@ -938,7 +934,7 @@ static inline int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx return -EINVAL; } tmp = (reg - CB_COLOR0_BASE) / 4; - track->cb_color_bo_offset[tmp] = radeon_get_ib_value(p, idx); + track->cb_color_bo_offset[tmp] = radeon_get_ib_value(p, idx) << 8; ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); track->cb_color_base_last[tmp] = ib[idx]; track->cb_color_bo[tmp] = reloc->robj; @@ -950,7 +946,7 @@ static inline int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx "0x%04X\n", reg); return -EINVAL; } - track->db_offset = radeon_get_ib_value(p, idx); + track->db_offset = radeon_get_ib_value(p, idx) << 8; ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); track->db_bo = reloc->robj; break; @@ -1055,10 +1051,10 @@ static void r600_texture_size(unsigned nfaces, unsigned blevel, unsigned nlevels } *l0_size = ALIGN((w0 * bpe), pitch_align) * h0 * d0; *mipmap_size = offset; - if (!blevel) - *mipmap_size -= *l0_size; if (!nlevels) *mipmap_size = *l0_size; + if (!blevel) + *mipmap_size -= *l0_size; } /** @@ -1165,14 +1161,14 @@ static inline int r600_check_texture_resource(struct radeon_cs_parser *p, u32 i (pitch_align * bpe), &l0_size, &mipmap_size); /* using get ib will give us the offset into the texture bo */ - word0 = radeon_get_ib_value(p, idx + 2); + word0 = radeon_get_ib_value(p, idx + 2) << 8; if ((l0_size + word0) > radeon_bo_size(texture)) { dev_warn(p->dev, "texture bo too small (%d %d %d %d -> %d have %ld)\n", w0, h0, bpe, word0, l0_size, radeon_bo_size(texture)); return -EINVAL; } /* using get ib will give us the offset into the mipmap bo */ - word0 = radeon_get_ib_value(p, idx + 3); + word0 = radeon_get_ib_value(p, idx + 3) << 8; if ((mipmap_size + word0) > radeon_bo_size(mipmap)) { dev_warn(p->dev, "mipmap bo too small (%d %d %d %d %d %d -> %d have %ld)\n", w0, h0, bpe, blevel, nlevels, word0, mipmap_size, radeon_bo_size(texture)); @@ -1366,7 +1362,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p, } for (i = 0; i < (pkt->count / 7); i++) { struct radeon_bo *texture, *mipmap; - u32 size, offset; + u32 size, offset, base_offset, mip_offset; switch (G__SQ_VTX_CONSTANT_TYPE(radeon_get_ib_value(p, idx+(i*7)+6+1))) { case SQ_TEX_VTX_VALID_TEXTURE: @@ -1376,7 +1372,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p, DRM_ERROR("bad SET_RESOURCE\n"); return -EINVAL; } - ib[idx+1+(i*7)+2] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); + base_offset = (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) ib[idx+1+(i*7)+0] |= S_038000_TILE_MODE(V_038000_ARRAY_2D_TILED_THIN1); else if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) @@ -1388,12 +1384,14 @@ static int r600_packet3_check(struct radeon_cs_parser *p, DRM_ERROR("bad SET_RESOURCE\n"); return -EINVAL; } - ib[idx+1+(i*7)+3] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); + mip_offset = (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); mipmap = reloc->robj; r = r600_check_texture_resource(p, idx+(i*7)+1, texture, mipmap, reloc->lobj.tiling_flags); if (r) return r; + ib[idx+1+(i*7)+2] += base_offset; + ib[idx+1+(i*7)+3] += mip_offset; break; case SQ_TEX_VTX_VALID_BUFFER: /* vtx base */ @@ -1403,10 +1401,11 @@ static int r600_packet3_check(struct radeon_cs_parser *p, return -EINVAL; } offset = radeon_get_ib_value(p, idx+1+(i*7)+0); - size = radeon_get_ib_value(p, idx+1+(i*7)+1); + size = radeon_get_ib_value(p, idx+1+(i*7)+1) + 1; if (p->rdev && (size + offset) > radeon_bo_size(reloc->robj)) { /* force size to size of the buffer */ - dev_warn(p->dev, "vbo resource seems too big for the bo\n"); + dev_warn(p->dev, "vbo resource seems too big (%d) for the bo (%ld)\n", + size + offset, radeon_bo_size(reloc->robj)); ib[idx+1+(i*7)+1] = radeon_bo_size(reloc->robj); } ib[idx+1+(i*7)+0] += (u32)((reloc->lobj.gpu_offset) & 0xffffffff); diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 3cd1c470b77..3dfcfa3ca42 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -1100,6 +1100,8 @@ struct radeon_device { struct notifier_block acpi_nb; /* only one userspace can use Hyperz features at a time */ struct drm_file *hyperz_filp; + /* i2c buses */ + struct radeon_i2c_chan *i2c_bus[RADEON_MAX_I2C_BUS]; }; int radeon_device_init(struct radeon_device *rdev, diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c index 3bc2bcdf530..6d30868744e 100644 --- a/drivers/gpu/drm/radeon/radeon_atombios.c +++ b/drivers/gpu/drm/radeon/radeon_atombios.c @@ -48,7 +48,8 @@ radeon_add_atom_connector(struct drm_device *dev, struct radeon_i2c_bus_rec *i2c_bus, bool linkb, uint32_t igp_lane_info, uint16_t connector_object_id, - struct radeon_hpd *hpd); + struct radeon_hpd *hpd, + struct radeon_router *router); /* from radeon_legacy_encoder.c */ extern void @@ -114,7 +115,8 @@ static inline struct radeon_i2c_bus_rec radeon_lookup_i2c_gpio(struct radeon_dev i2c.i2c_id = gpio->sucI2cId.ucAccess; - i2c.valid = true; + if (i2c.mask_clk_reg) + i2c.valid = true; break; } } @@ -123,6 +125,66 @@ static inline struct radeon_i2c_bus_rec radeon_lookup_i2c_gpio(struct radeon_dev return i2c; } +void radeon_atombios_i2c_init(struct radeon_device *rdev) +{ + struct atom_context *ctx = rdev->mode_info.atom_context; + ATOM_GPIO_I2C_ASSIGMENT *gpio; + struct radeon_i2c_bus_rec i2c; + int index = GetIndexIntoMasterTable(DATA, GPIO_I2C_Info); + struct _ATOM_GPIO_I2C_INFO *i2c_info; + uint16_t data_offset, size; + int i, num_indices; + char stmp[32]; + + memset(&i2c, 0, sizeof(struct radeon_i2c_bus_rec)); + + if (atom_parse_data_header(ctx, index, &size, NULL, NULL, &data_offset)) { + i2c_info = (struct _ATOM_GPIO_I2C_INFO *)(ctx->bios + data_offset); + + num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / + sizeof(ATOM_GPIO_I2C_ASSIGMENT); + + for (i = 0; i < num_indices; i++) { + gpio = &i2c_info->asGPIO_Info[i]; + i2c.valid = false; + i2c.mask_clk_reg = le16_to_cpu(gpio->usClkMaskRegisterIndex) * 4; + i2c.mask_data_reg = le16_to_cpu(gpio->usDataMaskRegisterIndex) * 4; + i2c.en_clk_reg = le16_to_cpu(gpio->usClkEnRegisterIndex) * 4; + i2c.en_data_reg = le16_to_cpu(gpio->usDataEnRegisterIndex) * 4; + i2c.y_clk_reg = le16_to_cpu(gpio->usClkY_RegisterIndex) * 4; + i2c.y_data_reg = le16_to_cpu(gpio->usDataY_RegisterIndex) * 4; + i2c.a_clk_reg = le16_to_cpu(gpio->usClkA_RegisterIndex) * 4; + i2c.a_data_reg = le16_to_cpu(gpio->usDataA_RegisterIndex) * 4; + i2c.mask_clk_mask = (1 << gpio->ucClkMaskShift); + i2c.mask_data_mask = (1 << gpio->ucDataMaskShift); + i2c.en_clk_mask = (1 << gpio->ucClkEnShift); + i2c.en_data_mask = (1 << gpio->ucDataEnShift); + i2c.y_clk_mask = (1 << gpio->ucClkY_Shift); + i2c.y_data_mask = (1 << gpio->ucDataY_Shift); + i2c.a_clk_mask = (1 << gpio->ucClkA_Shift); + i2c.a_data_mask = (1 << gpio->ucDataA_Shift); + + if (gpio->sucI2cId.sbfAccess.bfHW_Capable) + i2c.hw_capable = true; + else + i2c.hw_capable = false; + + if (gpio->sucI2cId.ucAccess == 0xa0) + i2c.mm_i2c = true; + else + i2c.mm_i2c = false; + + i2c.i2c_id = gpio->sucI2cId.ucAccess; + + if (i2c.mask_clk_reg) { + i2c.valid = true; + sprintf(stmp, "0x%x", i2c.i2c_id); + rdev->i2c_bus[i] = radeon_i2c_create(rdev->ddev, &i2c, stmp); + } + } + } +} + static inline struct radeon_gpio_rec radeon_lookup_gpio(struct radeon_device *rdev, u8 id) { @@ -206,6 +268,7 @@ static bool radeon_atom_apply_quirks(struct drm_device *dev, uint16_t *line_mux, struct radeon_hpd *hpd) { + struct radeon_device *rdev = dev->dev_private; /* Asus M2A-VM HDMI board lists the DVI port as HDMI */ if ((dev->pdev->device == 0x791e) && @@ -308,13 +371,22 @@ static bool radeon_atom_apply_quirks(struct drm_device *dev, } } - /* Acer laptop reports DVI-D as DVI-I */ + /* Acer laptop reports DVI-D as DVI-I and hpd pins reversed */ if ((dev->pdev->device == 0x95c4) && (dev->pdev->subsystem_vendor == 0x1025) && (dev->pdev->subsystem_device == 0x013c)) { + struct radeon_gpio_rec gpio; + if ((*connector_type == DRM_MODE_CONNECTOR_DVII) && - (supported_device == ATOM_DEVICE_DFP1_SUPPORT)) + (supported_device == ATOM_DEVICE_DFP1_SUPPORT)) { + gpio = radeon_lookup_gpio(rdev, 6); + *hpd = radeon_atom_get_hpd_info_from_gpio(rdev, &gpio); *connector_type = DRM_MODE_CONNECTOR_DVID; + } else if ((*connector_type == DRM_MODE_CONNECTOR_HDMIA) && + (supported_device == ATOM_DEVICE_DFP1_SUPPORT)) { + gpio = radeon_lookup_gpio(rdev, 7); + *hpd = radeon_atom_get_hpd_info_from_gpio(rdev, &gpio); + } } /* XFX Pine Group device rv730 reports no VGA DDC lines @@ -399,13 +471,15 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev) u16 size, data_offset; u8 frev, crev; ATOM_CONNECTOR_OBJECT_TABLE *con_obj; + ATOM_OBJECT_TABLE *router_obj; ATOM_DISPLAY_OBJECT_PATH_TABLE *path_obj; ATOM_OBJECT_HEADER *obj_header; - int i, j, path_size, device_support; + int i, j, k, path_size, device_support; int connector_type; u16 igp_lane_info, conn_id, connector_object_id; bool linkb; struct radeon_i2c_bus_rec ddc_bus; + struct radeon_router router; struct radeon_gpio_rec gpio; struct radeon_hpd hpd; @@ -415,6 +489,8 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev) if (crev < 2) return false; + router.valid = false; + obj_header = (ATOM_OBJECT_HEADER *) (ctx->bios + data_offset); path_obj = (ATOM_DISPLAY_OBJECT_PATH_TABLE *) (ctx->bios + data_offset + @@ -422,6 +498,9 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev) con_obj = (ATOM_CONNECTOR_OBJECT_TABLE *) (ctx->bios + data_offset + le16_to_cpu(obj_header->usConnectorObjectTableOffset)); + router_obj = (ATOM_OBJECT_TABLE *) + (ctx->bios + data_offset + + le16_to_cpu(obj_header->usRouterObjectTableOffset)); device_support = le16_to_cpu(obj_header->usDeviceSupport); path_size = 0; @@ -508,33 +587,86 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev) if (connector_type == DRM_MODE_CONNECTOR_Unknown) continue; - for (j = 0; j < ((le16_to_cpu(path->usSize) - 8) / 2); - j++) { - uint8_t enc_obj_id, enc_obj_num, enc_obj_type; + for (j = 0; j < ((le16_to_cpu(path->usSize) - 8) / 2); j++) { + uint8_t grph_obj_id, grph_obj_num, grph_obj_type; - enc_obj_id = + grph_obj_id = (le16_to_cpu(path->usGraphicObjIds[j]) & OBJECT_ID_MASK) >> OBJECT_ID_SHIFT; - enc_obj_num = + grph_obj_num = (le16_to_cpu(path->usGraphicObjIds[j]) & ENUM_ID_MASK) >> ENUM_ID_SHIFT; - enc_obj_type = + grph_obj_type = (le16_to_cpu(path->usGraphicObjIds[j]) & OBJECT_TYPE_MASK) >> OBJECT_TYPE_SHIFT; - /* FIXME: add support for router objects */ - if (enc_obj_type == GRAPH_OBJECT_TYPE_ENCODER) { - if (enc_obj_num == 2) + if (grph_obj_type == GRAPH_OBJECT_TYPE_ENCODER) { + if (grph_obj_num == 2) linkb = true; else linkb = false; radeon_add_atom_encoder(dev, - enc_obj_id, + grph_obj_id, le16_to_cpu (path-> usDeviceTag)); + } else if (grph_obj_type == GRAPH_OBJECT_TYPE_ROUTER) { + router.valid = false; + for (k = 0; k < router_obj->ucNumberOfObjects; k++) { + u16 router_obj_id = le16_to_cpu(router_obj->asObjects[j].usObjectID); + if (le16_to_cpu(path->usGraphicObjIds[j]) == router_obj_id) { + ATOM_COMMON_RECORD_HEADER *record = (ATOM_COMMON_RECORD_HEADER *) + (ctx->bios + data_offset + + le16_to_cpu(router_obj->asObjects[k].usRecordOffset)); + ATOM_I2C_RECORD *i2c_record; + ATOM_I2C_ID_CONFIG_ACCESS *i2c_config; + ATOM_ROUTER_DDC_PATH_SELECT_RECORD *ddc_path; + ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT *router_src_dst_table = + (ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT *) + (ctx->bios + data_offset + + le16_to_cpu(router_obj->asObjects[k].usSrcDstTableOffset)); + int enum_id; + + router.router_id = router_obj_id; + for (enum_id = 0; enum_id < router_src_dst_table->ucNumberOfDst; + enum_id++) { + if (le16_to_cpu(path->usConnObjectId) == + le16_to_cpu(router_src_dst_table->usDstObjectID[enum_id])) + break; + } + + while (record->ucRecordType > 0 && + record->ucRecordType <= ATOM_MAX_OBJECT_RECORD_NUMBER) { + switch (record->ucRecordType) { + case ATOM_I2C_RECORD_TYPE: + i2c_record = + (ATOM_I2C_RECORD *) + record; + i2c_config = + (ATOM_I2C_ID_CONFIG_ACCESS *) + &i2c_record->sucI2cId; + router.i2c_info = + radeon_lookup_i2c_gpio(rdev, + i2c_config-> + ucAccess); + router.i2c_addr = i2c_record->ucI2CAddr >> 1; + break; + case ATOM_ROUTER_DDC_PATH_SELECT_RECORD_TYPE: + ddc_path = (ATOM_ROUTER_DDC_PATH_SELECT_RECORD *) + record; + router.valid = true; + router.mux_type = ddc_path->ucMuxType; + router.mux_control_pin = ddc_path->ucMuxControlPin; + router.mux_state = ddc_path->ucMuxState[enum_id]; + break; + } + record = (ATOM_COMMON_RECORD_HEADER *) + ((char *)record + record->ucRecordSize); + } + } + } } } @@ -614,7 +746,8 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev) connector_type, &ddc_bus, linkb, igp_lane_info, connector_object_id, - &hpd); + &hpd, + &router); } } @@ -691,6 +824,9 @@ bool radeon_get_atom_connector_info_from_supported_devices_table(struct int i, j, max_device; struct bios_connector *bios_connectors; size_t bc_size = sizeof(*bios_connectors) * ATOM_MAX_SUPPORTED_DEVICE; + struct radeon_router router; + + router.valid = false; bios_connectors = kzalloc(bc_size, GFP_KERNEL); if (!bios_connectors) @@ -862,7 +998,8 @@ bool radeon_get_atom_connector_info_from_supported_devices_table(struct &bios_connectors[i].ddc_bus, false, 0, connector_object_id, - &bios_connectors[i].hpd); + &bios_connectors[i].hpd, + &router); } } @@ -1521,7 +1658,7 @@ void radeon_atombios_get_power_modes(struct radeon_device *rdev) thermal_controller_names[power_info->info.ucOverdriveThermalController], power_info->info.ucOverdriveControllerAddress >> 1); i2c_bus = radeon_lookup_i2c_gpio(rdev, power_info->info.ucOverdriveI2cLine); - rdev->pm.i2c_bus = radeon_i2c_create(rdev->ddev, &i2c_bus, "Thermal"); + rdev->pm.i2c_bus = radeon_i2c_lookup(rdev, &i2c_bus); if (rdev->pm.i2c_bus) { struct i2c_board_info info = { }; const char *name = thermal_controller_names[power_info->info. @@ -1814,7 +1951,7 @@ void radeon_atombios_get_power_modes(struct radeon_device *rdev) (controller->ucFanParameters & ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with"); i2c_bus = radeon_lookup_i2c_gpio(rdev, controller->ucI2cLine); - rdev->pm.i2c_bus = radeon_i2c_create(rdev->ddev, &i2c_bus, "Thermal"); + rdev->pm.i2c_bus = radeon_i2c_lookup(rdev, &i2c_bus); if (rdev->pm.i2c_bus) { struct i2c_board_info info = { }; const char *name = pp_lib_thermal_controller_names[controller->ucType]; @@ -1927,6 +2064,11 @@ void radeon_atombios_get_power_modes(struct radeon_device *rdev) rdev->pm.power_state[state_index].type = POWER_STATE_TYPE_PERFORMANCE; break; + case ATOM_PPLIB_CLASSIFICATION_UI_NONE: + if (misc2 & ATOM_PPLIB_CLASSIFICATION_3DPERFORMANCE) + rdev->pm.power_state[state_index].type = + POWER_STATE_TYPE_PERFORMANCE; + break; } rdev->pm.power_state[state_index].flags = 0; if (misc & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) diff --git a/drivers/gpu/drm/radeon/radeon_clocks.c b/drivers/gpu/drm/radeon/radeon_clocks.c index f64936cc4dd..14448a740ba 100644 --- a/drivers/gpu/drm/radeon/radeon_clocks.c +++ b/drivers/gpu/drm/radeon/radeon_clocks.c @@ -91,6 +91,85 @@ uint32_t radeon_legacy_get_memory_clock(struct radeon_device *rdev) return mclk; } +#ifdef CONFIG_OF +/* + * Read XTAL (ref clock), SCLK and MCLK from Open Firmware device + * tree. Hopefully, ATI OF driver is kind enough to fill these + */ +static bool __devinit radeon_read_clocks_OF(struct drm_device *dev) +{ + struct radeon_device *rdev = dev->dev_private; + struct device_node *dp = rdev->pdev->dev.of_node; + const u32 *val; + struct radeon_pll *p1pll = &rdev->clock.p1pll; + struct radeon_pll *p2pll = &rdev->clock.p2pll; + struct radeon_pll *spll = &rdev->clock.spll; + struct radeon_pll *mpll = &rdev->clock.mpll; + + if (dp == NULL) + return false; + val = of_get_property(dp, "ATY,RefCLK", NULL); + if (!val || !*val) { + printk(KERN_WARNING "radeonfb: No ATY,RefCLK property !\n"); + return false; + } + p1pll->reference_freq = p2pll->reference_freq = (*val) / 10; + p1pll->reference_div = RREG32_PLL(RADEON_PPLL_REF_DIV) & 0x3ff; + if (p1pll->reference_div < 2) + p1pll->reference_div = 12; + p2pll->reference_div = p1pll->reference_div; + + /* These aren't in the device-tree */ + if (rdev->family >= CHIP_R420) { + p1pll->pll_in_min = 100; + p1pll->pll_in_max = 1350; + p1pll->pll_out_min = 20000; + p1pll->pll_out_max = 50000; + p2pll->pll_in_min = 100; + p2pll->pll_in_max = 1350; + p2pll->pll_out_min = 20000; + p2pll->pll_out_max = 50000; + } else { + p1pll->pll_in_min = 40; + p1pll->pll_in_max = 500; + p1pll->pll_out_min = 12500; + p1pll->pll_out_max = 35000; + p2pll->pll_in_min = 40; + p2pll->pll_in_max = 500; + p2pll->pll_out_min = 12500; + p2pll->pll_out_max = 35000; + } + + spll->reference_freq = mpll->reference_freq = p1pll->reference_freq; + spll->reference_div = mpll->reference_div = + RREG32_PLL(RADEON_M_SPLL_REF_FB_DIV) & + RADEON_M_SPLL_REF_DIV_MASK; + + val = of_get_property(dp, "ATY,SCLK", NULL); + if (val && *val) + rdev->clock.default_sclk = (*val) / 10; + else + rdev->clock.default_sclk = + radeon_legacy_get_engine_clock(rdev); + + val = of_get_property(dp, "ATY,MCLK", NULL); + if (val && *val) + rdev->clock.default_mclk = (*val) / 10; + else + rdev->clock.default_mclk = + radeon_legacy_get_memory_clock(rdev); + + DRM_INFO("Using device-tree clock info\n"); + + return true; +} +#else +static bool __devinit radeon_read_clocks_OF(struct drm_device *dev) +{ + return false; +} +#endif /* CONFIG_OF */ + void radeon_get_clock_info(struct drm_device *dev) { struct radeon_device *rdev = dev->dev_private; @@ -105,6 +184,8 @@ void radeon_get_clock_info(struct drm_device *dev) ret = radeon_atom_get_clock_info(dev); else ret = radeon_combios_get_clock_info(dev); + if (!ret) + ret = radeon_read_clocks_OF(dev); if (ret) { if (p1pll->reference_div < 2) { diff --git a/drivers/gpu/drm/radeon/radeon_combios.c b/drivers/gpu/drm/radeon/radeon_combios.c index 5e1474cde4b..885dcfac183 100644 --- a/drivers/gpu/drm/radeon/radeon_combios.c +++ b/drivers/gpu/drm/radeon/radeon_combios.c @@ -480,9 +480,66 @@ radeon_combios_get_hardcoded_edid(struct radeon_device *rdev) } static struct radeon_i2c_bus_rec combios_setup_i2c_bus(struct radeon_device *rdev, - int ddc_line) + enum radeon_combios_ddc ddc, + u32 clk_mask, + u32 data_mask) { struct radeon_i2c_bus_rec i2c; + int ddc_line = 0; + + /* ddc id = mask reg + * DDC_NONE_DETECTED = none + * DDC_DVI = RADEON_GPIO_DVI_DDC + * DDC_VGA = RADEON_GPIO_VGA_DDC + * DDC_LCD = RADEON_GPIOPAD_MASK + * DDC_GPIO = RADEON_MDGPIO_MASK + * r1xx/r2xx + * DDC_MONID = RADEON_GPIO_MONID + * DDC_CRT2 = RADEON_GPIO_CRT2_DDC + * r3xx + * DDC_MONID = RADEON_GPIO_MONID + * DDC_CRT2 = RADEON_GPIO_DVI_DDC + * rs3xx/rs4xx + * DDC_MONID = RADEON_GPIOPAD_MASK + * DDC_CRT2 = RADEON_GPIO_MONID + */ + switch (ddc) { + case DDC_NONE_DETECTED: + default: + ddc_line = 0; + break; + case DDC_DVI: + ddc_line = RADEON_GPIO_DVI_DDC; + break; + case DDC_VGA: + ddc_line = RADEON_GPIO_VGA_DDC; + break; + case DDC_LCD: + ddc_line = RADEON_GPIOPAD_MASK; + break; + case DDC_GPIO: + ddc_line = RADEON_MDGPIO_MASK; + break; + case DDC_MONID: + if (rdev->family == CHIP_RS300 || + rdev->family == CHIP_RS400 || + rdev->family == CHIP_RS480) + ddc_line = RADEON_GPIOPAD_MASK; + else + ddc_line = RADEON_GPIO_MONID; + break; + case DDC_CRT2: + if (rdev->family == CHIP_RS300 || + rdev->family == CHIP_RS400 || + rdev->family == CHIP_RS480) + ddc_line = RADEON_GPIO_MONID; + else if (rdev->family >= CHIP_R300) { + ddc_line = RADEON_GPIO_DVI_DDC; + ddc = DDC_DVI; + } else + ddc_line = RADEON_GPIO_CRT2_DDC; + break; + } if (ddc_line == RADEON_GPIOPAD_MASK) { i2c.mask_clk_reg = RADEON_GPIOPAD_MASK; @@ -503,15 +560,6 @@ static struct radeon_i2c_bus_rec combios_setup_i2c_bus(struct radeon_device *rde i2c.y_clk_reg = RADEON_MDGPIO_Y; i2c.y_data_reg = RADEON_MDGPIO_Y; } else { - i2c.mask_clk_mask = RADEON_GPIO_EN_1; - i2c.mask_data_mask = RADEON_GPIO_EN_0; - i2c.a_clk_mask = RADEON_GPIO_A_1; - i2c.a_data_mask = RADEON_GPIO_A_0; - i2c.en_clk_mask = RADEON_GPIO_EN_1; - i2c.en_data_mask = RADEON_GPIO_EN_0; - i2c.y_clk_mask = RADEON_GPIO_Y_1; - i2c.y_data_mask = RADEON_GPIO_Y_0; - i2c.mask_clk_reg = ddc_line; i2c.mask_data_reg = ddc_line; i2c.a_clk_reg = ddc_line; @@ -522,6 +570,26 @@ static struct radeon_i2c_bus_rec combios_setup_i2c_bus(struct radeon_device *rde i2c.y_data_reg = ddc_line; } + if (clk_mask && data_mask) { + i2c.mask_clk_mask = clk_mask; + i2c.mask_data_mask = data_mask; + i2c.a_clk_mask = clk_mask; + i2c.a_data_mask = data_mask; + i2c.en_clk_mask = clk_mask; + i2c.en_data_mask = data_mask; + i2c.y_clk_mask = clk_mask; + i2c.y_data_mask = data_mask; + } else { + i2c.mask_clk_mask = RADEON_GPIO_EN_1; + i2c.mask_data_mask = RADEON_GPIO_EN_0; + i2c.a_clk_mask = RADEON_GPIO_A_1; + i2c.a_data_mask = RADEON_GPIO_A_0; + i2c.en_clk_mask = RADEON_GPIO_EN_1; + i2c.en_data_mask = RADEON_GPIO_EN_0; + i2c.y_clk_mask = RADEON_GPIO_Y_1; + i2c.y_data_mask = RADEON_GPIO_Y_0; + } + switch (rdev->family) { case CHIP_R100: case CHIP_RV100: @@ -599,7 +667,8 @@ static struct radeon_i2c_bus_rec combios_setup_i2c_bus(struct radeon_device *rde break; } i2c.mm_i2c = false; - i2c.i2c_id = 0; + + i2c.i2c_id = ddc; i2c.hpd = RADEON_HPD_NONE; if (ddc_line) @@ -610,6 +679,62 @@ static struct radeon_i2c_bus_rec combios_setup_i2c_bus(struct radeon_device *rde return i2c; } +void radeon_combios_i2c_init(struct radeon_device *rdev) +{ + struct drm_device *dev = rdev->ddev; + struct radeon_i2c_bus_rec i2c; + + + i2c = combios_setup_i2c_bus(rdev, DDC_DVI, 0, 0); + rdev->i2c_bus[0] = radeon_i2c_create(dev, &i2c, "DVI_DDC"); + + i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0); + rdev->i2c_bus[1] = radeon_i2c_create(dev, &i2c, "VGA_DDC"); + + i2c.valid = true; + i2c.hw_capable = true; + i2c.mm_i2c = true; + i2c.i2c_id = 0xa0; + rdev->i2c_bus[2] = radeon_i2c_create(dev, &i2c, "MM_I2C"); + + if (rdev->family == CHIP_RS300 || + rdev->family == CHIP_RS400 || + rdev->family == CHIP_RS480) { + u16 offset; + u8 id, blocks, clk, data; + int i; + + i2c = combios_setup_i2c_bus(rdev, DDC_CRT2, 0, 0); + rdev->i2c_bus[3] = radeon_i2c_create(dev, &i2c, "MONID"); + + offset = combios_get_table_offset(dev, COMBIOS_I2C_INFO_TABLE); + if (offset) { + blocks = RBIOS8(offset + 2); + for (i = 0; i < blocks; i++) { + id = RBIOS8(offset + 3 + (i * 5) + 0); + if (id == 136) { + clk = RBIOS8(offset + 3 + (i * 5) + 3); + data = RBIOS8(offset + 3 + (i * 5) + 4); + i2c = combios_setup_i2c_bus(rdev, DDC_MONID, + clk, data); + rdev->i2c_bus[4] = radeon_i2c_create(dev, &i2c, "GPIOPAD_MASK"); + break; + } + } + } + + } else if (rdev->family >= CHIP_R300) { + i2c = combios_setup_i2c_bus(rdev, DDC_MONID, 0, 0); + rdev->i2c_bus[3] = radeon_i2c_create(dev, &i2c, "MONID"); + } else { + i2c = combios_setup_i2c_bus(rdev, DDC_MONID, 0, 0); + rdev->i2c_bus[3] = radeon_i2c_create(dev, &i2c, "MONID"); + + i2c = combios_setup_i2c_bus(rdev, DDC_CRT2, 0, 0); + rdev->i2c_bus[4] = radeon_i2c_create(dev, &i2c, "CRT2_DDC"); + } +} + bool radeon_combios_get_clock_info(struct drm_device *dev) { struct radeon_device *rdev = dev->dev_private; @@ -1247,8 +1372,8 @@ bool radeon_legacy_get_ext_tmds_info_from_table(struct radeon_encoder *encoder, struct radeon_i2c_bus_rec i2c_bus; /* default for macs */ - i2c_bus = combios_setup_i2c_bus(rdev, RADEON_GPIO_MONID); - tmds->i2c_bus = radeon_i2c_create(dev, &i2c_bus, "DVO"); + i2c_bus = combios_setup_i2c_bus(rdev, DDC_MONID, 0, 0); + tmds->i2c_bus = radeon_i2c_lookup(rdev, &i2c_bus); /* XXX some macs have duallink chips */ switch (rdev->mode_info.connector_table) { @@ -1269,47 +1394,16 @@ bool radeon_legacy_get_ext_tmds_info_from_combios(struct radeon_encoder *encoder struct drm_device *dev = encoder->base.dev; struct radeon_device *rdev = dev->dev_private; uint16_t offset; - uint8_t ver, id, blocks, clk, data; - int i; + uint8_t ver; enum radeon_combios_ddc gpio; struct radeon_i2c_bus_rec i2c_bus; tmds->i2c_bus = NULL; if (rdev->flags & RADEON_IS_IGP) { - offset = combios_get_table_offset(dev, COMBIOS_I2C_INFO_TABLE); - if (offset) { - ver = RBIOS8(offset); - DRM_INFO("GPIO Table revision: %d\n", ver); - blocks = RBIOS8(offset + 2); - for (i = 0; i < blocks; i++) { - id = RBIOS8(offset + 3 + (i * 5) + 0); - if (id == 136) { - clk = RBIOS8(offset + 3 + (i * 5) + 3); - data = RBIOS8(offset + 3 + (i * 5) + 4); - i2c_bus.valid = true; - i2c_bus.mask_clk_mask = (1 << clk); - i2c_bus.mask_data_mask = (1 << data); - i2c_bus.a_clk_mask = (1 << clk); - i2c_bus.a_data_mask = (1 << data); - i2c_bus.en_clk_mask = (1 << clk); - i2c_bus.en_data_mask = (1 << data); - i2c_bus.y_clk_mask = (1 << clk); - i2c_bus.y_data_mask = (1 << data); - i2c_bus.mask_clk_reg = RADEON_GPIOPAD_MASK; - i2c_bus.mask_data_reg = RADEON_GPIOPAD_MASK; - i2c_bus.a_clk_reg = RADEON_GPIOPAD_A; - i2c_bus.a_data_reg = RADEON_GPIOPAD_A; - i2c_bus.en_clk_reg = RADEON_GPIOPAD_EN; - i2c_bus.en_data_reg = RADEON_GPIOPAD_EN; - i2c_bus.y_clk_reg = RADEON_GPIOPAD_Y; - i2c_bus.y_data_reg = RADEON_GPIOPAD_Y; - tmds->i2c_bus = radeon_i2c_create(dev, &i2c_bus, "DVO"); - tmds->dvo_chip = DVO_SIL164; - tmds->slave_addr = 0x70 >> 1; /* 7 bit addressing */ - break; - } - } - } + i2c_bus = combios_setup_i2c_bus(rdev, DDC_MONID, 0, 0); + tmds->i2c_bus = radeon_i2c_lookup(rdev, &i2c_bus); + tmds->dvo_chip = DVO_SIL164; + tmds->slave_addr = 0x70 >> 1; /* 7 bit addressing */ } else { offset = combios_get_table_offset(dev, COMBIOS_EXT_TMDS_INFO_TABLE); if (offset) { @@ -1318,37 +1412,15 @@ bool radeon_legacy_get_ext_tmds_info_from_combios(struct radeon_encoder *encoder tmds->slave_addr = RBIOS8(offset + 4 + 2); tmds->slave_addr >>= 1; /* 7 bit addressing */ gpio = RBIOS8(offset + 4 + 3); - switch (gpio) { - case DDC_MONID: - i2c_bus = combios_setup_i2c_bus(rdev, RADEON_GPIO_MONID); - tmds->i2c_bus = radeon_i2c_create(dev, &i2c_bus, "DVO"); - break; - case DDC_DVI: - i2c_bus = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC); - tmds->i2c_bus = radeon_i2c_create(dev, &i2c_bus, "DVO"); - break; - case DDC_VGA: - i2c_bus = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC); - tmds->i2c_bus = radeon_i2c_create(dev, &i2c_bus, "DVO"); - break; - case DDC_CRT2: - /* R3xx+ chips don't have GPIO_CRT2_DDC gpio pad */ - if (rdev->family >= CHIP_R300) - i2c_bus = combios_setup_i2c_bus(rdev, RADEON_GPIO_MONID); - else - i2c_bus = combios_setup_i2c_bus(rdev, RADEON_GPIO_CRT2_DDC); - tmds->i2c_bus = radeon_i2c_create(dev, &i2c_bus, "DVO"); - break; - case DDC_LCD: /* MM i2c */ + if (gpio == DDC_LCD) { + /* MM i2c */ i2c_bus.valid = true; i2c_bus.hw_capable = true; i2c_bus.mm_i2c = true; - tmds->i2c_bus = radeon_i2c_create(dev, &i2c_bus, "DVO"); - break; - default: - DRM_ERROR("Unsupported gpio %d\n", gpio); - break; - } + i2c_bus.i2c_id = 0xa0; + } else + i2c_bus = combios_setup_i2c_bus(rdev, gpio, 0, 0); + tmds->i2c_bus = radeon_i2c_lookup(rdev, &i2c_bus); } } @@ -1430,7 +1502,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) /* these are the most common settings */ if (rdev->flags & RADEON_SINGLE_CRTC) { /* VGA - primary dac */ - ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC); + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0); hpd.hpd = RADEON_HPD_NONE; radeon_add_legacy_encoder(dev, radeon_get_encoder_id(dev, @@ -1445,7 +1517,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) &hpd); } else if (rdev->flags & RADEON_IS_MOBILITY) { /* LVDS */ - ddc_i2c = combios_setup_i2c_bus(rdev, 0); + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_NONE_DETECTED, 0, 0); hpd.hpd = RADEON_HPD_NONE; radeon_add_legacy_encoder(dev, radeon_get_encoder_id(dev, @@ -1460,7 +1532,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) &hpd); /* VGA - primary dac */ - ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC); + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0); hpd.hpd = RADEON_HPD_NONE; radeon_add_legacy_encoder(dev, radeon_get_encoder_id(dev, @@ -1475,7 +1547,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) &hpd); } else { /* DVI-I - tv dac, int tmds */ - ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC); + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_DVI, 0, 0); hpd.hpd = RADEON_HPD_1; radeon_add_legacy_encoder(dev, radeon_get_encoder_id(dev, @@ -1496,7 +1568,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) &hpd); /* VGA - primary dac */ - ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC); + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0); hpd.hpd = RADEON_HPD_NONE; radeon_add_legacy_encoder(dev, radeon_get_encoder_id(dev, @@ -1532,7 +1604,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) DRM_INFO("Connector Table: %d (ibook)\n", rdev->mode_info.connector_table); /* LVDS */ - ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC); + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_DVI, 0, 0); hpd.hpd = RADEON_HPD_NONE; radeon_add_legacy_encoder(dev, radeon_get_encoder_id(dev, @@ -1544,7 +1616,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) CONNECTOR_OBJECT_ID_LVDS, &hpd); /* VGA - TV DAC */ - ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC); + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0); hpd.hpd = RADEON_HPD_NONE; radeon_add_legacy_encoder(dev, radeon_get_encoder_id(dev, @@ -1573,7 +1645,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) DRM_INFO("Connector Table: %d (powerbook external tmds)\n", rdev->mode_info.connector_table); /* LVDS */ - ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC); + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_DVI, 0, 0); hpd.hpd = RADEON_HPD_NONE; radeon_add_legacy_encoder(dev, radeon_get_encoder_id(dev, @@ -1585,7 +1657,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) CONNECTOR_OBJECT_ID_LVDS, &hpd); /* DVI-I - primary dac, ext tmds */ - ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC); + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0); hpd.hpd = RADEON_HPD_2; /* ??? */ radeon_add_legacy_encoder(dev, radeon_get_encoder_id(dev, @@ -1622,7 +1694,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) DRM_INFO("Connector Table: %d (powerbook internal tmds)\n", rdev->mode_info.connector_table); /* LVDS */ - ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC); + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_DVI, 0, 0); hpd.hpd = RADEON_HPD_NONE; radeon_add_legacy_encoder(dev, radeon_get_encoder_id(dev, @@ -1634,7 +1706,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) CONNECTOR_OBJECT_ID_LVDS, &hpd); /* DVI-I - primary dac, int tmds */ - ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC); + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0); hpd.hpd = RADEON_HPD_1; /* ??? */ radeon_add_legacy_encoder(dev, radeon_get_encoder_id(dev, @@ -1670,7 +1742,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) DRM_INFO("Connector Table: %d (powerbook vga)\n", rdev->mode_info.connector_table); /* LVDS */ - ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC); + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_DVI, 0, 0); hpd.hpd = RADEON_HPD_NONE; radeon_add_legacy_encoder(dev, radeon_get_encoder_id(dev, @@ -1682,7 +1754,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) CONNECTOR_OBJECT_ID_LVDS, &hpd); /* VGA - primary dac */ - ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC); + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0); hpd.hpd = RADEON_HPD_NONE; radeon_add_legacy_encoder(dev, radeon_get_encoder_id(dev, @@ -1711,7 +1783,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) DRM_INFO("Connector Table: %d (mini external tmds)\n", rdev->mode_info.connector_table); /* DVI-I - tv dac, ext tmds */ - ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_CRT2_DDC); + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_CRT2, 0, 0); hpd.hpd = RADEON_HPD_2; /* ??? */ radeon_add_legacy_encoder(dev, radeon_get_encoder_id(dev, @@ -1748,7 +1820,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) DRM_INFO("Connector Table: %d (mini internal tmds)\n", rdev->mode_info.connector_table); /* DVI-I - tv dac, int tmds */ - ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_CRT2_DDC); + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_CRT2, 0, 0); hpd.hpd = RADEON_HPD_1; /* ??? */ radeon_add_legacy_encoder(dev, radeon_get_encoder_id(dev, @@ -1784,7 +1856,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) DRM_INFO("Connector Table: %d (imac g5 isight)\n", rdev->mode_info.connector_table); /* DVI-D - int tmds */ - ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_MONID); + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_MONID, 0, 0); hpd.hpd = RADEON_HPD_1; /* ??? */ radeon_add_legacy_encoder(dev, radeon_get_encoder_id(dev, @@ -1796,7 +1868,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D, &hpd); /* VGA - tv dac */ - ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC); + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_DVI, 0, 0); hpd.hpd = RADEON_HPD_NONE; radeon_add_legacy_encoder(dev, radeon_get_encoder_id(dev, @@ -1825,7 +1897,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) DRM_INFO("Connector Table: %d (emac)\n", rdev->mode_info.connector_table); /* VGA - primary dac */ - ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC); + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0); hpd.hpd = RADEON_HPD_NONE; radeon_add_legacy_encoder(dev, radeon_get_encoder_id(dev, @@ -1837,7 +1909,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) CONNECTOR_OBJECT_ID_VGA, &hpd); /* VGA - tv dac */ - ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_CRT2_DDC); + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_CRT2, 0, 0); hpd.hpd = RADEON_HPD_NONE; radeon_add_legacy_encoder(dev, radeon_get_encoder_id(dev, @@ -1866,7 +1938,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) DRM_INFO("Connector Table: %d (rn50-power)\n", rdev->mode_info.connector_table); /* VGA - primary dac */ - ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC); + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0); hpd.hpd = RADEON_HPD_NONE; radeon_add_legacy_encoder(dev, radeon_get_encoder_id(dev, @@ -1877,7 +1949,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) DRM_MODE_CONNECTOR_VGA, &ddc_i2c, CONNECTOR_OBJECT_ID_VGA, &hpd); - ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_CRT2_DDC); + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_CRT2, 0, 0); hpd.hpd = RADEON_HPD_NONE; radeon_add_legacy_encoder(dev, radeon_get_encoder_id(dev, @@ -1907,31 +1979,6 @@ static bool radeon_apply_legacy_quirks(struct drm_device *dev, struct radeon_i2c_bus_rec *ddc_i2c, struct radeon_hpd *hpd) { - struct radeon_device *rdev = dev->dev_private; - - /* XPRESS DDC quirks */ - if ((rdev->family == CHIP_RS400 || - rdev->family == CHIP_RS480) && - ddc_i2c->mask_clk_reg == RADEON_GPIO_CRT2_DDC) - *ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_MONID); - else if ((rdev->family == CHIP_RS400 || - rdev->family == CHIP_RS480) && - ddc_i2c->mask_clk_reg == RADEON_GPIO_MONID) { - *ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIOPAD_MASK); - ddc_i2c->mask_clk_mask = (0x20 << 8); - ddc_i2c->mask_data_mask = 0x80; - ddc_i2c->a_clk_mask = (0x20 << 8); - ddc_i2c->a_data_mask = 0x80; - ddc_i2c->en_clk_mask = (0x20 << 8); - ddc_i2c->en_data_mask = 0x80; - ddc_i2c->y_clk_mask = (0x20 << 8); - ddc_i2c->y_data_mask = 0x80; - } - - /* R3xx+ chips don't have GPIO_CRT2_DDC gpio pad */ - if ((rdev->family >= CHIP_R300) && - ddc_i2c->mask_clk_reg == RADEON_GPIO_CRT2_DDC) - *ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC); /* Certain IBM chipset RN50s have a BIOS reporting two VGAs, one with VGA DDC and one with CRT2 DDC. - kill the CRT2 DDC one */ @@ -2035,27 +2082,7 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev) connector = (tmp >> 12) & 0xf; ddc_type = (tmp >> 8) & 0xf; - switch (ddc_type) { - case DDC_MONID: - ddc_i2c = - combios_setup_i2c_bus(rdev, RADEON_GPIO_MONID); - break; - case DDC_DVI: - ddc_i2c = - combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC); - break; - case DDC_VGA: - ddc_i2c = - combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC); - break; - case DDC_CRT2: - ddc_i2c = - combios_setup_i2c_bus(rdev, RADEON_GPIO_CRT2_DDC); - break; - default: - ddc_i2c.valid = false; - break; - } + ddc_i2c = combios_setup_i2c_bus(rdev, ddc_type, 0, 0); switch (connector) { case CONNECTOR_PROPRIETARY_LEGACY: @@ -2225,7 +2252,7 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev) 0), ATOM_DEVICE_DFP1_SUPPORT); - ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC); + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_DVI, 0, 0); hpd.hpd = RADEON_HPD_1; radeon_add_legacy_connector(dev, 0, @@ -2245,7 +2272,7 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev) ATOM_DEVICE_CRT1_SUPPORT, 1), ATOM_DEVICE_CRT1_SUPPORT); - ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC); + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0); hpd.hpd = RADEON_HPD_NONE; radeon_add_legacy_connector(dev, 0, @@ -2278,70 +2305,25 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev) if (lcd_ddc_info) { ddc_type = RBIOS8(lcd_ddc_info + 2); switch (ddc_type) { - case DDC_MONID: - ddc_i2c = - combios_setup_i2c_bus - (rdev, RADEON_GPIO_MONID); - break; - case DDC_DVI: - ddc_i2c = - combios_setup_i2c_bus - (rdev, RADEON_GPIO_DVI_DDC); - break; - case DDC_VGA: - ddc_i2c = - combios_setup_i2c_bus - (rdev, RADEON_GPIO_VGA_DDC); - break; - case DDC_CRT2: - ddc_i2c = - combios_setup_i2c_bus - (rdev, RADEON_GPIO_CRT2_DDC); - break; case DDC_LCD: ddc_i2c = - combios_setup_i2c_bus - (rdev, RADEON_GPIOPAD_MASK); - ddc_i2c.mask_clk_mask = - RBIOS32(lcd_ddc_info + 3); - ddc_i2c.mask_data_mask = - RBIOS32(lcd_ddc_info + 7); - ddc_i2c.a_clk_mask = - RBIOS32(lcd_ddc_info + 3); - ddc_i2c.a_data_mask = - RBIOS32(lcd_ddc_info + 7); - ddc_i2c.en_clk_mask = - RBIOS32(lcd_ddc_info + 3); - ddc_i2c.en_data_mask = - RBIOS32(lcd_ddc_info + 7); - ddc_i2c.y_clk_mask = - RBIOS32(lcd_ddc_info + 3); - ddc_i2c.y_data_mask = - RBIOS32(lcd_ddc_info + 7); + combios_setup_i2c_bus(rdev, + DDC_LCD, + RBIOS32(lcd_ddc_info + 3), + RBIOS32(lcd_ddc_info + 7)); + radeon_i2c_add(rdev, &ddc_i2c, "LCD"); break; case DDC_GPIO: ddc_i2c = - combios_setup_i2c_bus - (rdev, RADEON_MDGPIO_MASK); - ddc_i2c.mask_clk_mask = - RBIOS32(lcd_ddc_info + 3); - ddc_i2c.mask_data_mask = - RBIOS32(lcd_ddc_info + 7); - ddc_i2c.a_clk_mask = - RBIOS32(lcd_ddc_info + 3); - ddc_i2c.a_data_mask = - RBIOS32(lcd_ddc_info + 7); - ddc_i2c.en_clk_mask = - RBIOS32(lcd_ddc_info + 3); - ddc_i2c.en_data_mask = - RBIOS32(lcd_ddc_info + 7); - ddc_i2c.y_clk_mask = - RBIOS32(lcd_ddc_info + 3); - ddc_i2c.y_data_mask = - RBIOS32(lcd_ddc_info + 7); + combios_setup_i2c_bus(rdev, + DDC_GPIO, + RBIOS32(lcd_ddc_info + 3), + RBIOS32(lcd_ddc_info + 7)); + radeon_i2c_add(rdev, &ddc_i2c, "LCD"); break; default: - ddc_i2c.valid = false; + ddc_i2c = + combios_setup_i2c_bus(rdev, ddc_type, 0, 0); break; } DRM_DEBUG_KMS("LCD DDC Info Table found!\n"); diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c index 2395c8600cf..47c4b276d30 100644 --- a/drivers/gpu/drm/radeon/radeon_connectors.c +++ b/drivers/gpu/drm/radeon/radeon_connectors.c @@ -518,8 +518,6 @@ static void radeon_connector_destroy(struct drm_connector *connector) { struct radeon_connector *radeon_connector = to_radeon_connector(connector); - if (radeon_connector->ddc_bus) - radeon_i2c_destroy(radeon_connector->ddc_bus); if (radeon_connector->edid) kfree(radeon_connector->edid); kfree(radeon_connector->con_priv); @@ -955,8 +953,6 @@ static void radeon_dp_connector_destroy(struct drm_connector *connector) struct radeon_connector *radeon_connector = to_radeon_connector(connector); struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv; - if (radeon_connector->ddc_bus) - radeon_i2c_destroy(radeon_connector->ddc_bus); if (radeon_connector->edid) kfree(radeon_connector->edid); if (radeon_dig_connector->dp_i2c_bus) @@ -1044,7 +1040,8 @@ radeon_add_atom_connector(struct drm_device *dev, bool linkb, uint32_t igp_lane_info, uint16_t connector_object_id, - struct radeon_hpd *hpd) + struct radeon_hpd *hpd, + struct radeon_router *router) { struct radeon_device *rdev = dev->dev_private; struct drm_connector *connector; @@ -1069,6 +1066,11 @@ radeon_add_atom_connector(struct drm_device *dev, radeon_connector->shared_ddc = true; shared_ddc = true; } + if (radeon_connector->router_bus && router->valid && + (radeon_connector->router.router_id == router->router_id)) { + radeon_connector->shared_ddc = false; + shared_ddc = false; + } } } @@ -1083,12 +1085,18 @@ radeon_add_atom_connector(struct drm_device *dev, radeon_connector->shared_ddc = shared_ddc; radeon_connector->connector_object_id = connector_object_id; radeon_connector->hpd = *hpd; + radeon_connector->router = *router; + if (router->valid) { + radeon_connector->router_bus = radeon_i2c_lookup(rdev, &router->i2c_info); + if (!radeon_connector->router_bus) + goto failed; + } switch (connector_type) { case DRM_MODE_CONNECTOR_VGA: drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type); drm_connector_helper_add(&radeon_connector->base, &radeon_vga_connector_helper_funcs); if (i2c_bus->valid) { - radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "VGA"); + radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus); if (!radeon_connector->ddc_bus) goto failed; } @@ -1104,7 +1112,7 @@ radeon_add_atom_connector(struct drm_device *dev, drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type); drm_connector_helper_add(&radeon_connector->base, &radeon_vga_connector_helper_funcs); if (i2c_bus->valid) { - radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "DVI"); + radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus); if (!radeon_connector->ddc_bus) goto failed; } @@ -1126,7 +1134,7 @@ radeon_add_atom_connector(struct drm_device *dev, drm_connector_init(dev, &radeon_connector->base, &radeon_dvi_connector_funcs, connector_type); drm_connector_helper_add(&radeon_connector->base, &radeon_dvi_connector_helper_funcs); if (i2c_bus->valid) { - radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "DVI"); + radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus); if (!radeon_connector->ddc_bus) goto failed; } @@ -1156,7 +1164,7 @@ radeon_add_atom_connector(struct drm_device *dev, drm_connector_init(dev, &radeon_connector->base, &radeon_dvi_connector_funcs, connector_type); drm_connector_helper_add(&radeon_connector->base, &radeon_dvi_connector_helper_funcs); if (i2c_bus->valid) { - radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "HDMI"); + radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus); if (!radeon_connector->ddc_bus) goto failed; } @@ -1187,10 +1195,7 @@ radeon_add_atom_connector(struct drm_device *dev, radeon_dig_connector->dp_i2c_bus = radeon_i2c_create_dp(dev, i2c_bus, "DP-auxch"); if (!radeon_dig_connector->dp_i2c_bus) goto failed; - if (connector_type == DRM_MODE_CONNECTOR_eDP) - radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "eDP"); - else - radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "DP"); + radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus); if (!radeon_connector->ddc_bus) goto failed; } @@ -1230,7 +1235,7 @@ radeon_add_atom_connector(struct drm_device *dev, drm_connector_init(dev, &radeon_connector->base, &radeon_lvds_connector_funcs, connector_type); drm_connector_helper_add(&radeon_connector->base, &radeon_lvds_connector_helper_funcs); if (i2c_bus->valid) { - radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "LVDS"); + radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus); if (!radeon_connector->ddc_bus) goto failed; } @@ -1252,8 +1257,6 @@ radeon_add_atom_connector(struct drm_device *dev, return; failed: - if (radeon_connector->ddc_bus) - radeon_i2c_destroy(radeon_connector->ddc_bus); drm_connector_cleanup(connector); kfree(connector); } @@ -1300,7 +1303,7 @@ radeon_add_legacy_connector(struct drm_device *dev, drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type); drm_connector_helper_add(&radeon_connector->base, &radeon_vga_connector_helper_funcs); if (i2c_bus->valid) { - radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "VGA"); + radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus); if (!radeon_connector->ddc_bus) goto failed; } @@ -1316,7 +1319,7 @@ radeon_add_legacy_connector(struct drm_device *dev, drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type); drm_connector_helper_add(&radeon_connector->base, &radeon_vga_connector_helper_funcs); if (i2c_bus->valid) { - radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "DVI"); + radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus); if (!radeon_connector->ddc_bus) goto failed; } @@ -1332,7 +1335,7 @@ radeon_add_legacy_connector(struct drm_device *dev, drm_connector_init(dev, &radeon_connector->base, &radeon_dvi_connector_funcs, connector_type); drm_connector_helper_add(&radeon_connector->base, &radeon_dvi_connector_helper_funcs); if (i2c_bus->valid) { - radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "DVI"); + radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus); if (!radeon_connector->ddc_bus) goto failed; } @@ -1372,7 +1375,7 @@ radeon_add_legacy_connector(struct drm_device *dev, drm_connector_init(dev, &radeon_connector->base, &radeon_lvds_connector_funcs, connector_type); drm_connector_helper_add(&radeon_connector->base, &radeon_lvds_connector_helper_funcs); if (i2c_bus->valid) { - radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "LVDS"); + radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus); if (!radeon_connector->ddc_bus) goto failed; } @@ -1393,8 +1396,6 @@ radeon_add_legacy_connector(struct drm_device *dev, return; failed: - if (radeon_connector->ddc_bus) - radeon_i2c_destroy(radeon_connector->ddc_bus); drm_connector_cleanup(connector); kfree(connector); } diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c index ae0fb7356e6..fcc79b5d22d 100644 --- a/drivers/gpu/drm/radeon/radeon_cs.c +++ b/drivers/gpu/drm/radeon/radeon_cs.c @@ -72,7 +72,7 @@ int radeon_cs_parser_relocs(struct radeon_cs_parser *p) if (p->relocs[i].gobj == NULL) { DRM_ERROR("gem object lookup failed 0x%x\n", r->handle); - return -EINVAL; + return -ENOENT; } p->relocs_ptr[i] = &p->relocs[i]; p->relocs[i].robj = p->relocs[i].gobj->driver_private; diff --git a/drivers/gpu/drm/radeon/radeon_cursor.c b/drivers/gpu/drm/radeon/radeon_cursor.c index 4eb67c0e099..5731fc9b1ae 100644 --- a/drivers/gpu/drm/radeon/radeon_cursor.c +++ b/drivers/gpu/drm/radeon/radeon_cursor.c @@ -170,7 +170,7 @@ int radeon_crtc_cursor_set(struct drm_crtc *crtc, obj = drm_gem_object_lookup(crtc->dev, file_priv, handle); if (!obj) { DRM_ERROR("Cannot find cursor object %x for crtc %d\n", handle, radeon_crtc->crtc_id); - return -EINVAL; + return -ENOENT; } ret = radeon_gem_object_pin(obj, RADEON_GEM_DOMAIN_VRAM, &gpu_addr); diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index a64811a9451..4f7a170d156 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -347,7 +347,8 @@ int radeon_dummy_page_init(struct radeon_device *rdev) return -ENOMEM; rdev->dummy_page.addr = pci_map_page(rdev->pdev, rdev->dummy_page.page, 0, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); - if (!rdev->dummy_page.addr) { + if (pci_dma_mapping_error(rdev->pdev, rdev->dummy_page.addr)) { + dev_err(&rdev->pdev->dev, "Failed to DMA MAP the dummy page\n"); __free_page(rdev->dummy_page.page); rdev->dummy_page.page = NULL; return -ENOMEM; diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index 74dac9635d7..5764f4d3b4f 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -161,17 +161,13 @@ void radeon_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, } static void radeon_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, - u16 *blue, uint32_t size) + u16 *blue, uint32_t start, uint32_t size) { struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); - int i; - - if (size != 256) { - return; - } + int end = (start + size > 256) ? 256 : start + size, i; /* userspace palettes are always correct as is */ - for (i = 0; i < 256; i++) { + for (i = start; i < end; i++) { radeon_crtc->lut_r[i] = red[i] >> 6; radeon_crtc->lut_g[i] = green[i] >> 6; radeon_crtc->lut_b[i] = blue[i] >> 6; @@ -319,6 +315,10 @@ static void radeon_print_display_setup(struct drm_device *dev) radeon_connector->ddc_bus->rec.en_data_reg, radeon_connector->ddc_bus->rec.y_clk_reg, radeon_connector->ddc_bus->rec.y_data_reg); + if (radeon_connector->router_bus) + DRM_INFO(" DDC Router 0x%x/0x%x\n", + radeon_connector->router.mux_control_pin, + radeon_connector->router.mux_state); } else { if (connector->connector_type == DRM_MODE_CONNECTOR_VGA || connector->connector_type == DRM_MODE_CONNECTOR_DVII || @@ -395,6 +395,10 @@ int radeon_ddc_get_modes(struct radeon_connector *radeon_connector) struct radeon_device *rdev = dev->dev_private; int ret = 0; + /* on hw with routers, select right port */ + if (radeon_connector->router.valid) + radeon_router_select_port(radeon_connector); + if ((radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_DisplayPort) || (radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_eDP)) { struct radeon_connector_atom_dig *dig = radeon_connector->con_priv; @@ -425,6 +429,10 @@ static int radeon_ddc_dump(struct drm_connector *connector) struct radeon_connector *radeon_connector = to_radeon_connector(connector); int ret = 0; + /* on hw with routers, select right port */ + if (radeon_connector->router.valid) + radeon_router_select_port(radeon_connector); + if (!radeon_connector->ddc_bus) return -1; edid = drm_get_edid(connector, &radeon_connector->ddc_bus->adapter); @@ -876,13 +884,12 @@ radeon_user_framebuffer_create(struct drm_device *dev, if (obj == NULL) { dev_err(&dev->pdev->dev, "No GEM object associated to handle 0x%08X, " "can't create framebuffer\n", mode_cmd->handle); - return NULL; + return ERR_PTR(-ENOENT); } radeon_fb = kzalloc(sizeof(*radeon_fb), GFP_KERNEL); - if (radeon_fb == NULL) { - return NULL; - } + if (radeon_fb == NULL) + return ERR_PTR(-ENOMEM); radeon_framebuffer_init(dev, radeon_fb, mode_cmd, obj); @@ -1040,6 +1047,9 @@ int radeon_modeset_init(struct radeon_device *rdev) return ret; } + /* init i2c buses */ + radeon_i2c_init(rdev); + /* check combios for a valid hardcoded EDID - Sun servers */ if (!rdev->is_atom_bios) { /* check for hardcoded EDID in BIOS */ @@ -1080,6 +1090,8 @@ void radeon_modeset_fini(struct radeon_device *rdev) drm_mode_config_cleanup(rdev->ddev); rdev->mode_info.mode_config_initialized = false; } + /* free i2c buses */ + radeon_i2c_fini(rdev); } bool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc, diff --git a/drivers/gpu/drm/radeon/radeon_gem.c b/drivers/gpu/drm/radeon/radeon_gem.c index a72a3ee5d69..c578f265b24 100644 --- a/drivers/gpu/drm/radeon/radeon_gem.c +++ b/drivers/gpu/drm/radeon/radeon_gem.c @@ -226,7 +226,7 @@ int radeon_gem_set_domain_ioctl(struct drm_device *dev, void *data, /* just do a BO wait for now */ gobj = drm_gem_object_lookup(dev, filp, args->handle); if (gobj == NULL) { - return -EINVAL; + return -ENOENT; } robj = gobj->driver_private; @@ -245,7 +245,7 @@ int radeon_gem_mmap_ioctl(struct drm_device *dev, void *data, gobj = drm_gem_object_lookup(dev, filp, args->handle); if (gobj == NULL) { - return -EINVAL; + return -ENOENT; } robj = gobj->driver_private; args->addr_ptr = radeon_bo_mmap_offset(robj); @@ -264,7 +264,7 @@ int radeon_gem_busy_ioctl(struct drm_device *dev, void *data, gobj = drm_gem_object_lookup(dev, filp, args->handle); if (gobj == NULL) { - return -EINVAL; + return -ENOENT; } robj = gobj->driver_private; r = radeon_bo_wait(robj, &cur_placement, true); @@ -294,7 +294,7 @@ int radeon_gem_wait_idle_ioctl(struct drm_device *dev, void *data, gobj = drm_gem_object_lookup(dev, filp, args->handle); if (gobj == NULL) { - return -EINVAL; + return -ENOENT; } robj = gobj->driver_private; r = radeon_bo_wait(robj, NULL, false); @@ -316,7 +316,7 @@ int radeon_gem_set_tiling_ioctl(struct drm_device *dev, void *data, DRM_DEBUG("%d \n", args->handle); gobj = drm_gem_object_lookup(dev, filp, args->handle); if (gobj == NULL) - return -EINVAL; + return -ENOENT; robj = gobj->driver_private; r = radeon_bo_set_tiling_flags(robj, args->tiling_flags, args->pitch); drm_gem_object_unreference_unlocked(gobj); @@ -334,7 +334,7 @@ int radeon_gem_get_tiling_ioctl(struct drm_device *dev, void *data, DRM_DEBUG("\n"); gobj = drm_gem_object_lookup(dev, filp, args->handle); if (gobj == NULL) - return -EINVAL; + return -ENOENT; rbo = gobj->driver_private; r = radeon_bo_reserve(rbo, false); if (unlikely(r != 0)) diff --git a/drivers/gpu/drm/radeon/radeon_i2c.c b/drivers/gpu/drm/radeon/radeon_i2c.c index 5def6f5dff3..bfd2ce5f537 100644 --- a/drivers/gpu/drm/radeon/radeon_i2c.c +++ b/drivers/gpu/drm/radeon/radeon_i2c.c @@ -52,6 +52,10 @@ bool radeon_ddc_probe(struct radeon_connector *radeon_connector) } }; + /* on hw with routers, select right port */ + if (radeon_connector->router.valid) + radeon_router_select_port(radeon_connector); + ret = i2c_transfer(&radeon_connector->ddc_bus->adapter, msgs, 2); if (ret == 2) return true; @@ -960,6 +964,59 @@ void radeon_i2c_destroy(struct radeon_i2c_chan *i2c) kfree(i2c); } +/* Add the default buses */ +void radeon_i2c_init(struct radeon_device *rdev) +{ + if (rdev->is_atom_bios) + radeon_atombios_i2c_init(rdev); + else + radeon_combios_i2c_init(rdev); +} + +/* remove all the buses */ +void radeon_i2c_fini(struct radeon_device *rdev) +{ + int i; + + for (i = 0; i < RADEON_MAX_I2C_BUS; i++) { + if (rdev->i2c_bus[i]) { + radeon_i2c_destroy(rdev->i2c_bus[i]); + rdev->i2c_bus[i] = NULL; + } + } +} + +/* Add additional buses */ +void radeon_i2c_add(struct radeon_device *rdev, + struct radeon_i2c_bus_rec *rec, + const char *name) +{ + struct drm_device *dev = rdev->ddev; + int i; + + for (i = 0; i < RADEON_MAX_I2C_BUS; i++) { + if (!rdev->i2c_bus[i]) { + rdev->i2c_bus[i] = radeon_i2c_create(dev, rec, name); + return; + } + } +} + +/* looks up bus based on id */ +struct radeon_i2c_chan *radeon_i2c_lookup(struct radeon_device *rdev, + struct radeon_i2c_bus_rec *i2c_bus) +{ + int i; + + for (i = 0; i < RADEON_MAX_I2C_BUS; i++) { + if (rdev->i2c_bus[i] && + (rdev->i2c_bus[i]->rec.i2c_id == i2c_bus->i2c_id)) { + return rdev->i2c_bus[i]; + } + } + return NULL; +} + struct drm_encoder *radeon_best_encoder(struct drm_connector *connector) { return NULL; @@ -1020,3 +1077,28 @@ void radeon_i2c_put_byte(struct radeon_i2c_chan *i2c_bus, addr, val); } +/* router switching */ +void radeon_router_select_port(struct radeon_connector *radeon_connector) +{ + u8 val; + + if (!radeon_connector->router.valid) + return; + + radeon_i2c_get_byte(radeon_connector->router_bus, + radeon_connector->router.i2c_addr, + 0x3, &val); + val &= radeon_connector->router.mux_control_pin; + radeon_i2c_put_byte(radeon_connector->router_bus, + radeon_connector->router.i2c_addr, + 0x3, val); + radeon_i2c_get_byte(radeon_connector->router_bus, + radeon_connector->router.i2c_addr, + 0x1, &val); + val &= radeon_connector->router.mux_control_pin; + val |= radeon_connector->router.mux_state; + radeon_i2c_put_byte(radeon_connector->router_bus, + radeon_connector->router.i2c_addr, + 0x1, val); +} + diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c index ddcd3b13f15..b1c8ace5f08 100644 --- a/drivers/gpu/drm/radeon/radeon_kms.c +++ b/drivers/gpu/drm/radeon/radeon_kms.c @@ -112,7 +112,9 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) info = data; value_ptr = (uint32_t *)((unsigned long)info->value); - value = *value_ptr; + if (DRM_COPY_FROM_USER(&value, value_ptr, sizeof(value))) + return -EFAULT; + switch (info->request) { case RADEON_INFO_DEVICE_ID: value = dev->pci_device; @@ -160,13 +162,27 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) return -EINVAL; } case RADEON_INFO_WANT_HYPERZ: + /* The "value" here is both an input and output parameter. + * If the input value is 1, filp requests hyper-z access. + * If the input value is 0, filp revokes its hyper-z access. + * + * When returning, the value is 1 if filp owns hyper-z access, + * 0 otherwise. */ + if (value >= 2) { + DRM_DEBUG_KMS("WANT_HYPERZ: invalid value %d\n", value); + return -EINVAL; + } mutex_lock(&dev->struct_mutex); - if (rdev->hyperz_filp) - value = 0; - else { - rdev->hyperz_filp = filp; - value = 1; + if (value == 1) { + /* wants hyper-z */ + if (!rdev->hyperz_filp) + rdev->hyperz_filp = filp; + } else if (value == 0) { + /* revokes hyper-z */ + if (rdev->hyperz_filp == filp) + rdev->hyperz_filp = NULL; } + value = rdev->hyperz_filp == filp ? 1 : 0; mutex_unlock(&dev->struct_mutex); break; default: diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h index 71aea4037e9..5bbc086b926 100644 --- a/drivers/gpu/drm/radeon/radeon_mode.h +++ b/drivers/gpu/drm/radeon/radeon_mode.h @@ -82,6 +82,8 @@ enum radeon_hpd_id { RADEON_HPD_NONE = 0xff, }; +#define RADEON_MAX_I2C_BUS 16 + /* radeon gpio-based i2c * 1. "mask" reg and bits * grabs the gpio pins for software use @@ -398,6 +400,16 @@ struct radeon_hpd { struct radeon_gpio_rec gpio; }; +struct radeon_router { + bool valid; + u32 router_id; + struct radeon_i2c_bus_rec i2c_info; + u8 i2c_addr; + u8 mux_type; + u8 mux_control_pin; + u8 mux_state; +}; + struct radeon_connector { struct drm_connector base; uint32_t connector_id; @@ -413,6 +425,8 @@ struct radeon_connector { bool dac_load_detect; uint16_t connector_object_id; struct radeon_hpd hpd; + struct radeon_router router; + struct radeon_i2c_chan *router_bus; }; struct radeon_framebuffer { @@ -445,6 +459,15 @@ extern void atombios_dig_transmitter_setup(struct drm_encoder *encoder, extern int radeon_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode, uint8_t write_byte, uint8_t *read_byte); +extern void radeon_i2c_init(struct radeon_device *rdev); +extern void radeon_i2c_fini(struct radeon_device *rdev); +extern void radeon_combios_i2c_init(struct radeon_device *rdev); +extern void radeon_atombios_i2c_init(struct radeon_device *rdev); +extern void radeon_i2c_add(struct radeon_device *rdev, + struct radeon_i2c_bus_rec *rec, + const char *name); +extern struct radeon_i2c_chan *radeon_i2c_lookup(struct radeon_device *rdev, + struct radeon_i2c_bus_rec *i2c_bus); extern struct radeon_i2c_chan *radeon_i2c_create_dp(struct drm_device *dev, struct radeon_i2c_bus_rec *rec, const char *name); @@ -460,6 +483,7 @@ extern void radeon_i2c_put_byte(struct radeon_i2c_chan *i2c, u8 slave_addr, u8 addr, u8 val); +extern void radeon_router_select_port(struct radeon_connector *radeon_connector); extern bool radeon_ddc_probe(struct radeon_connector *radeon_connector); extern int radeon_ddc_get_modes(struct radeon_connector *radeon_connector); diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index 95f8b3a3c43..58038f5cab3 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -472,9 +472,9 @@ static const struct attribute_group hwmon_attrgroup = { .attrs = hwmon_attributes, }; -static void radeon_hwmon_init(struct radeon_device *rdev) +static int radeon_hwmon_init(struct radeon_device *rdev) { - int err; + int err = 0; rdev->pm.int_hwmon_dev = NULL; @@ -483,15 +483,26 @@ static void radeon_hwmon_init(struct radeon_device *rdev) case THERMAL_TYPE_RV770: case THERMAL_TYPE_EVERGREEN: rdev->pm.int_hwmon_dev = hwmon_device_register(rdev->dev); + if (IS_ERR(rdev->pm.int_hwmon_dev)) { + err = PTR_ERR(rdev->pm.int_hwmon_dev); + dev_err(rdev->dev, + "Unable to register hwmon device: %d\n", err); + break; + } dev_set_drvdata(rdev->pm.int_hwmon_dev, rdev->ddev); err = sysfs_create_group(&rdev->pm.int_hwmon_dev->kobj, &hwmon_attrgroup); - if (err) - DRM_ERROR("Unable to create hwmon sysfs file: %d\n", err); + if (err) { + dev_err(rdev->dev, + "Unable to create hwmon sysfs file: %d\n", err); + hwmon_device_unregister(rdev->dev); + } break; default: break; } + + return err; } static void radeon_hwmon_fini(struct radeon_device *rdev) @@ -540,6 +551,7 @@ void radeon_pm_resume(struct radeon_device *rdev) int radeon_pm_init(struct radeon_device *rdev) { int ret; + /* default to profile method */ rdev->pm.pm_method = PM_METHOD_PROFILE; rdev->pm.profile = PM_PROFILE_DEFAULT; @@ -561,7 +573,9 @@ int radeon_pm_init(struct radeon_device *rdev) } /* set up the internal thermal sensor if applicable */ - radeon_hwmon_init(rdev); + ret = radeon_hwmon_init(rdev); + if (ret) + return ret; if (rdev->pm.num_power_states > 1) { /* where's the best place to put these? */ ret = device_create_file(rdev->dev, &dev_attr_power_profile); diff --git a/drivers/gpu/drm/radeon/reg_srcs/rv515 b/drivers/gpu/drm/radeon/reg_srcs/rv515 index 8293855f5f0..b3f9f1d9200 100644 --- a/drivers/gpu/drm/radeon/reg_srcs/rv515 +++ b/drivers/gpu/drm/radeon/reg_srcs/rv515 @@ -316,6 +316,7 @@ rv515 0x6d40 0x4BD0 FG_FOG_COLOR_B 0x4BD4 FG_ALPHA_FUNC 0x4BD8 FG_DEPTH_SRC +0x4BE0 FG_ALPHA_VALUE 0x4C00 US_ALU_CONST_R_0 0x4C04 US_ALU_CONST_G_0 0x4C08 US_ALU_CONST_B_0 diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 437ac786277..64d7f47df86 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -737,7 +737,7 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev, if (ret) { DRM_ERROR("failed to create vmw_framebuffer: %i\n", ret); - return NULL; + return ERR_PTR(ret); } return &vfb->base; @@ -747,7 +747,7 @@ try_dmabuf: ret = vmw_user_dmabuf_lookup(tfile, mode_cmd->handle, &bo); if (ret) { DRM_ERROR("failed to find buffer: %i\n", ret); - return NULL; + return ERR_PTR(-ENOENT); } ret = vmw_kms_new_framebuffer_dmabuf(dev_priv, bo, &vfb, @@ -758,7 +758,7 @@ try_dmabuf: if (ret) { DRM_ERROR("failed to create vmw_framebuffer: %i\n", ret); - return NULL; + return ERR_PTR(ret); } return &vfb->base; @@ -768,7 +768,7 @@ err_not_scanout: /* vmw_user_surface_lookup takes one ref */ vmw_surface_unreference(&surface); - return NULL; + return ERR_PTR(-EINVAL); } static struct drm_mode_config_funcs vmw_kms_funcs = { diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c index cfaf690a5b2..2ff5cf78235 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c @@ -79,7 +79,7 @@ static void vmw_ldu_crtc_restore(struct drm_crtc *crtc) static void vmw_ldu_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, - uint32_t size) + uint32_t start, uint32_t size) { } diff --git a/drivers/hwmon/ams/ams.h b/drivers/hwmon/ams/ams.h index b28d7e27a03..90f094d4545 100644 --- a/drivers/hwmon/ams/ams.h +++ b/drivers/hwmon/ams/ams.h @@ -23,7 +23,7 @@ struct ams { /* General properties */ struct device_node *of_node; - struct of_device *of_dev; + struct platform_device *of_dev; char has_device; char vflag; u32 orient1; diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index a92e28a3576..c070c9714cb 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -501,13 +501,10 @@ static int __cpuinit coretemp_cpu_callback(struct notifier_block *nfb, switch (action) { case CPU_ONLINE: - case CPU_ONLINE_FROZEN: case CPU_DOWN_FAILED: - case CPU_DOWN_FAILED_FROZEN: coretemp_device_add(cpu); break; case CPU_DOWN_PREPARE: - case CPU_DOWN_PREPARE_FROZEN: coretemp_device_remove(cpu); break; } diff --git a/drivers/hwmon/mc13783-adc.c b/drivers/hwmon/mc13783-adc.c index ce3c7bc8181..d5226c9e120 100644 --- a/drivers/hwmon/mc13783-adc.c +++ b/drivers/hwmon/mc13783-adc.c @@ -18,7 +18,7 @@ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <linux/mfd/mc13783-private.h> +#include <linux/mfd/mc13783.h> #include <linux/platform_device.h> #include <linux/hwmon-sysfs.h> #include <linux/kernel.h> @@ -144,6 +144,14 @@ static const struct attribute_group mc13783_group_ts = { .attrs = mc13783_attr_ts, }; +static int mc13783_adc_use_touchscreen(struct platform_device *pdev) +{ + struct mc13783_adc_priv *priv = platform_get_drvdata(pdev); + unsigned flags = mc13783_get_flags(priv->mc13783); + + return flags & MC13783_USE_TOUCHSCREEN; +} + static int __init mc13783_adc_probe(struct platform_device *pdev) { struct mc13783_adc_priv *priv; @@ -162,10 +170,11 @@ static int __init mc13783_adc_probe(struct platform_device *pdev) if (ret) goto out_err_create1; - if (!(priv->mc13783->flags & MC13783_USE_TOUCHSCREEN)) + if (!mc13783_adc_use_touchscreen(pdev)) { ret = sysfs_create_group(&pdev->dev.kobj, &mc13783_group_ts); if (ret) goto out_err_create2; + } priv->hwmon_dev = hwmon_device_register(&pdev->dev); if (IS_ERR(priv->hwmon_dev)) { @@ -180,7 +189,7 @@ static int __init mc13783_adc_probe(struct platform_device *pdev) out_err_register: - if (!(priv->mc13783->flags & MC13783_USE_TOUCHSCREEN)) + if (!mc13783_adc_use_touchscreen(pdev)) sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_ts); out_err_create2: @@ -199,7 +208,7 @@ static int __devexit mc13783_adc_remove(struct platform_device *pdev) hwmon_device_unregister(priv->hwmon_dev); - if (!(priv->mc13783->flags & MC13783_USE_TOUCHSCREEN)) + if (!mc13783_adc_use_touchscreen(pdev)) sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_ts); sysfs_remove_group(&pdev->dev.kobj, &mc13783_group); diff --git a/drivers/hwmon/ultra45_env.c b/drivers/hwmon/ultra45_env.c index 89643261ccd..d863e13a50b 100644 --- a/drivers/hwmon/ultra45_env.c +++ b/drivers/hwmon/ultra45_env.c @@ -234,7 +234,7 @@ static const struct attribute_group env_group = { .attrs = env_attributes, }; -static int __devinit env_probe(struct of_device *op, +static int __devinit env_probe(struct platform_device *op, const struct of_device_id *match) { struct env *p = kzalloc(sizeof(*p), GFP_KERNEL); @@ -276,7 +276,7 @@ out_free: goto out; } -static int __devexit env_remove(struct of_device *op) +static int __devexit env_remove(struct platform_device *op) { struct env *p = dev_get_drvdata(&op->dev); diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index bceafbfa726..15a9702e294 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -521,12 +521,19 @@ config I2C_PXA_SLAVE is necessary for systems where the PXA may be a target on the I2C bus. +config HAVE_S3C2410_I2C + bool + help + This will include I2C support for Samsung SoCs. If you want to + include I2C support for any machine, kindly select this in the + respective Kconfig file. + config I2C_S3C2410 tristate "S3C2410 I2C Driver" - depends on ARCH_S3C2410 || ARCH_S3C64XX + depends on HAVE_S3C2410_I2C help Say Y here to include support for I2C controller in the - Samsung S3C2410 based System-on-Chip devices. + Samsung SoCs. config I2C_S6000 tristate "S6000 I2C support" @@ -549,7 +556,7 @@ config I2C_SH7760 config I2C_SH_MOBILE tristate "SuperH Mobile I2C Controller" - depends on SUPERH + depends on SUPERH || ARCH_SHMOBILE help If you say yes to this option, support will be included for the built-in I2C interface on the Renesas SH-Mobile processor. diff --git a/drivers/i2c/busses/i2c-cpm.c b/drivers/i2c/busses/i2c-cpm.c index e591de1bc70..f7bd2613cec 100644 --- a/drivers/i2c/busses/i2c-cpm.c +++ b/drivers/i2c/busses/i2c-cpm.c @@ -105,7 +105,7 @@ struct i2c_reg { struct cpm_i2c { char *base; - struct of_device *ofdev; + struct platform_device *ofdev; struct i2c_adapter adap; uint dp_addr; int version; /* CPM1=1, CPM2=2 */ @@ -428,7 +428,7 @@ static const struct i2c_adapter cpm_ops = { static int __devinit cpm_i2c_setup(struct cpm_i2c *cpm) { - struct of_device *ofdev = cpm->ofdev; + struct platform_device *ofdev = cpm->ofdev; const u32 *data; int len, ret, i; void __iomem *i2c_base; @@ -634,7 +634,7 @@ static void cpm_i2c_shutdown(struct cpm_i2c *cpm) cpm_muram_free(cpm->i2c_addr); } -static int __devinit cpm_i2c_probe(struct of_device *ofdev, +static int __devinit cpm_i2c_probe(struct platform_device *ofdev, const struct of_device_id *match) { int result, len; @@ -687,7 +687,7 @@ out_free: return result; } -static int __devexit cpm_i2c_remove(struct of_device *ofdev) +static int __devexit cpm_i2c_remove(struct platform_device *ofdev) { struct cpm_i2c *cpm = dev_get_drvdata(&ofdev->dev); diff --git a/drivers/i2c/busses/i2c-ibm_iic.c b/drivers/i2c/busses/i2c-ibm_iic.c index 1168d61418c..43ca32fddde 100644 --- a/drivers/i2c/busses/i2c-ibm_iic.c +++ b/drivers/i2c/busses/i2c-ibm_iic.c @@ -661,7 +661,7 @@ static inline u8 iic_clckdiv(unsigned int opb) return (u8)((opb + 9) / 10 - 1); } -static int __devinit iic_request_irq(struct of_device *ofdev, +static int __devinit iic_request_irq(struct platform_device *ofdev, struct ibm_iic_private *dev) { struct device_node *np = ofdev->dev.of_node; @@ -692,7 +692,7 @@ static int __devinit iic_request_irq(struct of_device *ofdev, /* * Register single IIC interface */ -static int __devinit iic_probe(struct of_device *ofdev, +static int __devinit iic_probe(struct platform_device *ofdev, const struct of_device_id *match) { struct device_node *np = ofdev->dev.of_node; @@ -780,7 +780,7 @@ error_cleanup: /* * Cleanup initialized IIC interface */ -static int __devexit iic_remove(struct of_device *ofdev) +static int __devexit iic_remove(struct platform_device *ofdev) { struct ibm_iic_private *dev = dev_get_drvdata(&ofdev->dev); diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c index 6545d1c99b6..a1c419a716a 100644 --- a/drivers/i2c/busses/i2c-mpc.c +++ b/drivers/i2c/busses/i2c-mpc.c @@ -560,7 +560,7 @@ static struct i2c_adapter mpc_ops = { .timeout = HZ, }; -static int __devinit fsl_i2c_probe(struct of_device *op, +static int __devinit fsl_i2c_probe(struct platform_device *op, const struct of_device_id *match) { struct mpc_i2c *i2c; @@ -646,7 +646,7 @@ static int __devinit fsl_i2c_probe(struct of_device *op, return result; }; -static int __devexit fsl_i2c_remove(struct of_device *op) +static int __devexit fsl_i2c_remove(struct platform_device *op) { struct mpc_i2c *i2c = dev_get_drvdata(&op->dev); diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index ffb405d7c6f..598c49acaeb 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -119,8 +119,10 @@ struct sh_mobile_i2c_data { struct i2c_adapter adap; struct clk *clk; + u_int8_t icic; u_int8_t iccl; u_int8_t icch; + u_int8_t flags; spinlock_t lock; wait_queue_head_t wait; @@ -129,15 +131,17 @@ struct sh_mobile_i2c_data { int sr; }; +#define IIC_FLAG_HAS_ICIC67 (1 << 0) + #define NORMAL_SPEED 100000 /* FAST_SPEED 400000 */ /* Register offsets */ -#define ICDR(pd) (pd->reg + 0x00) -#define ICCR(pd) (pd->reg + 0x04) -#define ICSR(pd) (pd->reg + 0x08) -#define ICIC(pd) (pd->reg + 0x0c) -#define ICCL(pd) (pd->reg + 0x10) -#define ICCH(pd) (pd->reg + 0x14) +#define ICDR 0x00 +#define ICCR 0x04 +#define ICSR 0x08 +#define ICIC 0x0c +#define ICCL 0x10 +#define ICCH 0x14 /* Register bits */ #define ICCR_ICE 0x80 @@ -155,11 +159,32 @@ struct sh_mobile_i2c_data { #define ICSR_WAIT 0x02 #define ICSR_DTE 0x01 +#define ICIC_ICCLB8 0x80 +#define ICIC_ICCHB8 0x40 #define ICIC_ALE 0x08 #define ICIC_TACKE 0x04 #define ICIC_WAITE 0x02 #define ICIC_DTEE 0x01 +static void iic_wr(struct sh_mobile_i2c_data *pd, int offs, unsigned char data) +{ + if (offs == ICIC) + data |= pd->icic; + + iowrite8(data, pd->reg + offs); +} + +static unsigned char iic_rd(struct sh_mobile_i2c_data *pd, int offs) +{ + return ioread8(pd->reg + offs); +} + +static void iic_set_clr(struct sh_mobile_i2c_data *pd, int offs, + unsigned char set, unsigned char clr) +{ + iic_wr(pd, offs, (iic_rd(pd, offs) | set) & ~clr); +} + static void activate_ch(struct sh_mobile_i2c_data *pd) { unsigned long i2c_clk; @@ -187,6 +212,14 @@ static void activate_ch(struct sh_mobile_i2c_data *pd) else pd->iccl = (u_int8_t)(num/denom); + /* one more bit of ICCL in ICIC */ + if (pd->flags & IIC_FLAG_HAS_ICIC67) { + if ((num/denom) > 0xff) + pd->icic |= ICIC_ICCLB8; + else + pd->icic &= ~ICIC_ICCLB8; + } + /* Calculate the value for icch. From the data sheet: icch = (p clock / transfer rate) * (H / (L + H)) */ num = i2c_clk * 4; @@ -196,25 +229,33 @@ static void activate_ch(struct sh_mobile_i2c_data *pd) else pd->icch = (u_int8_t)(num/denom); + /* one more bit of ICCH in ICIC */ + if (pd->flags & IIC_FLAG_HAS_ICIC67) { + if ((num/denom) > 0xff) + pd->icic |= ICIC_ICCHB8; + else + pd->icic &= ~ICIC_ICCHB8; + } + /* Enable channel and configure rx ack */ - iowrite8(ioread8(ICCR(pd)) | ICCR_ICE, ICCR(pd)); + iic_set_clr(pd, ICCR, ICCR_ICE, 0); /* Mask all interrupts */ - iowrite8(0, ICIC(pd)); + iic_wr(pd, ICIC, 0); /* Set the clock */ - iowrite8(pd->iccl, ICCL(pd)); - iowrite8(pd->icch, ICCH(pd)); + iic_wr(pd, ICCL, pd->iccl); + iic_wr(pd, ICCH, pd->icch); } static void deactivate_ch(struct sh_mobile_i2c_data *pd) { /* Clear/disable interrupts */ - iowrite8(0, ICSR(pd)); - iowrite8(0, ICIC(pd)); + iic_wr(pd, ICSR, 0); + iic_wr(pd, ICIC, 0); /* Disable channel */ - iowrite8(ioread8(ICCR(pd)) & ~ICCR_ICE, ICCR(pd)); + iic_set_clr(pd, ICCR, 0, ICCR_ICE); /* Disable clock and mark device as idle */ clk_disable(pd->clk); @@ -233,35 +274,35 @@ static unsigned char i2c_op(struct sh_mobile_i2c_data *pd, switch (op) { case OP_START: /* issue start and trigger DTE interrupt */ - iowrite8(0x94, ICCR(pd)); + iic_wr(pd, ICCR, 0x94); break; case OP_TX_FIRST: /* disable DTE interrupt and write data */ - iowrite8(ICIC_WAITE | ICIC_ALE | ICIC_TACKE, ICIC(pd)); - iowrite8(data, ICDR(pd)); + iic_wr(pd, ICIC, ICIC_WAITE | ICIC_ALE | ICIC_TACKE); + iic_wr(pd, ICDR, data); break; case OP_TX: /* write data */ - iowrite8(data, ICDR(pd)); + iic_wr(pd, ICDR, data); break; case OP_TX_STOP: /* write data and issue a stop afterwards */ - iowrite8(data, ICDR(pd)); - iowrite8(0x90, ICCR(pd)); + iic_wr(pd, ICDR, data); + iic_wr(pd, ICCR, 0x90); break; case OP_TX_TO_RX: /* select read mode */ - iowrite8(0x81, ICCR(pd)); + iic_wr(pd, ICCR, 0x81); break; case OP_RX: /* just read data */ - ret = ioread8(ICDR(pd)); + ret = iic_rd(pd, ICDR); break; case OP_RX_STOP: /* enable DTE interrupt, issue stop */ - iowrite8(ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE, - ICIC(pd)); - iowrite8(0xc0, ICCR(pd)); + iic_wr(pd, ICIC, + ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE); + iic_wr(pd, ICCR, 0xc0); break; case OP_RX_STOP_DATA: /* enable DTE interrupt, read data, issue stop */ - iowrite8(ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE, - ICIC(pd)); - ret = ioread8(ICDR(pd)); - iowrite8(0xc0, ICCR(pd)); + iic_wr(pd, ICIC, + ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE); + ret = iic_rd(pd, ICDR); + iic_wr(pd, ICCR, 0xc0); break; } @@ -367,7 +408,7 @@ static irqreturn_t sh_mobile_i2c_isr(int irq, void *dev_id) unsigned char sr; int wakeup; - sr = ioread8(ICSR(pd)); + sr = iic_rd(pd, ICSR); pd->sr |= sr; /* remember state */ dev_dbg(pd->dev, "i2c_isr 0x%02x 0x%02x %s %d %d!\n", sr, pd->sr, @@ -376,7 +417,7 @@ static irqreturn_t sh_mobile_i2c_isr(int irq, void *dev_id) if (sr & (ICSR_AL | ICSR_TACK)) { /* don't interrupt transaction - continue to issue stop */ - iowrite8(sr & ~(ICSR_AL | ICSR_TACK), ICSR(pd)); + iic_wr(pd, ICSR, sr & ~(ICSR_AL | ICSR_TACK)); wakeup = 0; } else if (pd->msg->flags & I2C_M_RD) wakeup = sh_mobile_i2c_isr_rx(pd); @@ -384,7 +425,7 @@ static irqreturn_t sh_mobile_i2c_isr(int irq, void *dev_id) wakeup = sh_mobile_i2c_isr_tx(pd); if (sr & ICSR_WAIT) /* TODO: add delay here to support slow acks */ - iowrite8(sr & ~ICSR_WAIT, ICSR(pd)); + iic_wr(pd, ICSR, sr & ~ICSR_WAIT); if (wakeup) { pd->sr |= SW_DONE; @@ -402,21 +443,21 @@ static int start_ch(struct sh_mobile_i2c_data *pd, struct i2c_msg *usr_msg) } /* Initialize channel registers */ - iowrite8(ioread8(ICCR(pd)) & ~ICCR_ICE, ICCR(pd)); + iic_set_clr(pd, ICCR, 0, ICCR_ICE); /* Enable channel and configure rx ack */ - iowrite8(ioread8(ICCR(pd)) | ICCR_ICE, ICCR(pd)); + iic_set_clr(pd, ICCR, ICCR_ICE, 0); /* Set the clock */ - iowrite8(pd->iccl, ICCL(pd)); - iowrite8(pd->icch, ICCH(pd)); + iic_wr(pd, ICCL, pd->iccl); + iic_wr(pd, ICCH, pd->icch); pd->msg = usr_msg; pd->pos = -1; pd->sr = 0; /* Enable all interrupts to begin with */ - iowrite8(ICIC_WAITE | ICIC_ALE | ICIC_TACKE | ICIC_DTEE, ICIC(pd)); + iic_wr(pd, ICIC, ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE); return 0; } @@ -451,7 +492,7 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter, retry_count = 1000; again: - val = ioread8(ICSR(pd)); + val = iic_rd(pd, ICSR); dev_dbg(pd->dev, "val 0x%02x pd->sr 0x%02x\n", val, pd->sr); @@ -576,6 +617,12 @@ static int sh_mobile_i2c_probe(struct platform_device *dev) goto err_irq; } + /* The IIC blocks on SH-Mobile ARM processors + * come with two new bits in ICIC. + */ + if (size > 0x17) + pd->flags |= IIC_FLAG_HAS_ICIC67; + /* Enable Runtime PM for this device. * * Also tell the Runtime PM core to ignore children diff --git a/drivers/infiniband/hw/ehca/ehca_classes.h b/drivers/infiniband/hw/ehca/ehca_classes.h index 0136abd50dd..aaf6023a483 100644 --- a/drivers/infiniband/hw/ehca/ehca_classes.h +++ b/drivers/infiniband/hw/ehca/ehca_classes.h @@ -112,7 +112,7 @@ struct ehca_sport { struct ehca_shca { struct ib_device ib_device; - struct of_device *ofdev; + struct platform_device *ofdev; u8 num_ports; int hw_level; struct list_head shca_list; diff --git a/drivers/infiniband/hw/ehca/ehca_main.c b/drivers/infiniband/hw/ehca/ehca_main.c index cfc4de7a5da..c240e9972cb 100644 --- a/drivers/infiniband/hw/ehca/ehca_main.c +++ b/drivers/infiniband/hw/ehca/ehca_main.c @@ -713,7 +713,7 @@ static struct attribute_group ehca_dev_attr_grp = { .attrs = ehca_dev_attrs }; -static int __devinit ehca_probe(struct of_device *dev, +static int __devinit ehca_probe(struct platform_device *dev, const struct of_device_id *id) { struct ehca_shca *shca; @@ -879,7 +879,7 @@ probe1: return -EINVAL; } -static int __devexit ehca_remove(struct of_device *dev) +static int __devexit ehca_remove(struct platform_device *dev) { struct ehca_shca *shca = dev_get_drvdata(&dev->dev); unsigned long flags; diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index b171f63fe4d..9cc488d2149 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -395,6 +395,16 @@ config KEYBOARD_SH_KEYSC To compile this driver as a module, choose M here: the module will be called sh_keysc. +config KEYBOARD_STMPE + tristate "STMPE keypad support" + depends on MFD_STMPE + help + Say Y here if you want to use the keypad controller on STMPE I/O + expanders. + + To compile this driver as a module, choose M here: the module will be + called stmpe-keypad. + config KEYBOARD_DAVINCI tristate "TI DaVinci Key Scan" depends on ARCH_DAVINCI_DM365 diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 1a66d5f1ca8..504b591be0c 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o obj-$(CONFIG_KEYBOARD_QT2160) += qt2160.o obj-$(CONFIG_KEYBOARD_SAMSUNG) += samsung-keypad.o obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o +obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o diff --git a/drivers/input/keyboard/stmpe-keypad.c b/drivers/input/keyboard/stmpe-keypad.c new file mode 100644 index 00000000000..ab7610ca10e --- /dev/null +++ b/drivers/input/keyboard/stmpe-keypad.c @@ -0,0 +1,386 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * License Terms: GNU General Public License, version 2 + * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/input/matrix_keypad.h> +#include <linux/mfd/stmpe.h> + +/* These are at the same addresses in all STMPE variants */ +#define STMPE_KPC_COL 0x60 +#define STMPE_KPC_ROW_MSB 0x61 +#define STMPE_KPC_ROW_LSB 0x62 +#define STMPE_KPC_CTRL_MSB 0x63 +#define STMPE_KPC_CTRL_LSB 0x64 +#define STMPE_KPC_COMBI_KEY_0 0x65 +#define STMPE_KPC_COMBI_KEY_1 0x66 +#define STMPE_KPC_COMBI_KEY_2 0x67 +#define STMPE_KPC_DATA_BYTE0 0x68 +#define STMPE_KPC_DATA_BYTE1 0x69 +#define STMPE_KPC_DATA_BYTE2 0x6a +#define STMPE_KPC_DATA_BYTE3 0x6b +#define STMPE_KPC_DATA_BYTE4 0x6c + +#define STMPE_KPC_CTRL_LSB_SCAN (0x1 << 0) +#define STMPE_KPC_CTRL_LSB_DEBOUNCE (0x7f << 1) +#define STMPE_KPC_CTRL_MSB_SCAN_COUNT (0xf << 4) + +#define STMPE_KPC_ROW_MSB_ROWS 0xff + +#define STMPE_KPC_DATA_UP (0x1 << 7) +#define STMPE_KPC_DATA_ROW (0xf << 3) +#define STMPE_KPC_DATA_COL (0x7 << 0) +#define STMPE_KPC_DATA_NOKEY_MASK 0x78 + +#define STMPE_KEYPAD_MAX_DEBOUNCE 127 +#define STMPE_KEYPAD_MAX_SCAN_COUNT 15 + +#define STMPE_KEYPAD_MAX_ROWS 8 +#define STMPE_KEYPAD_MAX_COLS 8 +#define STMPE_KEYPAD_ROW_SHIFT 3 +#define STMPE_KEYPAD_KEYMAP_SIZE \ + (STMPE_KEYPAD_MAX_ROWS * STMPE_KEYPAD_MAX_COLS) + +/** + * struct stmpe_keypad_variant - model-specific attributes + * @auto_increment: whether the KPC_DATA_BYTE register address + * auto-increments on multiple read + * @num_data: number of data bytes + * @num_normal_data: number of normal keys' data bytes + * @max_cols: maximum number of columns supported + * @max_rows: maximum number of rows supported + * @col_gpios: bitmask of gpios which can be used for columns + * @row_gpios: bitmask of gpios which can be used for rows + */ +struct stmpe_keypad_variant { + bool auto_increment; + int num_data; + int num_normal_data; + int max_cols; + int max_rows; + unsigned int col_gpios; + unsigned int row_gpios; +}; + +static const struct stmpe_keypad_variant stmpe_keypad_variants[] = { + [STMPE1601] = { + .auto_increment = true, + .num_data = 5, + .num_normal_data = 3, + .max_cols = 8, + .max_rows = 8, + .col_gpios = 0x000ff, /* GPIO 0 - 7 */ + .row_gpios = 0x0ff00, /* GPIO 8 - 15 */ + }, + [STMPE2401] = { + .auto_increment = false, + .num_data = 3, + .num_normal_data = 2, + .max_cols = 8, + .max_rows = 12, + .col_gpios = 0x0000ff, /* GPIO 0 - 7*/ + .row_gpios = 0x1fef00, /* GPIO 8-14, 16-20 */ + }, + [STMPE2403] = { + .auto_increment = true, + .num_data = 5, + .num_normal_data = 3, + .max_cols = 8, + .max_rows = 12, + .col_gpios = 0x0000ff, /* GPIO 0 - 7*/ + .row_gpios = 0x1fef00, /* GPIO 8-14, 16-20 */ + }, +}; + +struct stmpe_keypad { + struct stmpe *stmpe; + struct input_dev *input; + const struct stmpe_keypad_variant *variant; + const struct stmpe_keypad_platform_data *plat; + + unsigned int rows; + unsigned int cols; + + unsigned short keymap[STMPE_KEYPAD_KEYMAP_SIZE]; +}; + +static int stmpe_keypad_read_data(struct stmpe_keypad *keypad, u8 *data) +{ + const struct stmpe_keypad_variant *variant = keypad->variant; + struct stmpe *stmpe = keypad->stmpe; + int ret; + int i; + + if (variant->auto_increment) + return stmpe_block_read(stmpe, STMPE_KPC_DATA_BYTE0, + variant->num_data, data); + + for (i = 0; i < variant->num_data; i++) { + ret = stmpe_reg_read(stmpe, STMPE_KPC_DATA_BYTE0 + i); + if (ret < 0) + return ret; + + data[i] = ret; + } + + return 0; +} + +static irqreturn_t stmpe_keypad_irq(int irq, void *dev) +{ + struct stmpe_keypad *keypad = dev; + struct input_dev *input = keypad->input; + const struct stmpe_keypad_variant *variant = keypad->variant; + u8 fifo[variant->num_data]; + int ret; + int i; + + ret = stmpe_keypad_read_data(keypad, fifo); + if (ret < 0) + return IRQ_NONE; + + for (i = 0; i < variant->num_normal_data; i++) { + u8 data = fifo[i]; + int row = (data & STMPE_KPC_DATA_ROW) >> 3; + int col = data & STMPE_KPC_DATA_COL; + int code = MATRIX_SCAN_CODE(row, col, STMPE_KEYPAD_ROW_SHIFT); + bool up = data & STMPE_KPC_DATA_UP; + + if ((data & STMPE_KPC_DATA_NOKEY_MASK) + == STMPE_KPC_DATA_NOKEY_MASK) + continue; + + input_event(input, EV_MSC, MSC_SCAN, code); + input_report_key(input, keypad->keymap[code], !up); + input_sync(input); + } + + return IRQ_HANDLED; +} + +static int __devinit stmpe_keypad_altfunc_init(struct stmpe_keypad *keypad) +{ + const struct stmpe_keypad_variant *variant = keypad->variant; + unsigned int col_gpios = variant->col_gpios; + unsigned int row_gpios = variant->row_gpios; + struct stmpe *stmpe = keypad->stmpe; + unsigned int pins = 0; + int i; + + /* + * Figure out which pins need to be set to the keypad alternate + * function. + * + * {cols,rows}_gpios are bitmasks of which pins on the chip can be used + * for the keypad. + * + * keypad->{cols,rows} are a bitmask of which pins (of the ones useable + * for the keypad) are used on the board. + */ + + for (i = 0; i < variant->max_cols; i++) { + int num = __ffs(col_gpios); + + if (keypad->cols & (1 << i)) + pins |= 1 << num; + + col_gpios &= ~(1 << num); + } + + for (i = 0; i < variant->max_rows; i++) { + int num = __ffs(row_gpios); + + if (keypad->rows & (1 << i)) + pins |= 1 << num; + + row_gpios &= ~(1 << num); + } + + return stmpe_set_altfunc(stmpe, pins, STMPE_BLOCK_KEYPAD); +} + +static int __devinit stmpe_keypad_chip_init(struct stmpe_keypad *keypad) +{ + const struct stmpe_keypad_platform_data *plat = keypad->plat; + const struct stmpe_keypad_variant *variant = keypad->variant; + struct stmpe *stmpe = keypad->stmpe; + int ret; + + if (plat->debounce_ms > STMPE_KEYPAD_MAX_DEBOUNCE) + return -EINVAL; + + if (plat->scan_count > STMPE_KEYPAD_MAX_SCAN_COUNT) + return -EINVAL; + + ret = stmpe_enable(stmpe, STMPE_BLOCK_KEYPAD); + if (ret < 0) + return ret; + + ret = stmpe_keypad_altfunc_init(keypad); + if (ret < 0) + return ret; + + ret = stmpe_reg_write(stmpe, STMPE_KPC_COL, keypad->cols); + if (ret < 0) + return ret; + + ret = stmpe_reg_write(stmpe, STMPE_KPC_ROW_LSB, keypad->rows); + if (ret < 0) + return ret; + + if (variant->max_rows > 8) { + ret = stmpe_set_bits(stmpe, STMPE_KPC_ROW_MSB, + STMPE_KPC_ROW_MSB_ROWS, + keypad->rows >> 8); + if (ret < 0) + return ret; + } + + ret = stmpe_set_bits(stmpe, STMPE_KPC_CTRL_MSB, + STMPE_KPC_CTRL_MSB_SCAN_COUNT, + plat->scan_count << 4); + if (ret < 0) + return ret; + + return stmpe_set_bits(stmpe, STMPE_KPC_CTRL_LSB, + STMPE_KPC_CTRL_LSB_SCAN | + STMPE_KPC_CTRL_LSB_DEBOUNCE, + STMPE_KPC_CTRL_LSB_SCAN | + (plat->debounce_ms << 1)); +} + +static int __devinit stmpe_keypad_probe(struct platform_device *pdev) +{ + struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent); + struct stmpe_keypad_platform_data *plat; + struct stmpe_keypad *keypad; + struct input_dev *input; + int ret; + int irq; + int i; + + plat = stmpe->pdata->keypad; + if (!plat) + return -ENODEV; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + keypad = kzalloc(sizeof(struct stmpe_keypad), GFP_KERNEL); + if (!keypad) + return -ENOMEM; + + input = input_allocate_device(); + if (!input) { + ret = -ENOMEM; + goto out_freekeypad; + } + + input->name = "STMPE keypad"; + input->id.bustype = BUS_I2C; + input->dev.parent = &pdev->dev; + + input_set_capability(input, EV_MSC, MSC_SCAN); + + __set_bit(EV_KEY, input->evbit); + if (!plat->no_autorepeat) + __set_bit(EV_REP, input->evbit); + + input->keycode = keypad->keymap; + input->keycodesize = sizeof(keypad->keymap[0]); + input->keycodemax = ARRAY_SIZE(keypad->keymap); + + matrix_keypad_build_keymap(plat->keymap_data, STMPE_KEYPAD_ROW_SHIFT, + input->keycode, input->keybit); + + for (i = 0; i < plat->keymap_data->keymap_size; i++) { + unsigned int key = plat->keymap_data->keymap[i]; + + keypad->cols |= 1 << KEY_COL(key); + keypad->rows |= 1 << KEY_ROW(key); + } + + keypad->stmpe = stmpe; + keypad->plat = plat; + keypad->input = input; + keypad->variant = &stmpe_keypad_variants[stmpe->partnum]; + + ret = stmpe_keypad_chip_init(keypad); + if (ret < 0) + goto out_freeinput; + + ret = input_register_device(input); + if (ret) { + dev_err(&pdev->dev, + "unable to register input device: %d\n", ret); + goto out_freeinput; + } + + ret = request_threaded_irq(irq, NULL, stmpe_keypad_irq, IRQF_ONESHOT, + "stmpe-keypad", keypad); + if (ret) { + dev_err(&pdev->dev, "unable to get irq: %d\n", ret); + goto out_unregisterinput; + } + + platform_set_drvdata(pdev, keypad); + + return 0; + +out_unregisterinput: + input_unregister_device(input); + input = NULL; +out_freeinput: + input_free_device(input); +out_freekeypad: + kfree(keypad); + return ret; +} + +static int __devexit stmpe_keypad_remove(struct platform_device *pdev) +{ + struct stmpe_keypad *keypad = platform_get_drvdata(pdev); + struct stmpe *stmpe = keypad->stmpe; + int irq = platform_get_irq(pdev, 0); + + stmpe_disable(stmpe, STMPE_BLOCK_KEYPAD); + + free_irq(irq, keypad); + input_unregister_device(keypad->input); + platform_set_drvdata(pdev, NULL); + kfree(keypad); + + return 0; +} + +static struct platform_driver stmpe_keypad_driver = { + .driver.name = "stmpe-keypad", + .driver.owner = THIS_MODULE, + .probe = stmpe_keypad_probe, + .remove = __devexit_p(stmpe_keypad_remove), +}; + +static int __init stmpe_keypad_init(void) +{ + return platform_driver_register(&stmpe_keypad_driver); +} +module_init(stmpe_keypad_init); + +static void __exit stmpe_keypad_exit(void) +{ + platform_driver_unregister(&stmpe_keypad_driver); +} +module_exit(stmpe_keypad_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("STMPExxxx keypad driver"); +MODULE_AUTHOR("Rabin Vincent <rabin.vincent@stericsson.com>"); diff --git a/drivers/input/misc/sparcspkr.c b/drivers/input/misc/sparcspkr.c index f3bb92e9755..8e130bf7d32 100644 --- a/drivers/input/misc/sparcspkr.c +++ b/drivers/input/misc/sparcspkr.c @@ -173,7 +173,7 @@ static int __devinit sparcspkr_probe(struct device *dev) return 0; } -static int sparcspkr_shutdown(struct of_device *dev) +static int sparcspkr_shutdown(struct platform_device *dev) { struct sparcspkr_state *state = dev_get_drvdata(&dev->dev); struct input_dev *input_dev = state->input_dev; @@ -184,7 +184,7 @@ static int sparcspkr_shutdown(struct of_device *dev) return 0; } -static int __devinit bbc_beep_probe(struct of_device *op, const struct of_device_id *match) +static int __devinit bbc_beep_probe(struct platform_device *op, const struct of_device_id *match) { struct sparcspkr_state *state; struct bbc_beep_info *info; @@ -231,7 +231,7 @@ out_err: return err; } -static int __devexit bbc_remove(struct of_device *op) +static int __devexit bbc_remove(struct platform_device *op) { struct sparcspkr_state *state = dev_get_drvdata(&op->dev); struct input_dev *input_dev = state->input_dev; @@ -269,7 +269,7 @@ static struct of_platform_driver bbc_beep_driver = { .shutdown = sparcspkr_shutdown, }; -static int __devinit grover_beep_probe(struct of_device *op, const struct of_device_id *match) +static int __devinit grover_beep_probe(struct platform_device *op, const struct of_device_id *match) { struct sparcspkr_state *state; struct grover_beep_info *info; @@ -312,7 +312,7 @@ out_err: return err; } -static int __devexit grover_remove(struct of_device *op) +static int __devexit grover_remove(struct platform_device *op) { struct sparcspkr_state *state = dev_get_drvdata(&op->dev); struct grover_beep_info *info = &state->u.grover; diff --git a/drivers/input/serio/i8042-sparcio.h b/drivers/input/serio/i8042-sparcio.h index cb2a24b9474..c5cc4508d6d 100644 --- a/drivers/input/serio/i8042-sparcio.h +++ b/drivers/input/serio/i8042-sparcio.h @@ -49,7 +49,7 @@ static inline void i8042_write_command(int val) #define OBP_PS2MS_NAME1 "kdmouse" #define OBP_PS2MS_NAME2 "mouse" -static int __devinit sparc_i8042_probe(struct of_device *op, const struct of_device_id *match) +static int __devinit sparc_i8042_probe(struct platform_device *op, const struct of_device_id *match) { struct device_node *dp = op->dev.of_node; @@ -57,7 +57,7 @@ static int __devinit sparc_i8042_probe(struct of_device *op, const struct of_dev while (dp) { if (!strcmp(dp->name, OBP_PS2KBD_NAME1) || !strcmp(dp->name, OBP_PS2KBD_NAME2)) { - struct of_device *kbd = of_find_device_by_node(dp); + struct platform_device *kbd = of_find_device_by_node(dp); unsigned int irq = kbd->archdata.irqs[0]; if (irq == 0xffffffff) irq = op->archdata.irqs[0]; @@ -67,7 +67,7 @@ static int __devinit sparc_i8042_probe(struct of_device *op, const struct of_dev kbd_res = &kbd->resource[0]; } else if (!strcmp(dp->name, OBP_PS2MS_NAME1) || !strcmp(dp->name, OBP_PS2MS_NAME2)) { - struct of_device *ms = of_find_device_by_node(dp); + struct platform_device *ms = of_find_device_by_node(dp); unsigned int irq = ms->archdata.irqs[0]; if (irq == 0xffffffff) irq = op->archdata.irqs[0]; @@ -80,7 +80,7 @@ static int __devinit sparc_i8042_probe(struct of_device *op, const struct of_dev return 0; } -static int __devexit sparc_i8042_remove(struct of_device *op) +static int __devexit sparc_i8042_remove(struct platform_device *op) { of_iounmap(kbd_res, kbd_iobase, 8); diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index 258b98b9d7c..46e4ba0b924 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -61,10 +61,6 @@ static bool i8042_noloop; module_param_named(noloop, i8042_noloop, bool, 0); MODULE_PARM_DESC(noloop, "Disable the AUX Loopback command while probing for the AUX port"); -static unsigned int i8042_blink_frequency = 500; -module_param_named(panicblink, i8042_blink_frequency, uint, 0600); -MODULE_PARM_DESC(panicblink, "Frequency with which keyboard LEDs should blink when kernel panics"); - #ifdef CONFIG_X86 static bool i8042_dritek; module_param_named(dritek, i8042_dritek, bool, 0); @@ -1030,8 +1026,8 @@ static void i8042_controller_reset(void) /* - * i8042_panic_blink() will flash the keyboard LEDs and is called when - * kernel panics. Flashing LEDs is useful for users running X who may + * i8042_panic_blink() will turn the keyboard LEDs on or off and is called + * when kernel panics. Flashing LEDs is useful for users running X who may * not see the console and will help distingushing panics from "real" * lockups. * @@ -1041,22 +1037,12 @@ static void i8042_controller_reset(void) #define DELAY do { mdelay(1); if (++delay > 10) return delay; } while(0) -static long i8042_panic_blink(long count) +static long i8042_panic_blink(int state) { long delay = 0; - static long last_blink; - static char led; - - /* - * We expect frequency to be about 1/2s. KDB uses about 1s. - * Make sure they are different. - */ - if (!i8042_blink_frequency) - return 0; - if (count - last_blink < i8042_blink_frequency) - return 0; + char led; - led ^= 0x01 | 0x04; + led = (state) ? 0x01 | 0x04 : 0; while (i8042_read_status() & I8042_STR_IBF) DELAY; dbg("%02x -> i8042 (panic blink)", 0xed); @@ -1069,7 +1055,6 @@ static long i8042_panic_blink(long count) dbg("%02x -> i8042 (panic blink)", led); i8042_write_data(led); DELAY; - last_blink = count; return delay; } diff --git a/drivers/input/serio/xilinx_ps2.c b/drivers/input/serio/xilinx_ps2.c index e2c028d2638..bb14449fb02 100644 --- a/drivers/input/serio/xilinx_ps2.c +++ b/drivers/input/serio/xilinx_ps2.c @@ -232,7 +232,7 @@ static void sxps2_close(struct serio *pserio) * It returns 0, if the driver is bound to the PS/2 device, or a negative * value if there is an error. */ -static int __devinit xps2_of_probe(struct of_device *ofdev, +static int __devinit xps2_of_probe(struct platform_device *ofdev, const struct of_device_id *match) { struct resource r_irq; /* Interrupt resources */ @@ -332,7 +332,7 @@ failed1: * if the driver module is being unloaded. It frees any resources allocated to * the device. */ -static int __devexit xps2_of_remove(struct of_device *of_dev) +static int __devexit xps2_of_remove(struct platform_device *of_dev) { struct device *dev = &of_dev->dev; struct xps2data *drvdata = dev_get_drvdata(dev); diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 61f35184f76..0069d9703fd 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -628,4 +628,14 @@ config TOUCHSCREEN_TPS6507X To compile this driver as a module, choose M here: the module will be called tps6507x_ts. +config TOUCHSCREEN_STMPE + tristate "STMicroelectronics STMPE touchscreens" + depends on MFD_STMPE + help + Say Y here if you want support for STMicroelectronics + STMPE touchscreen controllers. + + To compile this driver as a module, choose M here: the + module will be called stmpe-ts. + endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index bd6f30b4ff7..28217e1dcaf 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -36,6 +36,7 @@ obj-$(CONFIG_TOUCHSCREEN_PCAP) += pcap_ts.o obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o obj-$(CONFIG_TOUCHSCREEN_QT602240) += qt602240_ts.o obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o +obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o diff --git a/drivers/input/touchscreen/stmpe-ts.c b/drivers/input/touchscreen/stmpe-ts.c new file mode 100644 index 00000000000..656148ec002 --- /dev/null +++ b/drivers/input/touchscreen/stmpe-ts.c @@ -0,0 +1,397 @@ +/* STMicroelectronics STMPE811 Touchscreen Driver + * + * (C) 2010 Luotao Fu <l.fu@pengutronix.de> + * 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; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/interrupt.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/input.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/workqueue.h> + +#include <linux/mfd/stmpe.h> + +/* Register layouts and functionalities are identical on all stmpexxx variants + * with touchscreen controller + */ +#define STMPE_REG_INT_STA 0x0B +#define STMPE_REG_ADC_CTRL1 0x20 +#define STMPE_REG_ADC_CTRL2 0x21 +#define STMPE_REG_TSC_CTRL 0x40 +#define STMPE_REG_TSC_CFG 0x41 +#define STMPE_REG_FIFO_TH 0x4A +#define STMPE_REG_FIFO_STA 0x4B +#define STMPE_REG_FIFO_SIZE 0x4C +#define STMPE_REG_TSC_DATA_XYZ 0x52 +#define STMPE_REG_TSC_FRACTION_Z 0x56 +#define STMPE_REG_TSC_I_DRIVE 0x58 + +#define OP_MOD_XYZ 0 + +#define STMPE_TSC_CTRL_TSC_EN (1<<0) + +#define STMPE_FIFO_STA_RESET (1<<0) + +#define STMPE_IRQ_TOUCH_DET 0 + +#define SAMPLE_TIME(x) ((x & 0xf) << 4) +#define MOD_12B(x) ((x & 0x1) << 3) +#define REF_SEL(x) ((x & 0x1) << 1) +#define ADC_FREQ(x) (x & 0x3) +#define AVE_CTRL(x) ((x & 0x3) << 6) +#define DET_DELAY(x) ((x & 0x7) << 3) +#define SETTLING(x) (x & 0x7) +#define FRACTION_Z(x) (x & 0x7) +#define I_DRIVE(x) (x & 0x1) +#define OP_MODE(x) ((x & 0x7) << 1) + +#define STMPE_TS_NAME "stmpe-ts" +#define XY_MASK 0xfff + +struct stmpe_touch { + struct stmpe *stmpe; + struct input_dev *idev; + struct delayed_work work; + struct device *dev; + u8 sample_time; + u8 mod_12b; + u8 ref_sel; + u8 adc_freq; + u8 ave_ctrl; + u8 touch_det_delay; + u8 settling; + u8 fraction_z; + u8 i_drive; +}; + +static int __stmpe_reset_fifo(struct stmpe *stmpe) +{ + int ret; + + ret = stmpe_set_bits(stmpe, STMPE_REG_FIFO_STA, + STMPE_FIFO_STA_RESET, STMPE_FIFO_STA_RESET); + if (ret) + return ret; + + return stmpe_set_bits(stmpe, STMPE_REG_FIFO_STA, + STMPE_FIFO_STA_RESET, 0); +} + +static void stmpe_work(struct work_struct *work) +{ + int int_sta; + u32 timeout = 40; + + struct stmpe_touch *ts = + container_of(work, struct stmpe_touch, work.work); + + int_sta = stmpe_reg_read(ts->stmpe, STMPE_REG_INT_STA); + + /* + * touch_det sometimes get desasserted or just get stuck. This appears + * to be a silicon bug, We still have to clearify this with the + * manufacture. As a workaround We release the key anyway if the + * touch_det keeps coming in after 4ms, while the FIFO contains no value + * during the whole time. + */ + while ((int_sta & (1 << STMPE_IRQ_TOUCH_DET)) && (timeout > 0)) { + timeout--; + int_sta = stmpe_reg_read(ts->stmpe, STMPE_REG_INT_STA); + udelay(100); + } + + /* reset the FIFO before we report release event */ + __stmpe_reset_fifo(ts->stmpe); + + input_report_abs(ts->idev, ABS_PRESSURE, 0); + input_sync(ts->idev); +} + +static irqreturn_t stmpe_ts_handler(int irq, void *data) +{ + u8 data_set[4]; + int x, y, z; + struct stmpe_touch *ts = data; + + /* + * Cancel scheduled polling for release if we have new value + * available. Wait if the polling is already running. + */ + cancel_delayed_work_sync(&ts->work); + + /* + * The FIFO sometimes just crashes and stops generating interrupts. This + * appears to be a silicon bug. We still have to clearify this with + * the manufacture. As a workaround we disable the TSC while we are + * collecting data and flush the FIFO after reading + */ + stmpe_set_bits(ts->stmpe, STMPE_REG_TSC_CTRL, + STMPE_TSC_CTRL_TSC_EN, 0); + + stmpe_block_read(ts->stmpe, STMPE_REG_TSC_DATA_XYZ, 4, data_set); + + x = (data_set[0] << 4) | (data_set[1] >> 4); + y = ((data_set[1] & 0xf) << 8) | data_set[2]; + z = data_set[3]; + + input_report_abs(ts->idev, ABS_X, x); + input_report_abs(ts->idev, ABS_Y, y); + input_report_abs(ts->idev, ABS_PRESSURE, z); + input_sync(ts->idev); + + /* flush the FIFO after we have read out our values. */ + __stmpe_reset_fifo(ts->stmpe); + + /* reenable the tsc */ + stmpe_set_bits(ts->stmpe, STMPE_REG_TSC_CTRL, + STMPE_TSC_CTRL_TSC_EN, STMPE_TSC_CTRL_TSC_EN); + + /* start polling for touch_det to detect release */ + schedule_delayed_work(&ts->work, HZ / 50); + + return IRQ_HANDLED; +} + +static int __devinit stmpe_init_hw(struct stmpe_touch *ts) +{ + int ret; + u8 adc_ctrl1, adc_ctrl1_mask, tsc_cfg, tsc_cfg_mask; + struct stmpe *stmpe = ts->stmpe; + struct device *dev = ts->dev; + + ret = stmpe_enable(stmpe, STMPE_BLOCK_TOUCHSCREEN | STMPE_BLOCK_ADC); + if (ret) { + dev_err(dev, "Could not enable clock for ADC and TS\n"); + return ret; + } + + adc_ctrl1 = SAMPLE_TIME(ts->sample_time) | MOD_12B(ts->mod_12b) | + REF_SEL(ts->ref_sel); + adc_ctrl1_mask = SAMPLE_TIME(0xff) | MOD_12B(0xff) | REF_SEL(0xff); + + ret = stmpe_set_bits(stmpe, STMPE_REG_ADC_CTRL1, + adc_ctrl1_mask, adc_ctrl1); + if (ret) { + dev_err(dev, "Could not setup ADC\n"); + return ret; + } + + ret = stmpe_set_bits(stmpe, STMPE_REG_ADC_CTRL2, + ADC_FREQ(0xff), ADC_FREQ(ts->adc_freq)); + if (ret) { + dev_err(dev, "Could not setup ADC\n"); + return ret; + } + + tsc_cfg = AVE_CTRL(ts->ave_ctrl) | DET_DELAY(ts->touch_det_delay) | + SETTLING(ts->settling); + tsc_cfg_mask = AVE_CTRL(0xff) | DET_DELAY(0xff) | SETTLING(0xff); + + ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_CFG, tsc_cfg_mask, tsc_cfg); + if (ret) { + dev_err(dev, "Could not config touch\n"); + return ret; + } + + ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_FRACTION_Z, + FRACTION_Z(0xff), FRACTION_Z(ts->fraction_z)); + if (ret) { + dev_err(dev, "Could not config touch\n"); + return ret; + } + + ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_I_DRIVE, + I_DRIVE(0xff), I_DRIVE(ts->i_drive)); + if (ret) { + dev_err(dev, "Could not config touch\n"); + return ret; + } + + /* set FIFO to 1 for single point reading */ + ret = stmpe_reg_write(stmpe, STMPE_REG_FIFO_TH, 1); + if (ret) { + dev_err(dev, "Could not set FIFO\n"); + return ret; + } + + ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_CTRL, + OP_MODE(0xff), OP_MODE(OP_MOD_XYZ)); + if (ret) { + dev_err(dev, "Could not set mode\n"); + return ret; + } + + return 0; +} + +static int stmpe_ts_open(struct input_dev *dev) +{ + struct stmpe_touch *ts = input_get_drvdata(dev); + int ret = 0; + + ret = __stmpe_reset_fifo(ts->stmpe); + if (ret) + return ret; + + return stmpe_set_bits(ts->stmpe, STMPE_REG_TSC_CTRL, + STMPE_TSC_CTRL_TSC_EN, STMPE_TSC_CTRL_TSC_EN); +} + +static void stmpe_ts_close(struct input_dev *dev) +{ + struct stmpe_touch *ts = input_get_drvdata(dev); + + cancel_delayed_work_sync(&ts->work); + + stmpe_set_bits(ts->stmpe, STMPE_REG_TSC_CTRL, + STMPE_TSC_CTRL_TSC_EN, 0); +} + +static int __devinit stmpe_input_probe(struct platform_device *pdev) +{ + struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent); + struct stmpe_platform_data *pdata = stmpe->pdata; + struct stmpe_touch *ts; + struct input_dev *idev; + struct stmpe_ts_platform_data *ts_pdata = NULL; + int ret = 0; + int ts_irq; + + ts_irq = platform_get_irq_byname(pdev, "FIFO_TH"); + if (ts_irq < 0) + return ts_irq; + + ts = kzalloc(sizeof(*ts), GFP_KERNEL); + if (!ts) + goto err_out; + + idev = input_allocate_device(); + if (!idev) + goto err_free_ts; + + platform_set_drvdata(pdev, ts); + ts->stmpe = stmpe; + ts->idev = idev; + ts->dev = &pdev->dev; + + if (pdata) + ts_pdata = pdata->ts; + + if (ts_pdata) { + ts->sample_time = ts_pdata->sample_time; + ts->mod_12b = ts_pdata->mod_12b; + ts->ref_sel = ts_pdata->ref_sel; + ts->adc_freq = ts_pdata->adc_freq; + ts->ave_ctrl = ts_pdata->ave_ctrl; + ts->touch_det_delay = ts_pdata->touch_det_delay; + ts->settling = ts_pdata->settling; + ts->fraction_z = ts_pdata->fraction_z; + ts->i_drive = ts_pdata->i_drive; + } + + INIT_DELAYED_WORK(&ts->work, stmpe_work); + + ret = request_threaded_irq(ts_irq, NULL, stmpe_ts_handler, + IRQF_ONESHOT, STMPE_TS_NAME, ts); + if (ret) { + dev_err(&pdev->dev, "Failed to request IRQ %d\n", ts_irq); + goto err_free_input; + } + + ret = stmpe_init_hw(ts); + if (ret) + goto err_free_irq; + + idev->name = STMPE_TS_NAME; + idev->id.bustype = BUS_I2C; + idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + idev->open = stmpe_ts_open; + idev->close = stmpe_ts_close; + + input_set_drvdata(idev, ts); + + input_set_abs_params(idev, ABS_X, 0, XY_MASK, 0, 0); + input_set_abs_params(idev, ABS_Y, 0, XY_MASK, 0, 0); + input_set_abs_params(idev, ABS_PRESSURE, 0x0, 0xff, 0, 0); + + ret = input_register_device(idev); + if (ret) { + dev_err(&pdev->dev, "Could not register input device\n"); + goto err_free_irq; + } + + return ret; + +err_free_irq: + free_irq(ts_irq, ts); +err_free_input: + input_free_device(idev); + platform_set_drvdata(pdev, NULL); +err_free_ts: + kfree(ts); +err_out: + return ret; +} + +static int __devexit stmpe_ts_remove(struct platform_device *pdev) +{ + struct stmpe_touch *ts = platform_get_drvdata(pdev); + unsigned int ts_irq = platform_get_irq_byname(pdev, "FIFO_TH"); + + stmpe_disable(ts->stmpe, STMPE_BLOCK_TOUCHSCREEN); + + free_irq(ts_irq, ts); + + platform_set_drvdata(pdev, NULL); + + input_unregister_device(ts->idev); + input_free_device(ts->idev); + + kfree(ts); + + return 0; +} + +static struct platform_driver stmpe_ts_driver = { + .driver = { + .name = STMPE_TS_NAME, + .owner = THIS_MODULE, + }, + .probe = stmpe_input_probe, + .remove = __devexit_p(stmpe_ts_remove), +}; + +static int __init stmpe_ts_init(void) +{ + return platform_driver_register(&stmpe_ts_driver); +} + +module_init(stmpe_ts_init); + +static void __exit stmpe_ts_exit(void) +{ + platform_driver_unregister(&stmpe_ts_driver); +} + +module_exit(stmpe_ts_exit); + +MODULE_AUTHOR("Luotao Fu <l.fu@pengutronix.de>"); +MODULE_DESCRIPTION("STMPEXXX touchscreen driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" STMPE_TS_NAME); diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index cc22eeefa10..ea57e05d08f 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -224,7 +224,7 @@ struct gpio_led_of_platform_data { struct gpio_led_data led_data[]; }; -static int __devinit of_gpio_leds_probe(struct of_device *ofdev, +static int __devinit of_gpio_leds_probe(struct platform_device *ofdev, const struct of_device_id *match) { struct device_node *np = ofdev->dev.of_node, *child; @@ -283,7 +283,7 @@ err: return ret; } -static int __devexit of_gpio_leds_remove(struct of_device *ofdev) +static int __devexit of_gpio_leds_remove(struct platform_device *ofdev) { struct gpio_led_of_platform_data *pdata = dev_get_drvdata(&ofdev->dev); int i; diff --git a/drivers/macintosh/macio_sysfs.c b/drivers/macintosh/macio_sysfs.c index 6024038a5b9..8eb40afbd0f 100644 --- a/drivers/macintosh/macio_sysfs.c +++ b/drivers/macintosh/macio_sysfs.c @@ -15,7 +15,7 @@ field##_show (struct device *dev, struct device_attribute *attr, \ static ssize_t compatible_show (struct device *dev, struct device_attribute *attr, char *buf) { - struct of_device *of; + struct platform_device *of; const char *compat; int cplen; int length = 0; @@ -52,9 +52,9 @@ static ssize_t modalias_show (struct device *dev, struct device_attribute *attr, static ssize_t devspec_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct of_device *ofdev; + struct platform_device *ofdev; - ofdev = to_of_device(dev); + ofdev = to_platform_device(dev); return sprintf(buf, "%s\n", ofdev->dev.of_node->full_name); } diff --git a/drivers/macintosh/smu.c b/drivers/macintosh/smu.c index 2506c957712..e58c3d33e03 100644 --- a/drivers/macintosh/smu.c +++ b/drivers/macintosh/smu.c @@ -75,7 +75,7 @@ struct smu_cmd_buf { struct smu_device { spinlock_t lock; struct device_node *of_node; - struct of_device *of_dev; + struct platform_device *of_dev; int doorbell; /* doorbell gpio */ u32 __iomem *db_buf; /* doorbell buffer */ struct device_node *db_node; @@ -645,7 +645,7 @@ static void smu_expose_childs(struct work_struct *unused) static DECLARE_WORK(smu_expose_childs_work, smu_expose_childs); -static int smu_platform_probe(struct of_device* dev, +static int smu_platform_probe(struct platform_device* dev, const struct of_device_id *match) { if (!smu) @@ -695,7 +695,7 @@ static int __init smu_init_sysfs(void) device_initcall(smu_init_sysfs); -struct of_device *smu_get_ofdev(void) +struct platform_device *smu_get_ofdev(void) { if (!smu) return NULL; diff --git a/drivers/macintosh/therm_adt746x.c b/drivers/macintosh/therm_adt746x.c index c42eeb43042..d0d221332db 100644 --- a/drivers/macintosh/therm_adt746x.c +++ b/drivers/macintosh/therm_adt746x.c @@ -84,7 +84,7 @@ struct thermostat { static enum {ADT7460, ADT7467} therm_type; static int therm_bus, therm_address; -static struct of_device * of_dev; +static struct platform_device * of_dev; static struct thermostat* thermostat; static struct task_struct *thread_therm = NULL; diff --git a/drivers/macintosh/therm_pm72.c b/drivers/macintosh/therm_pm72.c index e60605bd0ea..44549272333 100644 --- a/drivers/macintosh/therm_pm72.c +++ b/drivers/macintosh/therm_pm72.c @@ -148,7 +148,7 @@ * Driver statics */ -static struct of_device * of_dev; +static struct platform_device * of_dev; static struct i2c_adapter * u3_0; static struct i2c_adapter * u3_1; static struct i2c_adapter * k2; @@ -2210,7 +2210,7 @@ static void fcu_lookup_fans(struct device_node *fcu_node) } } -static int fcu_of_probe(struct of_device* dev, const struct of_device_id *match) +static int fcu_of_probe(struct platform_device* dev, const struct of_device_id *match) { state = state_detached; @@ -2221,7 +2221,7 @@ static int fcu_of_probe(struct of_device* dev, const struct of_device_id *match) return i2c_add_driver(&therm_pm72_driver); } -static int fcu_of_remove(struct of_device* dev) +static int fcu_of_remove(struct platform_device* dev) { i2c_del_driver(&therm_pm72_driver); diff --git a/drivers/macintosh/therm_windtunnel.c b/drivers/macintosh/therm_windtunnel.c index 5c9367acf0c..133f195de1f 100644 --- a/drivers/macintosh/therm_windtunnel.c +++ b/drivers/macintosh/therm_windtunnel.c @@ -52,7 +52,7 @@ static struct { struct task_struct *poll_task; struct mutex lock; - struct of_device *of_dev; + struct platform_device *of_dev; struct i2c_client *thermostat; struct i2c_client *fan; @@ -444,13 +444,13 @@ static struct i2c_driver g4fan_driver = { /************************************************************************/ static int -therm_of_probe( struct of_device *dev, const struct of_device_id *match ) +therm_of_probe( struct platform_device *dev, const struct of_device_id *match ) { return i2c_add_driver( &g4fan_driver ); } static int -therm_of_remove( struct of_device *dev ) +therm_of_remove( struct platform_device *dev ) { i2c_del_driver( &g4fan_driver ); return 0; diff --git a/drivers/media/video/fsl-viu.c b/drivers/media/video/fsl-viu.c index 8f1c94f7e00..43d208f1f58 100644 --- a/drivers/media/video/fsl-viu.c +++ b/drivers/media/video/fsl-viu.c @@ -1418,7 +1418,7 @@ static struct video_device viu_template = { .current_norm = V4L2_STD_NTSC_M, }; -static int __devinit viu_of_probe(struct of_device *op, +static int __devinit viu_of_probe(struct platform_device *op, const struct of_device_id *match) { struct viu_dev *viu_dev; @@ -1549,7 +1549,7 @@ err: return ret; } -static int __devexit viu_of_remove(struct of_device *op) +static int __devexit viu_of_remove(struct platform_device *op) { struct v4l2_device *v4l2_dev = dev_get_drvdata(&op->dev); struct viu_dev *dev = container_of(v4l2_dev, struct viu_dev, v4l2_dev); @@ -1570,7 +1570,7 @@ static int __devexit viu_of_remove(struct of_device *op) } #ifdef CONFIG_PM -static int viu_suspend(struct of_device *op, pm_message_t state) +static int viu_suspend(struct platform_device *op, pm_message_t state) { struct v4l2_device *v4l2_dev = dev_get_drvdata(&op->dev); struct viu_dev *dev = container_of(v4l2_dev, struct viu_dev, v4l2_dev); @@ -1579,7 +1579,7 @@ static int viu_suspend(struct of_device *op, pm_message_t state) return 0; } -static int viu_resume(struct of_device *op) +static int viu_resume(struct platform_device *op) { struct v4l2_device *v4l2_dev = dev_get_drvdata(&op->dev); struct viu_dev *dev = container_of(v4l2_dev, struct viu_dev, v4l2_dev); diff --git a/drivers/memstick/core/mspro_block.c b/drivers/memstick/core/mspro_block.c index eef78a068fd..d3f1a087ece 100644 --- a/drivers/memstick/core/mspro_block.c +++ b/drivers/memstick/core/mspro_block.c @@ -1048,6 +1048,7 @@ static int mspro_block_read_attributes(struct memstick_dev *card) snprintf(s_attr->name, sizeof(s_attr->name), "attr_x%02x", attr->entries[cnt].id); + sysfs_attr_init(&s_attr->dev_attr.attr); s_attr->dev_attr.attr.name = s_attr->name; s_attr->dev_attr.attr.mode = S_IRUGO; s_attr->dev_attr.show = mspro_block_attr_show(s_attr->id); @@ -1338,13 +1339,14 @@ static void mspro_block_remove(struct memstick_dev *card) struct mspro_block_data *msb = memstick_get_drvdata(card); unsigned long flags; - del_gendisk(msb->disk); - dev_dbg(&card->dev, "mspro block remove\n"); spin_lock_irqsave(&msb->q_lock, flags); msb->eject = 1; blk_start_queue(msb->queue); spin_unlock_irqrestore(&msb->q_lock, flags); + del_gendisk(msb->disk); + dev_dbg(&card->dev, "mspro block remove\n"); + blk_cleanup_queue(msb->queue); msb->queue = NULL; diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c index d8c668d6441..b8f1719d7c0 100644 --- a/drivers/message/fusion/mptbase.c +++ b/drivers/message/fusion/mptbase.c @@ -50,6 +50,7 @@ #include <linux/module.h> #include <linux/errno.h> #include <linux/init.h> +#include <linux/seq_file.h> #include <linux/slab.h> #include <linux/types.h> #include <linux/pci.h> @@ -199,12 +200,9 @@ static int mpt_host_page_access_control(MPT_ADAPTER *ioc, u8 access_control_valu static int mpt_host_page_alloc(MPT_ADAPTER *ioc, pIOCInit_t ioc_init); #ifdef CONFIG_PROC_FS -static int procmpt_summary_read(char *buf, char **start, off_t offset, - int request, int *eof, void *data); -static int procmpt_version_read(char *buf, char **start, off_t offset, - int request, int *eof, void *data); -static int procmpt_iocinfo_read(char *buf, char **start, off_t offset, - int request, int *eof, void *data); +static const struct file_operations mpt_summary_proc_fops; +static const struct file_operations mpt_version_proc_fops; +static const struct file_operations mpt_iocinfo_proc_fops; #endif static void mpt_get_fw_exp_ver(char *buf, MPT_ADAPTER *ioc); @@ -1724,7 +1722,7 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id) u8 pcixcmd; static int mpt_ids = 0; #ifdef CONFIG_PROC_FS - struct proc_dir_entry *dent, *ent; + struct proc_dir_entry *dent; #endif ioc = kzalloc(sizeof(MPT_ADAPTER), GFP_ATOMIC); @@ -1979,16 +1977,8 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id) */ dent = proc_mkdir(ioc->name, mpt_proc_root_dir); if (dent) { - ent = create_proc_entry("info", S_IFREG|S_IRUGO, dent); - if (ent) { - ent->read_proc = procmpt_iocinfo_read; - ent->data = ioc; - } - ent = create_proc_entry("summary", S_IFREG|S_IRUGO, dent); - if (ent) { - ent->read_proc = procmpt_summary_read; - ent->data = ioc; - } + proc_create_data("info", S_IRUGO, dent, &mpt_iocinfo_proc_fops, ioc); + proc_create_data("summary", S_IRUGO, dent, &mpt_summary_proc_fops, ioc); } #endif @@ -6545,20 +6535,12 @@ mpt_ioc_reset(MPT_ADAPTER *ioc, int reset_phase) static int procmpt_create(void) { - struct proc_dir_entry *ent; - mpt_proc_root_dir = proc_mkdir(MPT_PROCFS_MPTBASEDIR, NULL); if (mpt_proc_root_dir == NULL) return -ENOTDIR; - ent = create_proc_entry("summary", S_IFREG|S_IRUGO, mpt_proc_root_dir); - if (ent) - ent->read_proc = procmpt_summary_read; - - ent = create_proc_entry("version", S_IFREG|S_IRUGO, mpt_proc_root_dir); - if (ent) - ent->read_proc = procmpt_version_read; - + proc_create("summary", S_IRUGO, mpt_proc_root_dir, &mpt_summary_proc_fops); + proc_create("version", S_IRUGO, mpt_proc_root_dir, &mpt_version_proc_fops); return 0; } @@ -6578,70 +6560,46 @@ procmpt_destroy(void) /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /** - * procmpt_summary_read - Handle read request of a summary file - * @buf: Pointer to area to write information - * @start: Pointer to start pointer - * @offset: Offset to start writing - * @request: Amount of read data requested - * @eof: Pointer to EOF integer - * @data: Pointer - * * Handles read request from /proc/mpt/summary or /proc/mpt/iocN/summary. - * Returns number of characters written to process performing the read. */ -static int -procmpt_summary_read(char *buf, char **start, off_t offset, int request, int *eof, void *data) -{ - MPT_ADAPTER *ioc; - char *out = buf; - int len; +static void seq_mpt_print_ioc_summary(MPT_ADAPTER *ioc, struct seq_file *m, int showlan); - if (data) { - int more = 0; - - ioc = data; - mpt_print_ioc_summary(ioc, out, &more, 0, 1); +static int mpt_summary_proc_show(struct seq_file *m, void *v) +{ + MPT_ADAPTER *ioc = m->private; - out += more; + if (ioc) { + seq_mpt_print_ioc_summary(ioc, m, 1); } else { list_for_each_entry(ioc, &ioc_list, list) { - int more = 0; - - mpt_print_ioc_summary(ioc, out, &more, 0, 1); - - out += more; - if ((out-buf) >= request) - break; + seq_mpt_print_ioc_summary(ioc, m, 1); } } - len = out - buf; + return 0; +} - MPT_PROC_READ_RETURN(buf,start,offset,request,eof,len); +static int mpt_summary_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, mpt_summary_proc_show, PDE(inode)->data); } -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -/** - * procmpt_version_read - Handle read request from /proc/mpt/version. - * @buf: Pointer to area to write information - * @start: Pointer to start pointer - * @offset: Offset to start writing - * @request: Amount of read data requested - * @eof: Pointer to EOF integer - * @data: Pointer - * - * Returns number of characters written to process performing the read. - */ -static int -procmpt_version_read(char *buf, char **start, off_t offset, int request, int *eof, void *data) +static const struct file_operations mpt_summary_proc_fops = { + .owner = THIS_MODULE, + .open = mpt_summary_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int mpt_version_proc_show(struct seq_file *m, void *v) { u8 cb_idx; int scsi, fc, sas, lan, ctl, targ, dmp; char *drvname; - int len; - len = sprintf(buf, "%s-%s\n", "mptlinux", MPT_LINUX_VERSION_COMMON); - len += sprintf(buf+len, " Fusion MPT base driver\n"); + seq_printf(m, "%s-%s\n", "mptlinux", MPT_LINUX_VERSION_COMMON); + seq_printf(m, " Fusion MPT base driver\n"); scsi = fc = sas = lan = ctl = targ = dmp = 0; for (cb_idx = MPT_MAX_PROTOCOL_DRIVERS-1; cb_idx; cb_idx--) { @@ -6669,98 +6627,97 @@ procmpt_version_read(char *buf, char **start, off_t offset, int request, int *eo } if (drvname) - len += sprintf(buf+len, " Fusion MPT %s driver\n", drvname); + seq_printf(m, " Fusion MPT %s driver\n", drvname); } } - MPT_PROC_READ_RETURN(buf,start,offset,request,eof,len); + return 0; } -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -/** - * procmpt_iocinfo_read - Handle read request from /proc/mpt/iocN/info. - * @buf: Pointer to area to write information - * @start: Pointer to start pointer - * @offset: Offset to start writing - * @request: Amount of read data requested - * @eof: Pointer to EOF integer - * @data: Pointer - * - * Returns number of characters written to process performing the read. - */ -static int -procmpt_iocinfo_read(char *buf, char **start, off_t offset, int request, int *eof, void *data) +static int mpt_version_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, mpt_version_proc_show, NULL); +} + +static const struct file_operations mpt_version_proc_fops = { + .owner = THIS_MODULE, + .open = mpt_version_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int mpt_iocinfo_proc_show(struct seq_file *m, void *v) { - MPT_ADAPTER *ioc = data; - int len; + MPT_ADAPTER *ioc = m->private; char expVer[32]; int sz; int p; mpt_get_fw_exp_ver(expVer, ioc); - len = sprintf(buf, "%s:", ioc->name); + seq_printf(m, "%s:", ioc->name); if (ioc->facts.Flags & MPI_IOCFACTS_FLAGS_FW_DOWNLOAD_BOOT) - len += sprintf(buf+len, " (f/w download boot flag set)"); + seq_printf(m, " (f/w download boot flag set)"); // if (ioc->facts.IOCExceptions & MPI_IOCFACTS_EXCEPT_CONFIG_CHECKSUM_FAIL) -// len += sprintf(buf+len, " CONFIG_CHECKSUM_FAIL!"); +// seq_printf(m, " CONFIG_CHECKSUM_FAIL!"); - len += sprintf(buf+len, "\n ProductID = 0x%04x (%s)\n", + seq_printf(m, "\n ProductID = 0x%04x (%s)\n", ioc->facts.ProductID, ioc->prod_name); - len += sprintf(buf+len, " FWVersion = 0x%08x%s", ioc->facts.FWVersion.Word, expVer); + seq_printf(m, " FWVersion = 0x%08x%s", ioc->facts.FWVersion.Word, expVer); if (ioc->facts.FWImageSize) - len += sprintf(buf+len, " (fw_size=%d)", ioc->facts.FWImageSize); - len += sprintf(buf+len, "\n MsgVersion = 0x%04x\n", ioc->facts.MsgVersion); - len += sprintf(buf+len, " FirstWhoInit = 0x%02x\n", ioc->FirstWhoInit); - len += sprintf(buf+len, " EventState = 0x%02x\n", ioc->facts.EventState); + seq_printf(m, " (fw_size=%d)", ioc->facts.FWImageSize); + seq_printf(m, "\n MsgVersion = 0x%04x\n", ioc->facts.MsgVersion); + seq_printf(m, " FirstWhoInit = 0x%02x\n", ioc->FirstWhoInit); + seq_printf(m, " EventState = 0x%02x\n", ioc->facts.EventState); - len += sprintf(buf+len, " CurrentHostMfaHighAddr = 0x%08x\n", + seq_printf(m, " CurrentHostMfaHighAddr = 0x%08x\n", ioc->facts.CurrentHostMfaHighAddr); - len += sprintf(buf+len, " CurrentSenseBufferHighAddr = 0x%08x\n", + seq_printf(m, " CurrentSenseBufferHighAddr = 0x%08x\n", ioc->facts.CurrentSenseBufferHighAddr); - len += sprintf(buf+len, " MaxChainDepth = 0x%02x frames\n", ioc->facts.MaxChainDepth); - len += sprintf(buf+len, " MinBlockSize = 0x%02x bytes\n", 4*ioc->facts.BlockSize); + seq_printf(m, " MaxChainDepth = 0x%02x frames\n", ioc->facts.MaxChainDepth); + seq_printf(m, " MinBlockSize = 0x%02x bytes\n", 4*ioc->facts.BlockSize); - len += sprintf(buf+len, " RequestFrames @ 0x%p (Dma @ 0x%p)\n", + seq_printf(m, " RequestFrames @ 0x%p (Dma @ 0x%p)\n", (void *)ioc->req_frames, (void *)(ulong)ioc->req_frames_dma); /* * Rounding UP to nearest 4-kB boundary here... */ sz = (ioc->req_sz * ioc->req_depth) + 128; sz = ((sz + 0x1000UL - 1UL) / 0x1000) * 0x1000; - len += sprintf(buf+len, " {CurReqSz=%d} x {CurReqDepth=%d} = %d bytes ^= 0x%x\n", + seq_printf(m, " {CurReqSz=%d} x {CurReqDepth=%d} = %d bytes ^= 0x%x\n", ioc->req_sz, ioc->req_depth, ioc->req_sz*ioc->req_depth, sz); - len += sprintf(buf+len, " {MaxReqSz=%d} {MaxReqDepth=%d}\n", + seq_printf(m, " {MaxReqSz=%d} {MaxReqDepth=%d}\n", 4*ioc->facts.RequestFrameSize, ioc->facts.GlobalCredits); - len += sprintf(buf+len, " Frames @ 0x%p (Dma @ 0x%p)\n", + seq_printf(m, " Frames @ 0x%p (Dma @ 0x%p)\n", (void *)ioc->alloc, (void *)(ulong)ioc->alloc_dma); sz = (ioc->reply_sz * ioc->reply_depth) + 128; - len += sprintf(buf+len, " {CurRepSz=%d} x {CurRepDepth=%d} = %d bytes ^= 0x%x\n", + seq_printf(m, " {CurRepSz=%d} x {CurRepDepth=%d} = %d bytes ^= 0x%x\n", ioc->reply_sz, ioc->reply_depth, ioc->reply_sz*ioc->reply_depth, sz); - len += sprintf(buf+len, " {MaxRepSz=%d} {MaxRepDepth=%d}\n", + seq_printf(m, " {MaxRepSz=%d} {MaxRepDepth=%d}\n", ioc->facts.CurReplyFrameSize, ioc->facts.ReplyQueueDepth); - len += sprintf(buf+len, " MaxDevices = %d\n", + seq_printf(m, " MaxDevices = %d\n", (ioc->facts.MaxDevices==0) ? 255 : ioc->facts.MaxDevices); - len += sprintf(buf+len, " MaxBuses = %d\n", ioc->facts.MaxBuses); + seq_printf(m, " MaxBuses = %d\n", ioc->facts.MaxBuses); /* per-port info */ for (p=0; p < ioc->facts.NumberOfPorts; p++) { - len += sprintf(buf+len, " PortNumber = %d (of %d)\n", + seq_printf(m, " PortNumber = %d (of %d)\n", p+1, ioc->facts.NumberOfPorts); if (ioc->bus_type == FC) { if (ioc->pfacts[p].ProtocolFlags & MPI_PORTFACTS_PROTOCOL_LAN) { u8 *a = (u8*)&ioc->lan_cnfg_page1.HardwareAddressLow; - len += sprintf(buf+len, " LanAddr = %02X:%02X:%02X:%02X:%02X:%02X\n", + seq_printf(m, " LanAddr = %02X:%02X:%02X:%02X:%02X:%02X\n", a[5], a[4], a[3], a[2], a[1], a[0]); } - len += sprintf(buf+len, " WWN = %08X%08X:%08X%08X\n", + seq_printf(m, " WWN = %08X%08X:%08X%08X\n", ioc->fc_port_page0[p].WWNN.High, ioc->fc_port_page0[p].WWNN.Low, ioc->fc_port_page0[p].WWPN.High, @@ -6768,9 +6725,21 @@ procmpt_iocinfo_read(char *buf, char **start, off_t offset, int request, int *eo } } - MPT_PROC_READ_RETURN(buf,start,offset,request,eof,len); + return 0; } +static int mpt_iocinfo_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, mpt_iocinfo_proc_show, PDE(inode)->data); +} + +static const struct file_operations mpt_iocinfo_proc_fops = { + .owner = THIS_MODULE, + .open = mpt_iocinfo_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; #endif /* CONFIG_PROC_FS } */ /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ @@ -6836,6 +6805,39 @@ mpt_print_ioc_summary(MPT_ADAPTER *ioc, char *buffer, int *size, int len, int sh *size = y; } + +static void seq_mpt_print_ioc_summary(MPT_ADAPTER *ioc, struct seq_file *m, int showlan) +{ + char expVer[32]; + + mpt_get_fw_exp_ver(expVer, ioc); + + /* + * Shorter summary of attached ioc's... + */ + seq_printf(m, "%s: %s, %s%08xh%s, Ports=%d, MaxQ=%d", + ioc->name, + ioc->prod_name, + MPT_FW_REV_MAGIC_ID_STRING, /* "FwRev=" or somesuch */ + ioc->facts.FWVersion.Word, + expVer, + ioc->facts.NumberOfPorts, + ioc->req_depth); + + if (showlan && (ioc->pfacts[0].ProtocolFlags & MPI_PORTFACTS_PROTOCOL_LAN)) { + u8 *a = (u8*)&ioc->lan_cnfg_page1.HardwareAddressLow; + seq_printf(m, ", LanAddr=%02X:%02X:%02X:%02X:%02X:%02X", + a[5], a[4], a[3], a[2], a[1], a[0]); + } + + seq_printf(m, ", IRQ=%d", ioc->pci_irq); + + if (!ioc->active) + seq_printf(m, " (disabled)"); + + seq_putc(m, '\n'); +} + /** * mpt_set_taskmgmt_in_progress_flag - set flags associated with task management * @ioc: Pointer to MPT_ADAPTER structure @@ -6921,7 +6923,6 @@ EXPORT_SYMBOL(mpt_halt_firmware); * mpt_SoftResetHandler - Issues a less expensive reset * @ioc: Pointer to MPT_ADAPTER structure * @sleepFlag: Indicates if sleep or schedule must be called. - * * Returns 0 for SUCCESS or -1 if FAILED. * @@ -7066,7 +7067,6 @@ mpt_SoftResetHandler(MPT_ADAPTER *ioc, int sleepFlag) * mpt_Soft_Hard_ResetHandler - Try less expensive reset * @ioc: Pointer to MPT_ADAPTER structure * @sleepFlag: Indicates if sleep or schedule must be called. - * * Returns 0 for SUCCESS or -1 if FAILED. * Try for softreset first, only if it fails go for expensive diff --git a/drivers/message/fusion/mptbase.h b/drivers/message/fusion/mptbase.h index 23ed3dec72a..f71f2294847 100644 --- a/drivers/message/fusion/mptbase.h +++ b/drivers/message/fusion/mptbase.h @@ -420,31 +420,6 @@ typedef struct _VirtDevice { #define MPT_TARGET_FLAGS_LED_ON 0x80 /* - * /proc/mpt interface - */ -typedef struct { - const char *name; - mode_t mode; - int pad; - read_proc_t *read_proc; - write_proc_t *write_proc; -} mpt_proc_entry_t; - -#define MPT_PROC_READ_RETURN(buf,start,offset,request,eof,len) \ -do { \ - len -= offset; \ - if (len < request) { \ - *eof = 1; \ - if (len <= 0) \ - return 0; \ - } else \ - len = request; \ - *start = buf + offset; \ - return len; \ -} while (0) - - -/* * IOCTL structure and associated defines */ diff --git a/drivers/message/i2o/exec-osm.c b/drivers/message/i2o/exec-osm.c index 06c655c5558..a3970e56ae5 100644 --- a/drivers/message/i2o/exec-osm.c +++ b/drivers/message/i2o/exec-osm.c @@ -389,12 +389,16 @@ static int i2o_exec_lct_notify(struct i2o_controller *c, u32 change_ind) dev = &c->pdev->dev; if (i2o_dma_realloc(dev, &c->dlct, - le32_to_cpu(sb->expected_lct_size))) + le32_to_cpu(sb->expected_lct_size))) { + mutex_unlock(&c->lct_lock); return -ENOMEM; + } msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET); - if (IS_ERR(msg)) + if (IS_ERR(msg)) { + mutex_unlock(&c->lct_lock); return PTR_ERR(msg); + } msg->u.head[0] = cpu_to_le32(EIGHT_WORD_MSG_SIZE | SGL_OFFSET_6); msg->u.head[1] = cpu_to_le32(I2O_CMD_LCT_NOTIFY << 24 | HOST_TID << 12 | diff --git a/drivers/message/i2o/i2o_block.c b/drivers/message/i2o/i2o_block.c index e6733bc9972..f0f1e667000 100644 --- a/drivers/message/i2o/i2o_block.c +++ b/drivers/message/i2o/i2o_block.c @@ -727,7 +727,7 @@ static int i2o_block_transfer(struct request *req) { struct i2o_block_device *dev = req->rq_disk->private_data; struct i2o_controller *c; - u32 tid = dev->i2o_dev->lct_data.tid; + u32 tid; struct i2o_message *msg; u32 *mptr; struct i2o_block_request *ireq = req->special; @@ -743,6 +743,7 @@ static int i2o_block_transfer(struct request *req) goto exit; } + tid = dev->i2o_dev->lct_data.tid; c = dev->i2o_dev->iop; msg = i2o_msg_get(c); diff --git a/drivers/message/i2o/i2o_config.c b/drivers/message/i2o/i2o_config.c index c4b117f5fb7..068ba0785bb 100644 --- a/drivers/message/i2o/i2o_config.c +++ b/drivers/message/i2o/i2o_config.c @@ -111,11 +111,11 @@ static int i2o_cfg_gethrt(unsigned long arg) len = 8 + ((hrt->entry_len * hrt->num_entries) << 2); - /* We did a get user...so assuming mem is ok...is this bad? */ - put_user(len, kcmd.reslen); - if (len > reslen) + if (put_user(len, kcmd.reslen)) + ret = -EFAULT; + else if (len > reslen) ret = -ENOBUFS; - if (copy_to_user(kcmd.resbuf, (void *)hrt, len)) + else if (copy_to_user(kcmd.resbuf, (void *)hrt, len)) ret = -EFAULT; return ret; @@ -147,8 +147,9 @@ static int i2o_cfg_getlct(unsigned long arg) lct = (i2o_lct *) c->lct; len = (unsigned int)lct->table_size << 2; - put_user(len, kcmd.reslen); - if (len > reslen) + if (put_user(len, kcmd.reslen)) + ret = -EFAULT; + else if (len > reslen) ret = -ENOBUFS; else if (copy_to_user(kcmd.resbuf, lct, len)) ret = -EFAULT; @@ -208,8 +209,9 @@ static int i2o_cfg_parms(unsigned long arg, unsigned int type) return -EAGAIN; } - put_user(len, kcmd.reslen); - if (len > reslen) + if (put_user(len, kcmd.reslen)) + ret = -EFAULT; + else if (len > reslen) ret = -ENOBUFS; else if (copy_to_user(kcmd.resbuf, res, len)) ret = -EFAULT; diff --git a/drivers/message/i2o/i2o_scsi.c b/drivers/message/i2o/i2o_scsi.c index 3d45817e6dc..ea6b2197da8 100644 --- a/drivers/message/i2o/i2o_scsi.c +++ b/drivers/message/i2o/i2o_scsi.c @@ -528,7 +528,6 @@ static int i2o_scsi_queuecommand(struct scsi_cmnd *SCpnt, * Do the incoming paperwork */ i2o_dev = SCpnt->device->hostdata; - c = i2o_dev->iop; SCpnt->scsi_done = done; @@ -538,7 +537,7 @@ static int i2o_scsi_queuecommand(struct scsi_cmnd *SCpnt, done(SCpnt); goto exit; } - + c = i2o_dev->iop; tid = i2o_dev->lct_data.tid; osm_debug("qcmd: Tid = %03x\n", tid); diff --git a/drivers/mfd/88pm860x-core.c b/drivers/mfd/88pm860x-core.c index 2c65a2c5729..07933f3f7e4 100644 --- a/drivers/mfd/88pm860x-core.c +++ b/drivers/mfd/88pm860x-core.c @@ -74,12 +74,12 @@ static struct mfd_cell backlight_devs[] = { } static struct resource led_resources[] = { - PM8606_LED_RESOURCE(PM8606_LED1_RED, RGB2B), - PM8606_LED_RESOURCE(PM8606_LED1_GREEN, RGB2C), - PM8606_LED_RESOURCE(PM8606_LED1_BLUE, RGB2D), - PM8606_LED_RESOURCE(PM8606_LED2_RED, RGB1B), - PM8606_LED_RESOURCE(PM8606_LED2_GREEN, RGB1C), - PM8606_LED_RESOURCE(PM8606_LED2_BLUE, RGB1D), + PM8606_LED_RESOURCE(PM8606_LED1_RED, RGB1B), + PM8606_LED_RESOURCE(PM8606_LED1_GREEN, RGB1C), + PM8606_LED_RESOURCE(PM8606_LED1_BLUE, RGB1D), + PM8606_LED_RESOURCE(PM8606_LED2_RED, RGB2B), + PM8606_LED_RESOURCE(PM8606_LED2_GREEN, RGB2C), + PM8606_LED_RESOURCE(PM8606_LED2_BLUE, RGB2D), }; #define PM8606_LED_DEVS(_i) \ @@ -428,52 +428,44 @@ static int __devinit device_gpadc_init(struct pm860x_chip *chip, { struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \ : chip->companion; - int use_gpadc = 0, data, ret; + int data; + int ret; /* initialize GPADC without activating it */ - if (pdata && pdata->touch) { - /* set GPADC MISC1 register */ - data = 0; - data |= (pdata->touch->gpadc_prebias << 1) - & PM8607_GPADC_PREBIAS_MASK; - data |= (pdata->touch->slot_cycle << 3) - & PM8607_GPADC_SLOT_CYCLE_MASK; - data |= (pdata->touch->off_scale << 5) - & PM8607_GPADC_OFF_SCALE_MASK; - data |= (pdata->touch->sw_cal << 7) - & PM8607_GPADC_SW_CAL_MASK; - if (data) { - ret = pm860x_reg_write(i2c, PM8607_GPADC_MISC1, data); - if (ret < 0) - goto out; - } - /* set tsi prebias time */ - if (pdata->touch->tsi_prebias) { - data = pdata->touch->tsi_prebias; - ret = pm860x_reg_write(i2c, PM8607_TSI_PREBIAS, data); - if (ret < 0) - goto out; - } - /* set prebias & prechg time of pen detect */ - data = 0; - data |= pdata->touch->pen_prebias & PM8607_PD_PREBIAS_MASK; - data |= (pdata->touch->pen_prechg << 5) - & PM8607_PD_PRECHG_MASK; - if (data) { - ret = pm860x_reg_write(i2c, PM8607_PD_PREBIAS, data); - if (ret < 0) - goto out; - } + if (!pdata || !pdata->touch) + return -EINVAL; - use_gpadc = 1; + /* set GPADC MISC1 register */ + data = 0; + data |= (pdata->touch->gpadc_prebias << 1) & PM8607_GPADC_PREBIAS_MASK; + data |= (pdata->touch->slot_cycle << 3) & PM8607_GPADC_SLOT_CYCLE_MASK; + data |= (pdata->touch->off_scale << 5) & PM8607_GPADC_OFF_SCALE_MASK; + data |= (pdata->touch->sw_cal << 7) & PM8607_GPADC_SW_CAL_MASK; + if (data) { + ret = pm860x_reg_write(i2c, PM8607_GPADC_MISC1, data); + if (ret < 0) + goto out; } - - /* turn on GPADC */ - if (use_gpadc) { - ret = pm860x_set_bits(i2c, PM8607_GPADC_MISC1, - PM8607_GPADC_EN, PM8607_GPADC_EN); + /* set tsi prebias time */ + if (pdata->touch->tsi_prebias) { + data = pdata->touch->tsi_prebias; + ret = pm860x_reg_write(i2c, PM8607_TSI_PREBIAS, data); + if (ret < 0) + goto out; } + /* set prebias & prechg time of pen detect */ + data = 0; + data |= pdata->touch->pen_prebias & PM8607_PD_PREBIAS_MASK; + data |= (pdata->touch->pen_prechg << 5) & PM8607_PD_PRECHG_MASK; + if (data) { + ret = pm860x_reg_write(i2c, PM8607_PD_PREBIAS, data); + if (ret < 0) + goto out; + } + + ret = pm860x_set_bits(i2c, PM8607_GPADC_MISC1, + PM8607_GPADC_EN, PM8607_GPADC_EN); out: return ret; } diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 9da0e504bbe..db51ea1c608 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -7,7 +7,16 @@ menuconfig MFD_SUPPORT depends on HAS_IOMEM default y help - Configure MFD device drivers. + Multifunction devices embed several functions (e.g. GPIOs, + touchscreens, keyboards, current regulators, power management chips, + etc...) in one single integrated circuit. They usually talk to the + main CPU through one or more IRQ lines and low speed data busses (SPI, + I2C, etc..). They appear as one single device to the main system + through the data bus and the MFD framework allows for sub devices + (a.k.a. functions) to appear as discrete platform devices. + MFDs are typically found on embedded platforms. + + This option alone does not add any kernel code. if MFD_SUPPORT @@ -177,6 +186,38 @@ config TWL4030_CODEC select MFD_CORE default n +config TWL6030_PWM + tristate "TWL6030 PWM (Pulse Width Modulator) Support" + depends on TWL4030_CORE + select HAVE_PWM + default n + help + Say yes here if you want support for TWL6030 PWM. + This is used to control charging LED brightness. + +config MFD_STMPE + bool "Support STMicroelectronics STMPE" + depends on I2C=y && GENERIC_HARDIRQS + select MFD_CORE + help + Support for the STMPE family of I/O Expanders from + STMicroelectronics. + + Currently supported devices are: + + STMPE811: GPIO, Touchscreen + STMPE1601: GPIO, Keypad + STMPE2401: GPIO, Keypad + STMPE2403: GPIO, Keypad + + This driver provides common support for accessing the device, + additional drivers must be enabled in order to use the functionality + of the device. Currently available sub drivers are: + + GPIO: stmpe-gpio + Keypad: stmpe-keypad + Touchscreen: stmpe-ts + config MFD_TC35892 bool "Support Toshiba TC35892" depends on I2C=y && GENERIC_HARDIRQS @@ -252,6 +293,16 @@ config MFD_MAX8925 accessing the device, additional drivers must be enabled in order to use the functionality of the device. +config MFD_MAX8998 + bool "Maxim Semiconductor MAX8998 PMIC Support" + depends on I2C=y + select MFD_CORE + help + Say yes here to support for Maxim Semiconductor MAX8998. This is + a Power Management IC. This driver provies common support for + accessing the device, additional drivers must be enabled in order + to use the functionality of the device. + config MFD_WM8400 tristate "Support Wolfson Microelectronics WM8400" select MFD_CORE @@ -482,6 +533,28 @@ config MFD_JANZ_CMODIO host many different types of MODULbus daughterboards, including CAN and GPIO controllers. +config MFD_JZ4740_ADC + tristate "Support for the JZ4740 SoC ADC core" + select MFD_CORE + depends on MACH_JZ4740 + help + Say yes here if you want support for the ADC unit in the JZ4740 SoC. + This driver is necessary for jz4740-battery and jz4740-hwmon driver. + +config MFD_TPS6586X + tristate "TPS6586x Power Management chips" + depends on I2C && GPIOLIB + select MFD_CORE + help + If you say yes here you get support for the TPS6586X series of + Power Management chips. + This driver provides common support for accessing the device, + additional drivers must be enabled in order to use the + functionality of the device. + + This driver can also be built as a module. If so, the module + will be called tps6586x. + endif # MFD_SUPPORT menu "Multimedia Capabilities Port drivers" diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index fb503e77dc6..feaeeaeeddb 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_HTC_I2CPLD) += htc-i2cpld.o obj-$(CONFIG_MFD_DAVINCI_VOICECODEC) += davinci_voicecodec.o obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o +obj-$(CONFIG_MFD_STMPE) += stmpe.o obj-$(CONFIG_MFD_TC35892) += tc35892.o obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o tmio_core.o obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o tmio_core.o @@ -36,6 +37,7 @@ obj-$(CONFIG_MENELAUS) += menelaus.o obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o obj-$(CONFIG_TWL4030_CODEC) += twl4030-codec.o +obj-$(CONFIG_TWL6030_PWM) += twl6030-pwm.o obj-$(CONFIG_MFD_MC13783) += mc13783-core.o @@ -56,6 +58,7 @@ obj-$(CONFIG_UCB1400_CORE) += ucb1400_core.o obj-$(CONFIG_PMIC_DA903X) += da903x.o max8925-objs := max8925-core.o max8925-i2c.o obj-$(CONFIG_MFD_MAX8925) += max8925.o +obj-$(CONFIG_MFD_MAX8998) += max8998.o pcf50633-objs := pcf50633-core.o pcf50633-irq.o obj-$(CONFIG_MFD_PCF50633) += pcf50633.o @@ -71,3 +74,5 @@ obj-$(CONFIG_PMIC_ADP5520) += adp5520.o obj-$(CONFIG_LPC_SCH) += lpc_sch.o obj-$(CONFIG_MFD_RDC321X) += rdc321x-southbridge.o obj-$(CONFIG_MFD_JANZ_CMODIO) += janz-cmodio.o +obj-$(CONFIG_MFD_JZ4740_ADC) += jz4740-adc.o +obj-$(CONFIG_MFD_TPS6586X) += tps6586x.o diff --git a/drivers/mfd/ab3100-otp.c b/drivers/mfd/ab3100-otp.c index 63d2b727ddb..8440010eb2b 100644 --- a/drivers/mfd/ab3100-otp.c +++ b/drivers/mfd/ab3100-otp.c @@ -199,7 +199,7 @@ static int __init ab3100_otp_probe(struct platform_device *pdev) err = ab3100_otp_read(otp); if (err) - return err; + goto err_otp_read; dev_info(&pdev->dev, "AB3100 OTP readout registered\n"); @@ -208,21 +208,21 @@ static int __init ab3100_otp_probe(struct platform_device *pdev) err = device_create_file(&pdev->dev, &ab3100_otp_attrs[i]); if (err) - goto out_no_sysfs; + goto err_create_file; } /* debugfs entries */ err = ab3100_otp_init_debugfs(&pdev->dev, otp); if (err) - goto out_no_debugfs; + goto err_init_debugfs; return 0; -out_no_sysfs: - for (i = 0; i < ARRAY_SIZE(ab3100_otp_attrs); i++) - device_remove_file(&pdev->dev, - &ab3100_otp_attrs[i]); -out_no_debugfs: +err_init_debugfs: +err_create_file: + while (--i >= 0) + device_remove_file(&pdev->dev, &ab3100_otp_attrs[i]); +err_otp_read: kfree(otp); return err; } diff --git a/drivers/mfd/ab3550-core.c b/drivers/mfd/ab3550-core.c index f54ab62e7bc..8a98739e6d9 100644 --- a/drivers/mfd/ab3550-core.c +++ b/drivers/mfd/ab3550-core.c @@ -589,16 +589,16 @@ static bool reg_read_allowed(const struct ab3550_reg_ranges *ranges, u8 reg) } /* - * The exported register access functionality. + * The register access functionality. */ -int ab3550_get_chip_id(struct device *dev) +static int ab3550_get_chip_id(struct device *dev) { struct ab3550 *ab = dev_get_drvdata(dev->parent); return (int)ab->chip_id; } -int ab3550_mask_and_set_register_interruptible(struct device *dev, u8 bank, - u8 reg, u8 bitmask, u8 bitvalues) +static int ab3550_mask_and_set_register_interruptible(struct device *dev, + u8 bank, u8 reg, u8 bitmask, u8 bitvalues) { struct ab3550 *ab; struct platform_device *pdev = to_platform_device(dev); @@ -612,15 +612,15 @@ int ab3550_mask_and_set_register_interruptible(struct device *dev, u8 bank, bitmask, bitvalues); } -int ab3550_set_register_interruptible(struct device *dev, u8 bank, u8 reg, - u8 value) +static int ab3550_set_register_interruptible(struct device *dev, u8 bank, + u8 reg, u8 value) { return ab3550_mask_and_set_register_interruptible(dev, bank, reg, 0xFF, value); } -int ab3550_get_register_interruptible(struct device *dev, u8 bank, u8 reg, - u8 *value) +static int ab3550_get_register_interruptible(struct device *dev, u8 bank, + u8 reg, u8 *value) { struct ab3550 *ab; struct platform_device *pdev = to_platform_device(dev); @@ -633,7 +633,7 @@ int ab3550_get_register_interruptible(struct device *dev, u8 bank, u8 reg, return get_register_interruptible(ab, bank, reg, value); } -int ab3550_get_register_page_interruptible(struct device *dev, u8 bank, +static int ab3550_get_register_page_interruptible(struct device *dev, u8 bank, u8 first_reg, u8 *regvals, u8 numregs) { struct ab3550 *ab; @@ -649,7 +649,8 @@ int ab3550_get_register_page_interruptible(struct device *dev, u8 bank, numregs); } -int ab3550_event_registers_startup_state_get(struct device *dev, u8 *event) +static int ab3550_event_registers_startup_state_get(struct device *dev, + u8 *event) { struct ab3550 *ab; @@ -661,7 +662,7 @@ int ab3550_event_registers_startup_state_get(struct device *dev, u8 *event) return 0; } -int ab3550_startup_irq_enabled(struct device *dev, unsigned int irq) +static int ab3550_startup_irq_enabled(struct device *dev, unsigned int irq) { struct ab3550 *ab; struct ab3550_platform_data *plf_data; diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index f3d26fa9c34..defa786dee3 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -16,6 +16,7 @@ #include <linux/platform_device.h> #include <linux/mfd/core.h> #include <linux/mfd/ab8500.h> +#include <linux/regulator/ab8500.h> /* * Interrupt register offsets @@ -352,6 +353,7 @@ static struct mfd_cell ab8500_devs[] = { { .name = "ab8500-audio", }, { .name = "ab8500-usb", }, { .name = "ab8500-pwm", }, + { .name = "ab8500-regulator", }, }; int __devinit ab8500_init(struct ab8500 *ab8500) @@ -411,7 +413,7 @@ int __devinit ab8500_init(struct ab8500 *ab8500) goto out_removeirq; } - ret = mfd_add_devices(ab8500->dev, -1, ab8500_devs, + ret = mfd_add_devices(ab8500->dev, 0, ab8500_devs, ARRAY_SIZE(ab8500_devs), NULL, ab8500->irq_base); if (ret) diff --git a/drivers/mfd/ab8500-spi.c b/drivers/mfd/ab8500-spi.c index b81d4f768ef..e1c8b62b086 100644 --- a/drivers/mfd/ab8500-spi.c +++ b/drivers/mfd/ab8500-spi.c @@ -68,7 +68,12 @@ static int ab8500_spi_read(struct ab8500 *ab8500, u16 addr) ret = spi_sync(spi, &msg); if (!ret) - ret = ab8500->rx_buf[0]; + /* + * Only the 8 lowermost bytes are + * defined with value, the rest may + * vary depending on chip/board noise. + */ + ret = ab8500->rx_buf[0] & 0xFFU; return ret; } diff --git a/drivers/mfd/abx500-core.c b/drivers/mfd/abx500-core.c index 3b3b97ec32a..f12720dbe12 100644 --- a/drivers/mfd/abx500-core.c +++ b/drivers/mfd/abx500-core.c @@ -36,7 +36,7 @@ int abx500_register_ops(struct device *dev, struct abx500_ops *ops) struct abx500_device_entry *dev_entry; dev_entry = kzalloc(sizeof(struct abx500_device_entry), GFP_KERNEL); - if (IS_ERR(dev_entry)) { + if (!dev_entry) { dev_err(dev, "register_ops kzalloc failed"); return -ENOMEM; } diff --git a/drivers/mfd/davinci_voicecodec.c b/drivers/mfd/davinci_voicecodec.c index 3e75f02e477..33c923d215c 100644 --- a/drivers/mfd/davinci_voicecodec.c +++ b/drivers/mfd/davinci_voicecodec.c @@ -94,7 +94,8 @@ static int __init davinci_vc_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_DMA, 0); if (!res) { dev_err(&pdev->dev, "no DMA resource\n"); - return -ENXIO; + ret = -ENXIO; + goto fail4; } davinci_vc->davinci_vcif.dma_tx_channel = res->start; @@ -104,7 +105,8 @@ static int __init davinci_vc_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_DMA, 1); if (!res) { dev_err(&pdev->dev, "no DMA resource\n"); - return -ENXIO; + ret = -ENXIO; + goto fail4; } davinci_vc->davinci_vcif.dma_rx_channel = res->start; diff --git a/drivers/mfd/janz-cmodio.c b/drivers/mfd/janz-cmodio.c index 9ed630799ac..36a166bcdb0 100644 --- a/drivers/mfd/janz-cmodio.c +++ b/drivers/mfd/janz-cmodio.c @@ -18,6 +18,7 @@ #include <linux/interrupt.h> #include <linux/delay.h> #include <linux/platform_device.h> +#include <linux/slab.h> #include <linux/mfd/core.h> #include <linux/mfd/janz.h> diff --git a/drivers/mfd/jz4740-adc.c b/drivers/mfd/jz4740-adc.c new file mode 100644 index 00000000000..3ad492cb6c4 --- /dev/null +++ b/drivers/mfd/jz4740-adc.c @@ -0,0 +1,394 @@ +/* + * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> + * JZ4740 SoC ADC driver + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + * This driver synchronizes access to the JZ4740 ADC core between the + * JZ4740 battery and hwmon drivers. + */ + +#include <linux/err.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/spinlock.h> + +#include <linux/clk.h> +#include <linux/mfd/core.h> + +#include <linux/jz4740-adc.h> + + +#define JZ_REG_ADC_ENABLE 0x00 +#define JZ_REG_ADC_CFG 0x04 +#define JZ_REG_ADC_CTRL 0x08 +#define JZ_REG_ADC_STATUS 0x0c + +#define JZ_REG_ADC_TOUCHSCREEN_BASE 0x10 +#define JZ_REG_ADC_BATTERY_BASE 0x1c +#define JZ_REG_ADC_HWMON_BASE 0x20 + +#define JZ_ADC_ENABLE_TOUCH BIT(2) +#define JZ_ADC_ENABLE_BATTERY BIT(1) +#define JZ_ADC_ENABLE_ADCIN BIT(0) + +enum { + JZ_ADC_IRQ_ADCIN = 0, + JZ_ADC_IRQ_BATTERY, + JZ_ADC_IRQ_TOUCH, + JZ_ADC_IRQ_PENUP, + JZ_ADC_IRQ_PENDOWN, +}; + +struct jz4740_adc { + struct resource *mem; + void __iomem *base; + + int irq; + int irq_base; + + struct clk *clk; + atomic_t clk_ref; + + spinlock_t lock; +}; + +static inline void jz4740_adc_irq_set_masked(struct jz4740_adc *adc, int irq, + bool masked) +{ + unsigned long flags; + uint8_t val; + + irq -= adc->irq_base; + + spin_lock_irqsave(&adc->lock, flags); + + val = readb(adc->base + JZ_REG_ADC_CTRL); + if (masked) + val |= BIT(irq); + else + val &= ~BIT(irq); + writeb(val, adc->base + JZ_REG_ADC_CTRL); + + spin_unlock_irqrestore(&adc->lock, flags); +} + +static void jz4740_adc_irq_mask(unsigned int irq) +{ + struct jz4740_adc *adc = get_irq_chip_data(irq); + jz4740_adc_irq_set_masked(adc, irq, true); +} + +static void jz4740_adc_irq_unmask(unsigned int irq) +{ + struct jz4740_adc *adc = get_irq_chip_data(irq); + jz4740_adc_irq_set_masked(adc, irq, false); +} + +static void jz4740_adc_irq_ack(unsigned int irq) +{ + struct jz4740_adc *adc = get_irq_chip_data(irq); + + irq -= adc->irq_base; + writeb(BIT(irq), adc->base + JZ_REG_ADC_STATUS); +} + +static struct irq_chip jz4740_adc_irq_chip = { + .name = "jz4740-adc", + .mask = jz4740_adc_irq_mask, + .unmask = jz4740_adc_irq_unmask, + .ack = jz4740_adc_irq_ack, +}; + +static void jz4740_adc_irq_demux(unsigned int irq, struct irq_desc *desc) +{ + struct jz4740_adc *adc = get_irq_desc_data(desc); + uint8_t status; + unsigned int i; + + status = readb(adc->base + JZ_REG_ADC_STATUS); + + for (i = 0; i < 5; ++i) { + if (status & BIT(i)) + generic_handle_irq(adc->irq_base + i); + } +} + + +/* Refcounting for the ADC clock is done in here instead of in the clock + * framework, because it is the only clock which is shared between multiple + * devices and thus is the only clock which needs refcounting */ +static inline void jz4740_adc_clk_enable(struct jz4740_adc *adc) +{ + if (atomic_inc_return(&adc->clk_ref) == 1) + clk_enable(adc->clk); +} + +static inline void jz4740_adc_clk_disable(struct jz4740_adc *adc) +{ + if (atomic_dec_return(&adc->clk_ref) == 0) + clk_disable(adc->clk); +} + +static inline void jz4740_adc_set_enabled(struct jz4740_adc *adc, int engine, + bool enabled) +{ + unsigned long flags; + uint8_t val; + + spin_lock_irqsave(&adc->lock, flags); + + val = readb(adc->base + JZ_REG_ADC_ENABLE); + if (enabled) + val |= BIT(engine); + else + val &= BIT(engine); + writeb(val, adc->base + JZ_REG_ADC_ENABLE); + + spin_unlock_irqrestore(&adc->lock, flags); +} + +static int jz4740_adc_cell_enable(struct platform_device *pdev) +{ + struct jz4740_adc *adc = dev_get_drvdata(pdev->dev.parent); + + jz4740_adc_clk_enable(adc); + jz4740_adc_set_enabled(adc, pdev->id, true); + + return 0; +} + +static int jz4740_adc_cell_disable(struct platform_device *pdev) +{ + struct jz4740_adc *adc = dev_get_drvdata(pdev->dev.parent); + + jz4740_adc_set_enabled(adc, pdev->id, false); + jz4740_adc_clk_disable(adc); + + return 0; +} + +int jz4740_adc_set_config(struct device *dev, uint32_t mask, uint32_t val) +{ + struct jz4740_adc *adc = dev_get_drvdata(dev); + unsigned long flags; + uint32_t cfg; + + if (!adc) + return -ENODEV; + + spin_lock_irqsave(&adc->lock, flags); + + cfg = readl(adc->base + JZ_REG_ADC_CFG); + + cfg &= ~mask; + cfg |= val; + + writel(cfg, adc->base + JZ_REG_ADC_CFG); + + spin_unlock_irqrestore(&adc->lock, flags); + + return 0; +} +EXPORT_SYMBOL_GPL(jz4740_adc_set_config); + +static struct resource jz4740_hwmon_resources[] = { + { + .start = JZ_ADC_IRQ_ADCIN, + .flags = IORESOURCE_IRQ, + }, + { + .start = JZ_REG_ADC_HWMON_BASE, + .end = JZ_REG_ADC_HWMON_BASE + 3, + .flags = IORESOURCE_MEM, + }, +}; + +static struct resource jz4740_battery_resources[] = { + { + .start = JZ_ADC_IRQ_BATTERY, + .flags = IORESOURCE_IRQ, + }, + { + .start = JZ_REG_ADC_BATTERY_BASE, + .end = JZ_REG_ADC_BATTERY_BASE + 3, + .flags = IORESOURCE_MEM, + }, +}; + +const struct mfd_cell jz4740_adc_cells[] = { + { + .id = 0, + .name = "jz4740-hwmon", + .num_resources = ARRAY_SIZE(jz4740_hwmon_resources), + .resources = jz4740_hwmon_resources, + .platform_data = (void *)&jz4740_adc_cells[0], + .data_size = sizeof(struct mfd_cell), + + .enable = jz4740_adc_cell_enable, + .disable = jz4740_adc_cell_disable, + }, + { + .id = 1, + .name = "jz4740-battery", + .num_resources = ARRAY_SIZE(jz4740_battery_resources), + .resources = jz4740_battery_resources, + .platform_data = (void *)&jz4740_adc_cells[1], + .data_size = sizeof(struct mfd_cell), + + .enable = jz4740_adc_cell_enable, + .disable = jz4740_adc_cell_disable, + }, +}; + +static int __devinit jz4740_adc_probe(struct platform_device *pdev) +{ + int ret; + struct jz4740_adc *adc; + struct resource *mem_base; + int irq; + + adc = kmalloc(sizeof(*adc), GFP_KERNEL); + if (!adc) { + dev_err(&pdev->dev, "Failed to allocate driver structure\n"); + return -ENOMEM; + } + + adc->irq = platform_get_irq(pdev, 0); + if (adc->irq < 0) { + ret = adc->irq; + dev_err(&pdev->dev, "Failed to get platform irq: %d\n", ret); + goto err_free; + } + + adc->irq_base = platform_get_irq(pdev, 1); + if (adc->irq_base < 0) { + ret = adc->irq_base; + dev_err(&pdev->dev, "Failed to get irq base: %d\n", ret); + goto err_free; + } + + mem_base = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem_base) { + ret = -ENOENT; + dev_err(&pdev->dev, "Failed to get platform mmio resource\n"); + goto err_free; + } + + /* Only request the shared registers for the MFD driver */ + adc->mem = request_mem_region(mem_base->start, JZ_REG_ADC_STATUS, + pdev->name); + if (!adc->mem) { + ret = -EBUSY; + dev_err(&pdev->dev, "Failed to request mmio memory region\n"); + goto err_free; + } + + adc->base = ioremap_nocache(adc->mem->start, resource_size(adc->mem)); + if (!adc->base) { + ret = -EBUSY; + dev_err(&pdev->dev, "Failed to ioremap mmio memory\n"); + goto err_release_mem_region; + } + + adc->clk = clk_get(&pdev->dev, "adc"); + if (IS_ERR(adc->clk)) { + ret = PTR_ERR(adc->clk); + dev_err(&pdev->dev, "Failed to get clock: %d\n", ret); + goto err_iounmap; + } + + spin_lock_init(&adc->lock); + atomic_set(&adc->clk_ref, 0); + + platform_set_drvdata(pdev, adc); + + for (irq = adc->irq_base; irq < adc->irq_base + 5; ++irq) { + set_irq_chip_data(irq, adc); + set_irq_chip_and_handler(irq, &jz4740_adc_irq_chip, + handle_level_irq); + } + + set_irq_data(adc->irq, adc); + set_irq_chained_handler(adc->irq, jz4740_adc_irq_demux); + + writeb(0x00, adc->base + JZ_REG_ADC_ENABLE); + writeb(0xff, adc->base + JZ_REG_ADC_CTRL); + + ret = mfd_add_devices(&pdev->dev, 0, jz4740_adc_cells, + ARRAY_SIZE(jz4740_adc_cells), mem_base, adc->irq_base); + if (ret < 0) + goto err_clk_put; + + return 0; + +err_clk_put: + clk_put(adc->clk); +err_iounmap: + platform_set_drvdata(pdev, NULL); + iounmap(adc->base); +err_release_mem_region: + release_mem_region(adc->mem->start, resource_size(adc->mem)); +err_free: + kfree(adc); + + return ret; +} + +static int __devexit jz4740_adc_remove(struct platform_device *pdev) +{ + struct jz4740_adc *adc = platform_get_drvdata(pdev); + + mfd_remove_devices(&pdev->dev); + + set_irq_data(adc->irq, NULL); + set_irq_chained_handler(adc->irq, NULL); + + iounmap(adc->base); + release_mem_region(adc->mem->start, resource_size(adc->mem)); + + clk_put(adc->clk); + + platform_set_drvdata(pdev, NULL); + + kfree(adc); + + return 0; +} + +struct platform_driver jz4740_adc_driver = { + .probe = jz4740_adc_probe, + .remove = __devexit_p(jz4740_adc_remove), + .driver = { + .name = "jz4740-adc", + .owner = THIS_MODULE, + }, +}; + +static int __init jz4740_adc_init(void) +{ + return platform_driver_register(&jz4740_adc_driver); +} +module_init(jz4740_adc_init); + +static void __exit jz4740_adc_exit(void) +{ + platform_driver_unregister(&jz4740_adc_driver); +} +module_exit(jz4740_adc_exit); + +MODULE_DESCRIPTION("JZ4740 SoC ADC driver"); +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:jz4740-adc"); diff --git a/drivers/mfd/max8925-core.c b/drivers/mfd/max8925-core.c index f621bcea3d0..04028a9ee08 100644 --- a/drivers/mfd/max8925-core.c +++ b/drivers/mfd/max8925-core.c @@ -90,6 +90,24 @@ static struct mfd_cell rtc_devs[] = { }, }; +static struct resource onkey_resources[] = { + { + .name = "max8925-onkey", + .start = MAX8925_IRQ_GPM_SW_3SEC, + .end = MAX8925_IRQ_GPM_SW_3SEC, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct mfd_cell onkey_devs[] = { + { + .name = "max8925-onkey", + .num_resources = 1, + .resources = &onkey_resources[0], + .id = -1, + }, +}; + #define MAX8925_REG_RESOURCE(_start, _end) \ { \ .start = MAX8925_##_start, \ @@ -596,6 +614,15 @@ int __devinit max8925_device_init(struct max8925_chip *chip, dev_err(chip->dev, "Failed to add rtc subdev\n"); goto out; } + + ret = mfd_add_devices(chip->dev, 0, &onkey_devs[0], + ARRAY_SIZE(onkey_devs), + &onkey_resources[0], 0); + if (ret < 0) { + dev_err(chip->dev, "Failed to add onkey subdev\n"); + goto out_dev; + } + if (pdata && pdata->regulator[0]) { ret = mfd_add_devices(chip->dev, 0, ®ulator_devs[0], ARRAY_SIZE(regulator_devs), diff --git a/drivers/mfd/max8998.c b/drivers/mfd/max8998.c new file mode 100644 index 00000000000..73e6f5c4efc --- /dev/null +++ b/drivers/mfd/max8998.c @@ -0,0 +1,158 @@ +/* + * max8698.c - mfd core driver for the Maxim 8998 + * + * Copyright (C) 2009-2010 Samsung Electronics + * Kyungmin Park <kyungmin.park@samsung.com> + * Marek Szyprowski <m.szyprowski@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/mutex.h> +#include <linux/mfd/core.h> +#include <linux/mfd/max8998.h> +#include <linux/mfd/max8998-private.h> + +static struct mfd_cell max8998_devs[] = { + { + .name = "max8998-pmic", + } +}; + +static int max8998_i2c_device_read(struct max8998_dev *max8998, u8 reg, u8 *dest) +{ + struct i2c_client *client = max8998->i2c_client; + int ret; + + mutex_lock(&max8998->iolock); + ret = i2c_smbus_read_byte_data(client, reg); + mutex_unlock(&max8998->iolock); + if (ret < 0) + return ret; + + ret &= 0xff; + *dest = ret; + return 0; +} + +static int max8998_i2c_device_write(struct max8998_dev *max8998, u8 reg, u8 value) +{ + struct i2c_client *client = max8998->i2c_client; + int ret; + + mutex_lock(&max8998->iolock); + ret = i2c_smbus_write_byte_data(client, reg, value); + mutex_unlock(&max8998->iolock); + return ret; +} + +static int max8998_i2c_device_update(struct max8998_dev *max8998, u8 reg, + u8 val, u8 mask) +{ + struct i2c_client *client = max8998->i2c_client; + int ret; + + mutex_lock(&max8998->iolock); + ret = i2c_smbus_read_byte_data(client, reg); + if (ret >= 0) { + u8 old_val = ret & 0xff; + u8 new_val = (val & mask) | (old_val & (~mask)); + ret = i2c_smbus_write_byte_data(client, reg, new_val); + if (ret >= 0) + ret = 0; + } + mutex_unlock(&max8998->iolock); + return ret; +} + +static int max8998_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct max8998_dev *max8998; + int ret = 0; + + max8998 = kzalloc(sizeof(struct max8998_dev), GFP_KERNEL); + if (max8998 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, max8998); + max8998->dev = &i2c->dev; + max8998->i2c_client = i2c; + max8998->dev_read = max8998_i2c_device_read; + max8998->dev_write = max8998_i2c_device_write; + max8998->dev_update = max8998_i2c_device_update; + mutex_init(&max8998->iolock); + + ret = mfd_add_devices(max8998->dev, -1, + max8998_devs, ARRAY_SIZE(max8998_devs), + NULL, 0); + if (ret < 0) + goto err; + + return ret; + +err: + mfd_remove_devices(max8998->dev); + kfree(max8998); + return ret; +} + +static int max8998_i2c_remove(struct i2c_client *i2c) +{ + struct max8998_dev *max8998 = i2c_get_clientdata(i2c); + + mfd_remove_devices(max8998->dev); + kfree(max8998); + + return 0; +} + +static const struct i2c_device_id max8998_i2c_id[] = { + { "max8998", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max8998_i2c_id); + +static struct i2c_driver max8998_i2c_driver = { + .driver = { + .name = "max8998", + .owner = THIS_MODULE, + }, + .probe = max8998_i2c_probe, + .remove = max8998_i2c_remove, + .id_table = max8998_i2c_id, +}; + +static int __init max8998_i2c_init(void) +{ + return i2c_add_driver(&max8998_i2c_driver); +} +/* init early so consumer devices can complete system boot */ +subsys_initcall(max8998_i2c_init); + +static void __exit max8998_i2c_exit(void) +{ + i2c_del_driver(&max8998_i2c_driver); +} +module_exit(max8998_i2c_exit); + +MODULE_DESCRIPTION("MAXIM 8998 multi-function core driver"); +MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/mc13783-core.c b/drivers/mfd/mc13783-core.c index fecf38a4f02..6df34989c1f 100644 --- a/drivers/mfd/mc13783-core.c +++ b/drivers/mfd/mc13783-core.c @@ -11,9 +11,31 @@ */ #include <linux/slab.h> #include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/mutex.h> +#include <linux/interrupt.h> #include <linux/spi/spi.h> #include <linux/mfd/core.h> -#include <linux/mfd/mc13783-private.h> +#include <linux/mfd/mc13783.h> + +struct mc13783 { + struct spi_device *spidev; + struct mutex lock; + int irq; + int flags; + + irq_handler_t irqhandler[MC13783_NUM_IRQ]; + void *irqdata[MC13783_NUM_IRQ]; + + /* XXX these should go as platformdata to the regulator subdevice */ + struct mc13783_regulator_init_data *regulators; + int num_regulators; +}; + +#define MC13783_REG_REVISION 7 +#define MC13783_REG_ADC_0 43 +#define MC13783_REG_ADC_1 44 +#define MC13783_REG_ADC_2 45 #define MC13783_IRQSTAT0 0 #define MC13783_IRQSTAT0_ADCDONEI (1 << 0) @@ -226,6 +248,12 @@ int mc13783_reg_rmw(struct mc13783 *mc13783, unsigned int offset, } EXPORT_SYMBOL(mc13783_reg_rmw); +int mc13783_get_flags(struct mc13783 *mc13783) +{ + return mc13783->flags; +} +EXPORT_SYMBOL(mc13783_get_flags); + int mc13783_irq_mask(struct mc13783 *mc13783, int irq) { int ret; diff --git a/drivers/mfd/menelaus.c b/drivers/mfd/menelaus.c index a3fb4bcb988..4ba85bbdb4c 100644 --- a/drivers/mfd/menelaus.c +++ b/drivers/mfd/menelaus.c @@ -128,6 +128,39 @@ #define MENELAUS_RESERVED14_IRQ 14 /* Reserved */ #define MENELAUS_RESERVED15_IRQ 15 /* Reserved */ +/* VCORE_CTRL1 register */ +#define VCORE_CTRL1_BYP_COMP (1 << 5) +#define VCORE_CTRL1_HW_NSW (1 << 7) + +/* GPIO_CTRL register */ +#define GPIO_CTRL_SLOTSELEN (1 << 5) +#define GPIO_CTRL_SLPCTLEN (1 << 6) +#define GPIO1_DIR_INPUT (1 << 0) +#define GPIO2_DIR_INPUT (1 << 1) +#define GPIO3_DIR_INPUT (1 << 2) + +/* MCT_CTRL1 register */ +#define MCT_CTRL1_S1_CMD_OD (1 << 2) +#define MCT_CTRL1_S2_CMD_OD (1 << 3) + +/* MCT_CTRL2 register */ +#define MCT_CTRL2_VS2_SEL_D0 (1 << 0) +#define MCT_CTRL2_VS2_SEL_D1 (1 << 1) +#define MCT_CTRL2_S1CD_BUFEN (1 << 4) +#define MCT_CTRL2_S2CD_BUFEN (1 << 5) +#define MCT_CTRL2_S1CD_DBEN (1 << 6) +#define MCT_CTRL2_S2CD_BEN (1 << 7) + +/* MCT_CTRL3 register */ +#define MCT_CTRL3_SLOT1_EN (1 << 0) +#define MCT_CTRL3_SLOT2_EN (1 << 1) +#define MCT_CTRL3_S1_AUTO_EN (1 << 2) +#define MCT_CTRL3_S2_AUTO_EN (1 << 3) + +/* MCT_PIN_ST register */ +#define MCT_PIN_ST_S1_CD_ST (1 << 0) +#define MCT_PIN_ST_S2_CD_ST (1 << 1) + static void menelaus_work(struct work_struct *_menelaus); struct menelaus_chip { @@ -249,10 +282,10 @@ static void menelaus_mmc_cd_work(struct menelaus_chip *menelaus_hw) return; if (!(reg & 0x1)) - card_mask |= (1 << 0); + card_mask |= MCT_PIN_ST_S1_CD_ST; if (!(reg & 0x2)) - card_mask |= (1 << 1); + card_mask |= MCT_PIN_ST_S2_CD_ST; if (menelaus_hw->mmc_callback) menelaus_hw->mmc_callback(menelaus_hw->mmc_callback_data, @@ -277,14 +310,14 @@ int menelaus_set_mmc_opendrain(int slot, int enable) val = ret; if (slot == 1) { if (enable) - val |= 1 << 2; + val |= MCT_CTRL1_S1_CMD_OD; else - val &= ~(1 << 2); + val &= ~MCT_CTRL1_S1_CMD_OD; } else { if (enable) - val |= 1 << 3; + val |= MCT_CTRL1_S2_CMD_OD; else - val &= ~(1 << 3); + val &= ~MCT_CTRL1_S2_CMD_OD; } ret = menelaus_write_reg(MENELAUS_MCT_CTRL1, val); mutex_unlock(&the_menelaus->lock); @@ -301,11 +334,11 @@ int menelaus_set_slot_sel(int enable) ret = menelaus_read_reg(MENELAUS_GPIO_CTRL); if (ret < 0) goto out; - ret |= 0x02; + ret |= GPIO2_DIR_INPUT; if (enable) - ret |= 1 << 5; + ret |= GPIO_CTRL_SLOTSELEN; else - ret &= ~(1 << 5); + ret &= ~GPIO_CTRL_SLOTSELEN; ret = menelaus_write_reg(MENELAUS_GPIO_CTRL, ret); out: mutex_unlock(&the_menelaus->lock); @@ -330,14 +363,14 @@ int menelaus_set_mmc_slot(int slot, int enable, int power, int cd_en) val = ret; if (slot == 1) { if (cd_en) - val |= (1 << 4) | (1 << 6); + val |= MCT_CTRL2_S1CD_BUFEN | MCT_CTRL2_S1CD_DBEN; else - val &= ~((1 << 4) | (1 << 6)); + val &= ~(MCT_CTRL2_S1CD_BUFEN | MCT_CTRL2_S1CD_DBEN); } else { if (cd_en) - val |= (1 << 5) | (1 << 7); + val |= MCT_CTRL2_S2CD_BUFEN | MCT_CTRL2_S2CD_BEN; else - val &= ~((1 << 5) | (1 << 7)); + val &= ~(MCT_CTRL2_S2CD_BUFEN | MCT_CTRL2_S2CD_BEN); } ret = menelaus_write_reg(MENELAUS_MCT_CTRL2, val); if (ret < 0) @@ -349,25 +382,25 @@ int menelaus_set_mmc_slot(int slot, int enable, int power, int cd_en) val = ret; if (slot == 1) { if (enable) - val |= 1 << 0; + val |= MCT_CTRL3_SLOT1_EN; else - val &= ~(1 << 0); + val &= ~MCT_CTRL3_SLOT1_EN; } else { int b; if (enable) - ret |= 1 << 1; + val |= MCT_CTRL3_SLOT2_EN; else - ret &= ~(1 << 1); + val &= ~MCT_CTRL3_SLOT2_EN; b = menelaus_read_reg(MENELAUS_MCT_CTRL2); - b &= ~0x03; + b &= ~(MCT_CTRL2_VS2_SEL_D0 | MCT_CTRL2_VS2_SEL_D1); b |= power; ret = menelaus_write_reg(MENELAUS_MCT_CTRL2, b); if (ret < 0) goto out; } /* Disable autonomous shutdown */ - val &= ~(0x03 << 2); + val &= ~(MCT_CTRL3_S1_AUTO_EN | MCT_CTRL3_S2_AUTO_EN); ret = menelaus_write_reg(MENELAUS_MCT_CTRL3, val); out: mutex_unlock(&the_menelaus->lock); @@ -552,7 +585,7 @@ int menelaus_set_vcore_hw(unsigned int roof_mV, unsigned int floor_mV) if (!the_menelaus->vcore_hw_mode) { val = menelaus_read_reg(MENELAUS_VCORE_CTRL1); /* HW mode, turn OFF byte comparator */ - val |= ((1 << 7) | (1 << 5)); + val |= (VCORE_CTRL1_HW_NSW | VCORE_CTRL1_BYP_COMP); ret = menelaus_write_reg(MENELAUS_VCORE_CTRL1, val); the_menelaus->vcore_hw_mode = 1; } @@ -749,7 +782,7 @@ int menelaus_set_regulator_sleep(int enable, u32 val) ret = menelaus_read_reg(MENELAUS_GPIO_CTRL); if (ret < 0) goto out; - t = ((1 << 6) | 0x04); + t = (GPIO_CTRL_SLPCTLEN | GPIO3_DIR_INPUT); if (enable) ret |= t; else diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index 7dd76bceaae..1823a57b7d8 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -70,7 +70,9 @@ static int mfd_add_device(struct device *parent, int id, goto fail_res; } - platform_device_add_resources(pdev, res, cell->num_resources); + ret = platform_device_add_resources(pdev, res, cell->num_resources); + if (ret) + goto fail_res; ret = platform_device_add(pdev); if (ret) diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c new file mode 100644 index 00000000000..0754c5e9199 --- /dev/null +++ b/drivers/mfd/stmpe.c @@ -0,0 +1,985 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * License Terms: GNU General Public License, version 2 + * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/mfd/core.h> +#include <linux/mfd/stmpe.h> +#include "stmpe.h" + +static int __stmpe_enable(struct stmpe *stmpe, unsigned int blocks) +{ + return stmpe->variant->enable(stmpe, blocks, true); +} + +static int __stmpe_disable(struct stmpe *stmpe, unsigned int blocks) +{ + return stmpe->variant->enable(stmpe, blocks, false); +} + +static int __stmpe_reg_read(struct stmpe *stmpe, u8 reg) +{ + int ret; + + ret = i2c_smbus_read_byte_data(stmpe->i2c, reg); + if (ret < 0) + dev_err(stmpe->dev, "failed to read reg %#x: %d\n", + reg, ret); + + dev_vdbg(stmpe->dev, "rd: reg %#x => data %#x\n", reg, ret); + + return ret; +} + +static int __stmpe_reg_write(struct stmpe *stmpe, u8 reg, u8 val) +{ + int ret; + + dev_vdbg(stmpe->dev, "wr: reg %#x <= %#x\n", reg, val); + + ret = i2c_smbus_write_byte_data(stmpe->i2c, reg, val); + if (ret < 0) + dev_err(stmpe->dev, "failed to write reg %#x: %d\n", + reg, ret); + + return ret; +} + +static int __stmpe_set_bits(struct stmpe *stmpe, u8 reg, u8 mask, u8 val) +{ + int ret; + + ret = __stmpe_reg_read(stmpe, reg); + if (ret < 0) + return ret; + + ret &= ~mask; + ret |= val; + + return __stmpe_reg_write(stmpe, reg, ret); +} + +static int __stmpe_block_read(struct stmpe *stmpe, u8 reg, u8 length, + u8 *values) +{ + int ret; + + ret = i2c_smbus_read_i2c_block_data(stmpe->i2c, reg, length, values); + if (ret < 0) + dev_err(stmpe->dev, "failed to read regs %#x: %d\n", + reg, ret); + + dev_vdbg(stmpe->dev, "rd: reg %#x (%d) => ret %#x\n", reg, length, ret); + stmpe_dump_bytes("stmpe rd: ", values, length); + + return ret; +} + +static int __stmpe_block_write(struct stmpe *stmpe, u8 reg, u8 length, + const u8 *values) +{ + int ret; + + dev_vdbg(stmpe->dev, "wr: regs %#x (%d)\n", reg, length); + stmpe_dump_bytes("stmpe wr: ", values, length); + + ret = i2c_smbus_write_i2c_block_data(stmpe->i2c, reg, length, + values); + if (ret < 0) + dev_err(stmpe->dev, "failed to write regs %#x: %d\n", + reg, ret); + + return ret; +} + +/** + * stmpe_enable - enable blocks on an STMPE device + * @stmpe: Device to work on + * @blocks: Mask of blocks (enum stmpe_block values) to enable + */ +int stmpe_enable(struct stmpe *stmpe, unsigned int blocks) +{ + int ret; + + mutex_lock(&stmpe->lock); + ret = __stmpe_enable(stmpe, blocks); + mutex_unlock(&stmpe->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(stmpe_enable); + +/** + * stmpe_disable - disable blocks on an STMPE device + * @stmpe: Device to work on + * @blocks: Mask of blocks (enum stmpe_block values) to enable + */ +int stmpe_disable(struct stmpe *stmpe, unsigned int blocks) +{ + int ret; + + mutex_lock(&stmpe->lock); + ret = __stmpe_disable(stmpe, blocks); + mutex_unlock(&stmpe->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(stmpe_disable); + +/** + * stmpe_reg_read() - read a single STMPE register + * @stmpe: Device to read from + * @reg: Register to read + */ +int stmpe_reg_read(struct stmpe *stmpe, u8 reg) +{ + int ret; + + mutex_lock(&stmpe->lock); + ret = __stmpe_reg_read(stmpe, reg); + mutex_unlock(&stmpe->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(stmpe_reg_read); + +/** + * stmpe_reg_write() - write a single STMPE register + * @stmpe: Device to write to + * @reg: Register to write + * @val: Value to write + */ +int stmpe_reg_write(struct stmpe *stmpe, u8 reg, u8 val) +{ + int ret; + + mutex_lock(&stmpe->lock); + ret = __stmpe_reg_write(stmpe, reg, val); + mutex_unlock(&stmpe->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(stmpe_reg_write); + +/** + * stmpe_set_bits() - set the value of a bitfield in a STMPE register + * @stmpe: Device to write to + * @reg: Register to write + * @mask: Mask of bits to set + * @val: Value to set + */ +int stmpe_set_bits(struct stmpe *stmpe, u8 reg, u8 mask, u8 val) +{ + int ret; + + mutex_lock(&stmpe->lock); + ret = __stmpe_set_bits(stmpe, reg, mask, val); + mutex_unlock(&stmpe->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(stmpe_set_bits); + +/** + * stmpe_block_read() - read multiple STMPE registers + * @stmpe: Device to read from + * @reg: First register + * @length: Number of registers + * @values: Buffer to write to + */ +int stmpe_block_read(struct stmpe *stmpe, u8 reg, u8 length, u8 *values) +{ + int ret; + + mutex_lock(&stmpe->lock); + ret = __stmpe_block_read(stmpe, reg, length, values); + mutex_unlock(&stmpe->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(stmpe_block_read); + +/** + * stmpe_block_write() - write multiple STMPE registers + * @stmpe: Device to write to + * @reg: First register + * @length: Number of registers + * @values: Values to write + */ +int stmpe_block_write(struct stmpe *stmpe, u8 reg, u8 length, + const u8 *values) +{ + int ret; + + mutex_lock(&stmpe->lock); + ret = __stmpe_block_write(stmpe, reg, length, values); + mutex_unlock(&stmpe->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(stmpe_block_write); + +/** + * stmpe_set_altfunc: set the alternate function for STMPE pins + * @stmpe: Device to configure + * @pins: Bitmask of pins to affect + * @block: block to enable alternate functions for + * + * @pins is assumed to have a bit set for each of the bits whose alternate + * function is to be changed, numbered according to the GPIOXY numbers. + * + * If the GPIO module is not enabled, this function automatically enables it in + * order to perform the change. + */ +int stmpe_set_altfunc(struct stmpe *stmpe, u32 pins, enum stmpe_block block) +{ + struct stmpe_variant_info *variant = stmpe->variant; + u8 regaddr = stmpe->regs[STMPE_IDX_GPAFR_U_MSB]; + int af_bits = variant->af_bits; + int numregs = DIV_ROUND_UP(stmpe->num_gpios * af_bits, 8); + int afperreg = 8 / af_bits; + int mask = (1 << af_bits) - 1; + u8 regs[numregs]; + int af; + int ret; + + mutex_lock(&stmpe->lock); + + ret = __stmpe_enable(stmpe, STMPE_BLOCK_GPIO); + if (ret < 0) + goto out; + + ret = __stmpe_block_read(stmpe, regaddr, numregs, regs); + if (ret < 0) + goto out; + + af = variant->get_altfunc(stmpe, block); + + while (pins) { + int pin = __ffs(pins); + int regoffset = numregs - (pin / afperreg) - 1; + int pos = (pin % afperreg) * (8 / afperreg); + + regs[regoffset] &= ~(mask << pos); + regs[regoffset] |= af << pos; + + pins &= ~(1 << pin); + } + + ret = __stmpe_block_write(stmpe, regaddr, numregs, regs); + +out: + mutex_unlock(&stmpe->lock); + return ret; +} +EXPORT_SYMBOL_GPL(stmpe_set_altfunc); + +/* + * GPIO (all variants) + */ + +static struct resource stmpe_gpio_resources[] = { + /* Start and end filled dynamically */ + { + .flags = IORESOURCE_IRQ, + }, +}; + +static struct mfd_cell stmpe_gpio_cell = { + .name = "stmpe-gpio", + .resources = stmpe_gpio_resources, + .num_resources = ARRAY_SIZE(stmpe_gpio_resources), +}; + +/* + * Keypad (1601, 2401, 2403) + */ + +static struct resource stmpe_keypad_resources[] = { + { + .name = "KEYPAD", + .start = 0, + .end = 0, + .flags = IORESOURCE_IRQ, + }, + { + .name = "KEYPAD_OVER", + .start = 1, + .end = 1, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct mfd_cell stmpe_keypad_cell = { + .name = "stmpe-keypad", + .resources = stmpe_keypad_resources, + .num_resources = ARRAY_SIZE(stmpe_keypad_resources), +}; + +/* + * Touchscreen (STMPE811) + */ + +static struct resource stmpe_ts_resources[] = { + { + .name = "TOUCH_DET", + .start = 0, + .end = 0, + .flags = IORESOURCE_IRQ, + }, + { + .name = "FIFO_TH", + .start = 1, + .end = 1, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct mfd_cell stmpe_ts_cell = { + .name = "stmpe-ts", + .resources = stmpe_ts_resources, + .num_resources = ARRAY_SIZE(stmpe_ts_resources), +}; + +/* + * STMPE811 + */ + +static const u8 stmpe811_regs[] = { + [STMPE_IDX_CHIP_ID] = STMPE811_REG_CHIP_ID, + [STMPE_IDX_ICR_LSB] = STMPE811_REG_INT_CTRL, + [STMPE_IDX_IER_LSB] = STMPE811_REG_INT_EN, + [STMPE_IDX_ISR_MSB] = STMPE811_REG_INT_STA, + [STMPE_IDX_GPMR_LSB] = STMPE811_REG_GPIO_MP_STA, + [STMPE_IDX_GPSR_LSB] = STMPE811_REG_GPIO_SET_PIN, + [STMPE_IDX_GPCR_LSB] = STMPE811_REG_GPIO_CLR_PIN, + [STMPE_IDX_GPDR_LSB] = STMPE811_REG_GPIO_DIR, + [STMPE_IDX_GPRER_LSB] = STMPE811_REG_GPIO_RE, + [STMPE_IDX_GPFER_LSB] = STMPE811_REG_GPIO_FE, + [STMPE_IDX_GPAFR_U_MSB] = STMPE811_REG_GPIO_AF, + [STMPE_IDX_IEGPIOR_LSB] = STMPE811_REG_GPIO_INT_EN, + [STMPE_IDX_ISGPIOR_MSB] = STMPE811_REG_GPIO_INT_STA, + [STMPE_IDX_GPEDR_MSB] = STMPE811_REG_GPIO_ED, +}; + +static struct stmpe_variant_block stmpe811_blocks[] = { + { + .cell = &stmpe_gpio_cell, + .irq = STMPE811_IRQ_GPIOC, + .block = STMPE_BLOCK_GPIO, + }, + { + .cell = &stmpe_ts_cell, + .irq = STMPE811_IRQ_TOUCH_DET, + .block = STMPE_BLOCK_TOUCHSCREEN, + }, +}; + +static int stmpe811_enable(struct stmpe *stmpe, unsigned int blocks, + bool enable) +{ + unsigned int mask = 0; + + if (blocks & STMPE_BLOCK_GPIO) + mask |= STMPE811_SYS_CTRL2_GPIO_OFF; + + if (blocks & STMPE_BLOCK_ADC) + mask |= STMPE811_SYS_CTRL2_ADC_OFF; + + if (blocks & STMPE_BLOCK_TOUCHSCREEN) + mask |= STMPE811_SYS_CTRL2_TSC_OFF; + + return __stmpe_set_bits(stmpe, STMPE811_REG_SYS_CTRL2, mask, + enable ? 0 : mask); +} + +static int stmpe811_get_altfunc(struct stmpe *stmpe, enum stmpe_block block) +{ + /* 0 for touchscreen, 1 for GPIO */ + return block != STMPE_BLOCK_TOUCHSCREEN; +} + +static struct stmpe_variant_info stmpe811 = { + .name = "stmpe811", + .id_val = 0x0811, + .id_mask = 0xffff, + .num_gpios = 8, + .af_bits = 1, + .regs = stmpe811_regs, + .blocks = stmpe811_blocks, + .num_blocks = ARRAY_SIZE(stmpe811_blocks), + .num_irqs = STMPE811_NR_INTERNAL_IRQS, + .enable = stmpe811_enable, + .get_altfunc = stmpe811_get_altfunc, +}; + +/* + * STMPE1601 + */ + +static const u8 stmpe1601_regs[] = { + [STMPE_IDX_CHIP_ID] = STMPE1601_REG_CHIP_ID, + [STMPE_IDX_ICR_LSB] = STMPE1601_REG_ICR_LSB, + [STMPE_IDX_IER_LSB] = STMPE1601_REG_IER_LSB, + [STMPE_IDX_ISR_MSB] = STMPE1601_REG_ISR_MSB, + [STMPE_IDX_GPMR_LSB] = STMPE1601_REG_GPIO_MP_LSB, + [STMPE_IDX_GPSR_LSB] = STMPE1601_REG_GPIO_SET_LSB, + [STMPE_IDX_GPCR_LSB] = STMPE1601_REG_GPIO_CLR_LSB, + [STMPE_IDX_GPDR_LSB] = STMPE1601_REG_GPIO_SET_DIR_LSB, + [STMPE_IDX_GPRER_LSB] = STMPE1601_REG_GPIO_RE_LSB, + [STMPE_IDX_GPFER_LSB] = STMPE1601_REG_GPIO_FE_LSB, + [STMPE_IDX_GPAFR_U_MSB] = STMPE1601_REG_GPIO_AF_U_MSB, + [STMPE_IDX_IEGPIOR_LSB] = STMPE1601_REG_INT_EN_GPIO_MASK_LSB, + [STMPE_IDX_ISGPIOR_MSB] = STMPE1601_REG_INT_STA_GPIO_MSB, + [STMPE_IDX_GPEDR_MSB] = STMPE1601_REG_GPIO_ED_MSB, +}; + +static struct stmpe_variant_block stmpe1601_blocks[] = { + { + .cell = &stmpe_gpio_cell, + .irq = STMPE24XX_IRQ_GPIOC, + .block = STMPE_BLOCK_GPIO, + }, + { + .cell = &stmpe_keypad_cell, + .irq = STMPE24XX_IRQ_KEYPAD, + .block = STMPE_BLOCK_KEYPAD, + }, +}; + +/* supported autosleep timeout delay (in msecs) */ +static const int stmpe_autosleep_delay[] = { + 4, 16, 32, 64, 128, 256, 512, 1024, +}; + +static int stmpe_round_timeout(int timeout) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(stmpe_autosleep_delay); i++) { + if (stmpe_autosleep_delay[i] >= timeout) + return i; + } + + /* + * requests for delays longer than supported should not return the + * longest supported delay + */ + return -EINVAL; +} + +static int stmpe_autosleep(struct stmpe *stmpe, int autosleep_timeout) +{ + int ret; + + if (!stmpe->variant->enable_autosleep) + return -ENOSYS; + + mutex_lock(&stmpe->lock); + ret = stmpe->variant->enable_autosleep(stmpe, autosleep_timeout); + mutex_unlock(&stmpe->lock); + + return ret; +} + +/* + * Both stmpe 1601/2403 support same layout for autosleep + */ +static int stmpe1601_autosleep(struct stmpe *stmpe, + int autosleep_timeout) +{ + int ret, timeout; + + /* choose the best available timeout */ + timeout = stmpe_round_timeout(autosleep_timeout); + if (timeout < 0) { + dev_err(stmpe->dev, "invalid timeout\n"); + return timeout; + } + + ret = __stmpe_set_bits(stmpe, STMPE1601_REG_SYS_CTRL2, + STMPE1601_AUTOSLEEP_TIMEOUT_MASK, + timeout); + if (ret < 0) + return ret; + + return __stmpe_set_bits(stmpe, STMPE1601_REG_SYS_CTRL2, + STPME1601_AUTOSLEEP_ENABLE, + STPME1601_AUTOSLEEP_ENABLE); +} + +static int stmpe1601_enable(struct stmpe *stmpe, unsigned int blocks, + bool enable) +{ + unsigned int mask = 0; + + if (blocks & STMPE_BLOCK_GPIO) + mask |= STMPE1601_SYS_CTRL_ENABLE_GPIO; + + if (blocks & STMPE_BLOCK_KEYPAD) + mask |= STMPE1601_SYS_CTRL_ENABLE_KPC; + + return __stmpe_set_bits(stmpe, STMPE1601_REG_SYS_CTRL, mask, + enable ? mask : 0); +} + +static int stmpe1601_get_altfunc(struct stmpe *stmpe, enum stmpe_block block) +{ + switch (block) { + case STMPE_BLOCK_PWM: + return 2; + + case STMPE_BLOCK_KEYPAD: + return 1; + + case STMPE_BLOCK_GPIO: + default: + return 0; + } +} + +static struct stmpe_variant_info stmpe1601 = { + .name = "stmpe1601", + .id_val = 0x0210, + .id_mask = 0xfff0, /* at least 0x0210 and 0x0212 */ + .num_gpios = 16, + .af_bits = 2, + .regs = stmpe1601_regs, + .blocks = stmpe1601_blocks, + .num_blocks = ARRAY_SIZE(stmpe1601_blocks), + .num_irqs = STMPE1601_NR_INTERNAL_IRQS, + .enable = stmpe1601_enable, + .get_altfunc = stmpe1601_get_altfunc, + .enable_autosleep = stmpe1601_autosleep, +}; + +/* + * STMPE24XX + */ + +static const u8 stmpe24xx_regs[] = { + [STMPE_IDX_CHIP_ID] = STMPE24XX_REG_CHIP_ID, + [STMPE_IDX_ICR_LSB] = STMPE24XX_REG_ICR_LSB, + [STMPE_IDX_IER_LSB] = STMPE24XX_REG_IER_LSB, + [STMPE_IDX_ISR_MSB] = STMPE24XX_REG_ISR_MSB, + [STMPE_IDX_GPMR_LSB] = STMPE24XX_REG_GPMR_LSB, + [STMPE_IDX_GPSR_LSB] = STMPE24XX_REG_GPSR_LSB, + [STMPE_IDX_GPCR_LSB] = STMPE24XX_REG_GPCR_LSB, + [STMPE_IDX_GPDR_LSB] = STMPE24XX_REG_GPDR_LSB, + [STMPE_IDX_GPRER_LSB] = STMPE24XX_REG_GPRER_LSB, + [STMPE_IDX_GPFER_LSB] = STMPE24XX_REG_GPFER_LSB, + [STMPE_IDX_GPAFR_U_MSB] = STMPE24XX_REG_GPAFR_U_MSB, + [STMPE_IDX_IEGPIOR_LSB] = STMPE24XX_REG_IEGPIOR_LSB, + [STMPE_IDX_ISGPIOR_MSB] = STMPE24XX_REG_ISGPIOR_MSB, + [STMPE_IDX_GPEDR_MSB] = STMPE24XX_REG_GPEDR_MSB, +}; + +static struct stmpe_variant_block stmpe24xx_blocks[] = { + { + .cell = &stmpe_gpio_cell, + .irq = STMPE24XX_IRQ_GPIOC, + .block = STMPE_BLOCK_GPIO, + }, + { + .cell = &stmpe_keypad_cell, + .irq = STMPE24XX_IRQ_KEYPAD, + .block = STMPE_BLOCK_KEYPAD, + }, +}; + +static int stmpe24xx_enable(struct stmpe *stmpe, unsigned int blocks, + bool enable) +{ + unsigned int mask = 0; + + if (blocks & STMPE_BLOCK_GPIO) + mask |= STMPE24XX_SYS_CTRL_ENABLE_GPIO; + + if (blocks & STMPE_BLOCK_KEYPAD) + mask |= STMPE24XX_SYS_CTRL_ENABLE_KPC; + + return __stmpe_set_bits(stmpe, STMPE24XX_REG_SYS_CTRL, mask, + enable ? mask : 0); +} + +static int stmpe24xx_get_altfunc(struct stmpe *stmpe, enum stmpe_block block) +{ + switch (block) { + case STMPE_BLOCK_ROTATOR: + return 2; + + case STMPE_BLOCK_KEYPAD: + return 1; + + case STMPE_BLOCK_GPIO: + default: + return 0; + } +} + +static struct stmpe_variant_info stmpe2401 = { + .name = "stmpe2401", + .id_val = 0x0101, + .id_mask = 0xffff, + .num_gpios = 24, + .af_bits = 2, + .regs = stmpe24xx_regs, + .blocks = stmpe24xx_blocks, + .num_blocks = ARRAY_SIZE(stmpe24xx_blocks), + .num_irqs = STMPE24XX_NR_INTERNAL_IRQS, + .enable = stmpe24xx_enable, + .get_altfunc = stmpe24xx_get_altfunc, +}; + +static struct stmpe_variant_info stmpe2403 = { + .name = "stmpe2403", + .id_val = 0x0120, + .id_mask = 0xffff, + .num_gpios = 24, + .af_bits = 2, + .regs = stmpe24xx_regs, + .blocks = stmpe24xx_blocks, + .num_blocks = ARRAY_SIZE(stmpe24xx_blocks), + .num_irqs = STMPE24XX_NR_INTERNAL_IRQS, + .enable = stmpe24xx_enable, + .get_altfunc = stmpe24xx_get_altfunc, + .enable_autosleep = stmpe1601_autosleep, /* same as stmpe1601 */ +}; + +static struct stmpe_variant_info *stmpe_variant_info[] = { + [STMPE811] = &stmpe811, + [STMPE1601] = &stmpe1601, + [STMPE2401] = &stmpe2401, + [STMPE2403] = &stmpe2403, +}; + +static irqreturn_t stmpe_irq(int irq, void *data) +{ + struct stmpe *stmpe = data; + struct stmpe_variant_info *variant = stmpe->variant; + int num = DIV_ROUND_UP(variant->num_irqs, 8); + u8 israddr = stmpe->regs[STMPE_IDX_ISR_MSB]; + u8 isr[num]; + int ret; + int i; + + ret = stmpe_block_read(stmpe, israddr, num, isr); + if (ret < 0) + return IRQ_NONE; + + for (i = 0; i < num; i++) { + int bank = num - i - 1; + u8 status = isr[i]; + u8 clear; + + status &= stmpe->ier[bank]; + if (!status) + continue; + + clear = status; + while (status) { + int bit = __ffs(status); + int line = bank * 8 + bit; + + handle_nested_irq(stmpe->irq_base + line); + status &= ~(1 << bit); + } + + stmpe_reg_write(stmpe, israddr + i, clear); + } + + return IRQ_HANDLED; +} + +static void stmpe_irq_lock(unsigned int irq) +{ + struct stmpe *stmpe = get_irq_chip_data(irq); + + mutex_lock(&stmpe->irq_lock); +} + +static void stmpe_irq_sync_unlock(unsigned int irq) +{ + struct stmpe *stmpe = get_irq_chip_data(irq); + struct stmpe_variant_info *variant = stmpe->variant; + int num = DIV_ROUND_UP(variant->num_irqs, 8); + int i; + + for (i = 0; i < num; i++) { + u8 new = stmpe->ier[i]; + u8 old = stmpe->oldier[i]; + + if (new == old) + continue; + + stmpe->oldier[i] = new; + stmpe_reg_write(stmpe, stmpe->regs[STMPE_IDX_IER_LSB] - i, new); + } + + mutex_unlock(&stmpe->irq_lock); +} + +static void stmpe_irq_mask(unsigned int irq) +{ + struct stmpe *stmpe = get_irq_chip_data(irq); + int offset = irq - stmpe->irq_base; + int regoffset = offset / 8; + int mask = 1 << (offset % 8); + + stmpe->ier[regoffset] &= ~mask; +} + +static void stmpe_irq_unmask(unsigned int irq) +{ + struct stmpe *stmpe = get_irq_chip_data(irq); + int offset = irq - stmpe->irq_base; + int regoffset = offset / 8; + int mask = 1 << (offset % 8); + + stmpe->ier[regoffset] |= mask; +} + +static struct irq_chip stmpe_irq_chip = { + .name = "stmpe", + .bus_lock = stmpe_irq_lock, + .bus_sync_unlock = stmpe_irq_sync_unlock, + .mask = stmpe_irq_mask, + .unmask = stmpe_irq_unmask, +}; + +static int __devinit stmpe_irq_init(struct stmpe *stmpe) +{ + int num_irqs = stmpe->variant->num_irqs; + int base = stmpe->irq_base; + int irq; + + for (irq = base; irq < base + num_irqs; irq++) { + set_irq_chip_data(irq, stmpe); + set_irq_chip_and_handler(irq, &stmpe_irq_chip, + handle_edge_irq); + set_irq_nested_thread(irq, 1); +#ifdef CONFIG_ARM + set_irq_flags(irq, IRQF_VALID); +#else + set_irq_noprobe(irq); +#endif + } + + return 0; +} + +static void stmpe_irq_remove(struct stmpe *stmpe) +{ + int num_irqs = stmpe->variant->num_irqs; + int base = stmpe->irq_base; + int irq; + + for (irq = base; irq < base + num_irqs; irq++) { +#ifdef CONFIG_ARM + set_irq_flags(irq, 0); +#endif + set_irq_chip_and_handler(irq, NULL, NULL); + set_irq_chip_data(irq, NULL); + } +} + +static int __devinit stmpe_chip_init(struct stmpe *stmpe) +{ + unsigned int irq_trigger = stmpe->pdata->irq_trigger; + int autosleep_timeout = stmpe->pdata->autosleep_timeout; + struct stmpe_variant_info *variant = stmpe->variant; + u8 icr = STMPE_ICR_LSB_GIM; + unsigned int id; + u8 data[2]; + int ret; + + ret = stmpe_block_read(stmpe, stmpe->regs[STMPE_IDX_CHIP_ID], + ARRAY_SIZE(data), data); + if (ret < 0) + return ret; + + id = (data[0] << 8) | data[1]; + if ((id & variant->id_mask) != variant->id_val) { + dev_err(stmpe->dev, "unknown chip id: %#x\n", id); + return -EINVAL; + } + + dev_info(stmpe->dev, "%s detected, chip id: %#x\n", variant->name, id); + + /* Disable all modules -- subdrivers should enable what they need. */ + ret = stmpe_disable(stmpe, ~0); + if (ret) + return ret; + + if (irq_trigger == IRQF_TRIGGER_FALLING || + irq_trigger == IRQF_TRIGGER_RISING) + icr |= STMPE_ICR_LSB_EDGE; + + if (irq_trigger == IRQF_TRIGGER_RISING || + irq_trigger == IRQF_TRIGGER_HIGH) + icr |= STMPE_ICR_LSB_HIGH; + + if (stmpe->pdata->irq_invert_polarity) + icr ^= STMPE_ICR_LSB_HIGH; + + if (stmpe->pdata->autosleep) { + ret = stmpe_autosleep(stmpe, autosleep_timeout); + if (ret) + return ret; + } + + return stmpe_reg_write(stmpe, stmpe->regs[STMPE_IDX_ICR_LSB], icr); +} + +static int __devinit stmpe_add_device(struct stmpe *stmpe, + struct mfd_cell *cell, int irq) +{ + return mfd_add_devices(stmpe->dev, stmpe->pdata->id, cell, 1, + NULL, stmpe->irq_base + irq); +} + +static int __devinit stmpe_devices_init(struct stmpe *stmpe) +{ + struct stmpe_variant_info *variant = stmpe->variant; + unsigned int platform_blocks = stmpe->pdata->blocks; + int ret = -EINVAL; + int i; + + for (i = 0; i < variant->num_blocks; i++) { + struct stmpe_variant_block *block = &variant->blocks[i]; + + if (!(platform_blocks & block->block)) + continue; + + platform_blocks &= ~block->block; + ret = stmpe_add_device(stmpe, block->cell, block->irq); + if (ret) + return ret; + } + + if (platform_blocks) + dev_warn(stmpe->dev, + "platform wants blocks (%#x) not present on variant", + platform_blocks); + + return ret; +} + +static int __devinit stmpe_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct stmpe_platform_data *pdata = i2c->dev.platform_data; + struct stmpe *stmpe; + int ret; + + if (!pdata) + return -EINVAL; + + stmpe = kzalloc(sizeof(struct stmpe), GFP_KERNEL); + if (!stmpe) + return -ENOMEM; + + mutex_init(&stmpe->irq_lock); + mutex_init(&stmpe->lock); + + stmpe->dev = &i2c->dev; + stmpe->i2c = i2c; + + stmpe->pdata = pdata; + stmpe->irq_base = pdata->irq_base; + + stmpe->partnum = id->driver_data; + stmpe->variant = stmpe_variant_info[stmpe->partnum]; + stmpe->regs = stmpe->variant->regs; + stmpe->num_gpios = stmpe->variant->num_gpios; + + i2c_set_clientdata(i2c, stmpe); + + ret = stmpe_chip_init(stmpe); + if (ret) + goto out_free; + + ret = stmpe_irq_init(stmpe); + if (ret) + goto out_free; + + ret = request_threaded_irq(stmpe->i2c->irq, NULL, stmpe_irq, + pdata->irq_trigger | IRQF_ONESHOT, + "stmpe", stmpe); + if (ret) { + dev_err(stmpe->dev, "failed to request IRQ: %d\n", ret); + goto out_removeirq; + } + + ret = stmpe_devices_init(stmpe); + if (ret) { + dev_err(stmpe->dev, "failed to add children\n"); + goto out_removedevs; + } + + return 0; + +out_removedevs: + mfd_remove_devices(stmpe->dev); + free_irq(stmpe->i2c->irq, stmpe); +out_removeirq: + stmpe_irq_remove(stmpe); +out_free: + kfree(stmpe); + return ret; +} + +static int __devexit stmpe_remove(struct i2c_client *client) +{ + struct stmpe *stmpe = i2c_get_clientdata(client); + + mfd_remove_devices(stmpe->dev); + + free_irq(stmpe->i2c->irq, stmpe); + stmpe_irq_remove(stmpe); + + kfree(stmpe); + + return 0; +} + +static const struct i2c_device_id stmpe_id[] = { + { "stmpe811", STMPE811 }, + { "stmpe1601", STMPE1601 }, + { "stmpe2401", STMPE2401 }, + { "stmpe2403", STMPE2403 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, stmpe_id); + +static struct i2c_driver stmpe_driver = { + .driver.name = "stmpe", + .driver.owner = THIS_MODULE, + .probe = stmpe_probe, + .remove = __devexit_p(stmpe_remove), + .id_table = stmpe_id, +}; + +static int __init stmpe_init(void) +{ + return i2c_add_driver(&stmpe_driver); +} +subsys_initcall(stmpe_init); + +static void __exit stmpe_exit(void) +{ + i2c_del_driver(&stmpe_driver); +} +module_exit(stmpe_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("STMPE MFD core driver"); +MODULE_AUTHOR("Rabin Vincent <rabin.vincent@stericsson.com>"); diff --git a/drivers/mfd/stmpe.h b/drivers/mfd/stmpe.h new file mode 100644 index 00000000000..0dbdc4e8cd7 --- /dev/null +++ b/drivers/mfd/stmpe.h @@ -0,0 +1,183 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * License Terms: GNU General Public License, version 2 + * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson + */ + +#ifndef __STMPE_H +#define __STMPE_H + +#ifdef STMPE_DUMP_BYTES +static inline void stmpe_dump_bytes(const char *str, const void *buf, + size_t len) +{ + print_hex_dump_bytes(str, DUMP_PREFIX_OFFSET, buf, len); +} +#else +static inline void stmpe_dump_bytes(const char *str, const void *buf, + size_t len) +{ +} +#endif + +/** + * struct stmpe_variant_block - information about block + * @cell: base mfd cell + * @irq: interrupt number to be added to each IORESOURCE_IRQ + * in the cell + * @block: block id; used for identification with platform data and for + * enable and altfunc callbacks + */ +struct stmpe_variant_block { + struct mfd_cell *cell; + int irq; + enum stmpe_block block; +}; + +/** + * struct stmpe_variant_info - variant-specific information + * @name: part name + * @id_val: content of CHIPID register + * @id_mask: bits valid in CHIPID register for comparison with id_val + * @num_gpios: number of GPIOS + * @af_bits: number of bits used to specify the alternate function + * @blocks: list of blocks present on this device + * @num_blocks: number of blocks present on this device + * @num_irqs: number of internal IRQs available on this device + * @enable: callback to enable the specified blocks. + * Called with the I/O lock held. + * @get_altfunc: callback to get the alternate function number for the + * specific block + * @enable_autosleep: callback to configure autosleep with specified timeout + */ +struct stmpe_variant_info { + const char *name; + u16 id_val; + u16 id_mask; + int num_gpios; + int af_bits; + const u8 *regs; + struct stmpe_variant_block *blocks; + int num_blocks; + int num_irqs; + int (*enable)(struct stmpe *stmpe, unsigned int blocks, bool enable); + int (*get_altfunc)(struct stmpe *stmpe, enum stmpe_block block); + int (*enable_autosleep)(struct stmpe *stmpe, int autosleep_timeout); +}; + +#define STMPE_ICR_LSB_HIGH (1 << 2) +#define STMPE_ICR_LSB_EDGE (1 << 1) +#define STMPE_ICR_LSB_GIM (1 << 0) + +/* + * STMPE811 + */ + +#define STMPE811_IRQ_TOUCH_DET 0 +#define STMPE811_IRQ_FIFO_TH 1 +#define STMPE811_IRQ_FIFO_OFLOW 2 +#define STMPE811_IRQ_FIFO_FULL 3 +#define STMPE811_IRQ_FIFO_EMPTY 4 +#define STMPE811_IRQ_TEMP_SENS 5 +#define STMPE811_IRQ_ADC 6 +#define STMPE811_IRQ_GPIOC 7 +#define STMPE811_NR_INTERNAL_IRQS 8 + +#define STMPE811_REG_CHIP_ID 0x00 +#define STMPE811_REG_SYS_CTRL2 0x04 +#define STMPE811_REG_INT_CTRL 0x09 +#define STMPE811_REG_INT_EN 0x0A +#define STMPE811_REG_INT_STA 0x0B +#define STMPE811_REG_GPIO_INT_EN 0x0C +#define STMPE811_REG_GPIO_INT_STA 0x0D +#define STMPE811_REG_GPIO_SET_PIN 0x10 +#define STMPE811_REG_GPIO_CLR_PIN 0x11 +#define STMPE811_REG_GPIO_MP_STA 0x12 +#define STMPE811_REG_GPIO_DIR 0x13 +#define STMPE811_REG_GPIO_ED 0x14 +#define STMPE811_REG_GPIO_RE 0x15 +#define STMPE811_REG_GPIO_FE 0x16 +#define STMPE811_REG_GPIO_AF 0x17 + +#define STMPE811_SYS_CTRL2_ADC_OFF (1 << 0) +#define STMPE811_SYS_CTRL2_TSC_OFF (1 << 1) +#define STMPE811_SYS_CTRL2_GPIO_OFF (1 << 2) +#define STMPE811_SYS_CTRL2_TS_OFF (1 << 3) + +/* + * STMPE1601 + */ + +#define STMPE1601_IRQ_GPIOC 8 +#define STMPE1601_IRQ_PWM3 7 +#define STMPE1601_IRQ_PWM2 6 +#define STMPE1601_IRQ_PWM1 5 +#define STMPE1601_IRQ_PWM0 4 +#define STMPE1601_IRQ_KEYPAD_OVER 2 +#define STMPE1601_IRQ_KEYPAD 1 +#define STMPE1601_IRQ_WAKEUP 0 +#define STMPE1601_NR_INTERNAL_IRQS 9 + +#define STMPE1601_REG_SYS_CTRL 0x02 +#define STMPE1601_REG_SYS_CTRL2 0x03 +#define STMPE1601_REG_ICR_LSB 0x11 +#define STMPE1601_REG_IER_LSB 0x13 +#define STMPE1601_REG_ISR_MSB 0x14 +#define STMPE1601_REG_CHIP_ID 0x80 +#define STMPE1601_REG_INT_EN_GPIO_MASK_LSB 0x17 +#define STMPE1601_REG_INT_STA_GPIO_MSB 0x18 +#define STMPE1601_REG_GPIO_MP_LSB 0x87 +#define STMPE1601_REG_GPIO_SET_LSB 0x83 +#define STMPE1601_REG_GPIO_CLR_LSB 0x85 +#define STMPE1601_REG_GPIO_SET_DIR_LSB 0x89 +#define STMPE1601_REG_GPIO_ED_MSB 0x8A +#define STMPE1601_REG_GPIO_RE_LSB 0x8D +#define STMPE1601_REG_GPIO_FE_LSB 0x8F +#define STMPE1601_REG_GPIO_AF_U_MSB 0x92 + +#define STMPE1601_SYS_CTRL_ENABLE_GPIO (1 << 3) +#define STMPE1601_SYS_CTRL_ENABLE_KPC (1 << 1) +#define STMPE1601_SYSCON_ENABLE_SPWM (1 << 0) + +/* The 1601/2403 share the same masks */ +#define STMPE1601_AUTOSLEEP_TIMEOUT_MASK (0x7) +#define STPME1601_AUTOSLEEP_ENABLE (1 << 3) + +/* + * STMPE24xx + */ + +#define STMPE24XX_IRQ_GPIOC 8 +#define STMPE24XX_IRQ_PWM2 7 +#define STMPE24XX_IRQ_PWM1 6 +#define STMPE24XX_IRQ_PWM0 5 +#define STMPE24XX_IRQ_ROT_OVER 4 +#define STMPE24XX_IRQ_ROT 3 +#define STMPE24XX_IRQ_KEYPAD_OVER 2 +#define STMPE24XX_IRQ_KEYPAD 1 +#define STMPE24XX_IRQ_WAKEUP 0 +#define STMPE24XX_NR_INTERNAL_IRQS 9 + +#define STMPE24XX_REG_SYS_CTRL 0x02 +#define STMPE24XX_REG_ICR_LSB 0x11 +#define STMPE24XX_REG_IER_LSB 0x13 +#define STMPE24XX_REG_ISR_MSB 0x14 +#define STMPE24XX_REG_CHIP_ID 0x80 +#define STMPE24XX_REG_IEGPIOR_LSB 0x18 +#define STMPE24XX_REG_ISGPIOR_MSB 0x19 +#define STMPE24XX_REG_GPMR_LSB 0xA5 +#define STMPE24XX_REG_GPSR_LSB 0x85 +#define STMPE24XX_REG_GPCR_LSB 0x88 +#define STMPE24XX_REG_GPDR_LSB 0x8B +#define STMPE24XX_REG_GPEDR_MSB 0x8C +#define STMPE24XX_REG_GPRER_LSB 0x91 +#define STMPE24XX_REG_GPFER_LSB 0x94 +#define STMPE24XX_REG_GPAFR_U_MSB 0x9B + +#define STMPE24XX_SYS_CTRL_ENABLE_GPIO (1 << 3) +#define STMPE24XX_SYSCON_ENABLE_PWM (1 << 2) +#define STMPE24XX_SYS_CTRL_ENABLE_KPC (1 << 1) +#define STMPE24XX_SYSCON_ENABLE_ROT (1 << 0) + +#endif diff --git a/drivers/mfd/t7l66xb.c b/drivers/mfd/t7l66xb.c index 5041d33adf0..006c121f3f0 100644 --- a/drivers/mfd/t7l66xb.c +++ b/drivers/mfd/t7l66xb.c @@ -350,7 +350,6 @@ static int t7l66xb_probe(struct platform_device *dev) t7l66xb->clk48m = clk_get(&dev->dev, "CLK_CK48M"); if (IS_ERR(t7l66xb->clk48m)) { ret = PTR_ERR(t7l66xb->clk48m); - clk_put(t7l66xb->clk32k); goto err_clk48m_get; } @@ -425,6 +424,8 @@ static int t7l66xb_remove(struct platform_device *dev) ret = pdata->disable(dev); clk_disable(t7l66xb->clk48m); clk_put(t7l66xb->clk48m); + clk_disable(t7l66xb->clk32k); + clk_put(t7l66xb->clk32k); t7l66xb_detach_irq(dev); iounmap(t7l66xb->scr); release_resource(&t7l66xb->rscr); diff --git a/drivers/mfd/tc6387xb.c b/drivers/mfd/tc6387xb.c index 517f9bcdeaa..6315f63f017 100644 --- a/drivers/mfd/tc6387xb.c +++ b/drivers/mfd/tc6387xb.c @@ -137,7 +137,7 @@ static struct mfd_cell tc6387xb_cells[] = { }, }; -static int tc6387xb_probe(struct platform_device *dev) +static int __devinit tc6387xb_probe(struct platform_device *dev) { struct tc6387xb_platform_data *pdata = dev->dev.platform_data; struct resource *iomem, *rscr; @@ -201,6 +201,7 @@ static int tc6387xb_probe(struct platform_device *dev) if (!ret) return 0; + iounmap(tc6387xb->scr); err_ioremap: release_resource(&tc6387xb->rscr); err_resource: @@ -211,14 +212,17 @@ err_no_irq: return ret; } -static int tc6387xb_remove(struct platform_device *dev) +static int __devexit tc6387xb_remove(struct platform_device *dev) { - struct clk *clk32k = platform_get_drvdata(dev); + struct tc6387xb *tc6387xb = platform_get_drvdata(dev); mfd_remove_devices(&dev->dev); - clk_disable(clk32k); - clk_put(clk32k); + iounmap(tc6387xb->scr); + release_resource(&tc6387xb->rscr); + clk_disable(tc6387xb->clk32k); + clk_put(tc6387xb->clk32k); platform_set_drvdata(dev, NULL); + kfree(tc6387xb); return 0; } @@ -229,7 +233,7 @@ static struct platform_driver tc6387xb_platform_driver = { .name = "tc6387xb", }, .probe = tc6387xb_probe, - .remove = tc6387xb_remove, + .remove = __devexit_p(tc6387xb_remove), .suspend = tc6387xb_suspend, .resume = tc6387xb_resume, }; diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c index fcf9068810f..ef6c42c8917 100644 --- a/drivers/mfd/tc6393xb.c +++ b/drivers/mfd/tc6393xb.c @@ -732,9 +732,9 @@ err_gpio_add: if (tc6393xb->gpio.base != -1) temp = gpiochip_remove(&tc6393xb->gpio); tcpd->disable(dev); -err_clk_enable: - clk_disable(tc6393xb->clk); err_enable: + clk_disable(tc6393xb->clk); +err_clk_enable: iounmap(tc6393xb->scr); err_ioremap: release_resource(&tc6393xb->rscr); diff --git a/drivers/mfd/tps6507x.c b/drivers/mfd/tps6507x.c index d859dffed39..fc019764928 100644 --- a/drivers/mfd/tps6507x.c +++ b/drivers/mfd/tps6507x.c @@ -89,10 +89,8 @@ static int tps6507x_i2c_probe(struct i2c_client *i2c, int ret = 0; tps6507x = kzalloc(sizeof(struct tps6507x_dev), GFP_KERNEL); - if (tps6507x == NULL) { - kfree(i2c); + if (tps6507x == NULL) return -ENOMEM; - } i2c_set_clientdata(i2c, tps6507x); tps6507x->dev = &i2c->dev; diff --git a/drivers/mfd/tps6586x.c b/drivers/mfd/tps6586x.c new file mode 100644 index 00000000000..4cde31e6a25 --- /dev/null +++ b/drivers/mfd/tps6586x.c @@ -0,0 +1,375 @@ +/* + * Core driver for TI TPS6586x PMIC family + * + * Copyright (c) 2010 CompuLab Ltd. + * Mike Rapoport <mike@compulab.co.il> + * + * Based on da903x.c. + * Copyright (C) 2008 Compulab, Ltd. + * Mike Rapoport <mike@compulab.co.il> + * Copyright (C) 2006-2008 Marvell International Ltd. + * Eric Miao <eric.miao@marvell.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/gpio.h> +#include <linux/i2c.h> + +#include <linux/mfd/core.h> +#include <linux/mfd/tps6586x.h> + +/* GPIO control registers */ +#define TPS6586X_GPIOSET1 0x5d +#define TPS6586X_GPIOSET2 0x5e + +/* device id */ +#define TPS6586X_VERSIONCRC 0xcd +#define TPS658621A_VERSIONCRC 0x15 + +struct tps6586x { + struct mutex lock; + struct device *dev; + struct i2c_client *client; + + struct gpio_chip gpio; +}; + +static inline int __tps6586x_read(struct i2c_client *client, + int reg, uint8_t *val) +{ + int ret; + + ret = i2c_smbus_read_byte_data(client, reg); + if (ret < 0) { + dev_err(&client->dev, "failed reading at 0x%02x\n", reg); + return ret; + } + + *val = (uint8_t)ret; + + return 0; +} + +static inline int __tps6586x_reads(struct i2c_client *client, int reg, + int len, uint8_t *val) +{ + int ret; + + ret = i2c_smbus_read_i2c_block_data(client, reg, len, val); + if (ret < 0) { + dev_err(&client->dev, "failed reading from 0x%02x\n", reg); + return ret; + } + + return 0; +} + +static inline int __tps6586x_write(struct i2c_client *client, + int reg, uint8_t val) +{ + int ret; + + ret = i2c_smbus_write_byte_data(client, reg, val); + if (ret < 0) { + dev_err(&client->dev, "failed writing 0x%02x to 0x%02x\n", + val, reg); + return ret; + } + + return 0; +} + +static inline int __tps6586x_writes(struct i2c_client *client, int reg, + int len, uint8_t *val) +{ + int ret; + + ret = i2c_smbus_write_i2c_block_data(client, reg, len, val); + if (ret < 0) { + dev_err(&client->dev, "failed writings to 0x%02x\n", reg); + return ret; + } + + return 0; +} + +int tps6586x_write(struct device *dev, int reg, uint8_t val) +{ + return __tps6586x_write(to_i2c_client(dev), reg, val); +} +EXPORT_SYMBOL_GPL(tps6586x_write); + +int tps6586x_writes(struct device *dev, int reg, int len, uint8_t *val) +{ + return __tps6586x_writes(to_i2c_client(dev), reg, len, val); +} +EXPORT_SYMBOL_GPL(tps6586x_writes); + +int tps6586x_read(struct device *dev, int reg, uint8_t *val) +{ + return __tps6586x_read(to_i2c_client(dev), reg, val); +} +EXPORT_SYMBOL_GPL(tps6586x_read); + +int tps6586x_reads(struct device *dev, int reg, int len, uint8_t *val) +{ + return __tps6586x_reads(to_i2c_client(dev), reg, len, val); +} +EXPORT_SYMBOL_GPL(tps6586x_reads); + +int tps6586x_set_bits(struct device *dev, int reg, uint8_t bit_mask) +{ + struct tps6586x *tps6586x = dev_get_drvdata(dev); + uint8_t reg_val; + int ret = 0; + + mutex_lock(&tps6586x->lock); + + ret = __tps6586x_read(to_i2c_client(dev), reg, ®_val); + if (ret) + goto out; + + if ((reg_val & bit_mask) == 0) { + reg_val |= bit_mask; + ret = __tps6586x_write(to_i2c_client(dev), reg, reg_val); + } +out: + mutex_unlock(&tps6586x->lock); + return ret; +} +EXPORT_SYMBOL_GPL(tps6586x_set_bits); + +int tps6586x_clr_bits(struct device *dev, int reg, uint8_t bit_mask) +{ + struct tps6586x *tps6586x = dev_get_drvdata(dev); + uint8_t reg_val; + int ret = 0; + + mutex_lock(&tps6586x->lock); + + ret = __tps6586x_read(to_i2c_client(dev), reg, ®_val); + if (ret) + goto out; + + if (reg_val & bit_mask) { + reg_val &= ~bit_mask; + ret = __tps6586x_write(to_i2c_client(dev), reg, reg_val); + } +out: + mutex_unlock(&tps6586x->lock); + return ret; +} +EXPORT_SYMBOL_GPL(tps6586x_clr_bits); + +int tps6586x_update(struct device *dev, int reg, uint8_t val, uint8_t mask) +{ + struct tps6586x *tps6586x = dev_get_drvdata(dev); + uint8_t reg_val; + int ret = 0; + + mutex_lock(&tps6586x->lock); + + ret = __tps6586x_read(tps6586x->client, reg, ®_val); + if (ret) + goto out; + + if ((reg_val & mask) != val) { + reg_val = (reg_val & ~mask) | val; + ret = __tps6586x_write(tps6586x->client, reg, reg_val); + } +out: + mutex_unlock(&tps6586x->lock); + return ret; +} +EXPORT_SYMBOL_GPL(tps6586x_update); + +static int tps6586x_gpio_get(struct gpio_chip *gc, unsigned offset) +{ + struct tps6586x *tps6586x = container_of(gc, struct tps6586x, gpio); + uint8_t val; + int ret; + + ret = __tps6586x_read(tps6586x->client, TPS6586X_GPIOSET2, &val); + if (ret) + return ret; + + return !!(val & (1 << offset)); +} + + +static void tps6586x_gpio_set(struct gpio_chip *chip, unsigned offset, + int value) +{ + struct tps6586x *tps6586x = container_of(chip, struct tps6586x, gpio); + + __tps6586x_write(tps6586x->client, TPS6586X_GPIOSET2, + value << offset); +} + +static int tps6586x_gpio_output(struct gpio_chip *gc, unsigned offset, + int value) +{ + struct tps6586x *tps6586x = container_of(gc, struct tps6586x, gpio); + uint8_t val, mask; + + tps6586x_gpio_set(gc, offset, value); + + val = 0x1 << (offset * 2); + mask = 0x3 << (offset * 2); + + return tps6586x_update(tps6586x->dev, TPS6586X_GPIOSET1, val, mask); +} + +static void tps6586x_gpio_init(struct tps6586x *tps6586x, int gpio_base) +{ + int ret; + + if (!gpio_base) + return; + + tps6586x->gpio.owner = THIS_MODULE; + tps6586x->gpio.label = tps6586x->client->name; + tps6586x->gpio.dev = tps6586x->dev; + tps6586x->gpio.base = gpio_base; + tps6586x->gpio.ngpio = 4; + tps6586x->gpio.can_sleep = 1; + + /* FIXME: add handling of GPIOs as dedicated inputs */ + tps6586x->gpio.direction_output = tps6586x_gpio_output; + tps6586x->gpio.set = tps6586x_gpio_set; + tps6586x->gpio.get = tps6586x_gpio_get; + + ret = gpiochip_add(&tps6586x->gpio); + if (ret) + dev_warn(tps6586x->dev, "GPIO registration failed: %d\n", ret); +} + +static int __remove_subdev(struct device *dev, void *unused) +{ + platform_device_unregister(to_platform_device(dev)); + return 0; +} + +static int tps6586x_remove_subdevs(struct tps6586x *tps6586x) +{ + return device_for_each_child(tps6586x->dev, NULL, __remove_subdev); +} + +static int __devinit tps6586x_add_subdevs(struct tps6586x *tps6586x, + struct tps6586x_platform_data *pdata) +{ + struct tps6586x_subdev_info *subdev; + struct platform_device *pdev; + int i, ret = 0; + + for (i = 0; i < pdata->num_subdevs; i++) { + subdev = &pdata->subdevs[i]; + + pdev = platform_device_alloc(subdev->name, subdev->id); + + pdev->dev.parent = tps6586x->dev; + pdev->dev.platform_data = subdev->platform_data; + + ret = platform_device_add(pdev); + if (ret) + goto failed; + } + return 0; + +failed: + tps6586x_remove_subdevs(tps6586x); + return ret; +} + +static int __devinit tps6586x_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tps6586x_platform_data *pdata = client->dev.platform_data; + struct tps6586x *tps6586x; + int ret; + + if (!pdata) { + dev_err(&client->dev, "tps6586x requires platform data\n"); + return -ENOTSUPP; + } + + ret = i2c_smbus_read_byte_data(client, TPS6586X_VERSIONCRC); + if (ret < 0) { + dev_err(&client->dev, "Chip ID read failed: %d\n", ret); + return -EIO; + } + + if (ret != TPS658621A_VERSIONCRC) { + dev_err(&client->dev, "Unsupported chip ID: %x\n", ret); + return -ENODEV; + } + + tps6586x = kzalloc(sizeof(struct tps6586x), GFP_KERNEL); + if (tps6586x == NULL) + return -ENOMEM; + + tps6586x->client = client; + tps6586x->dev = &client->dev; + i2c_set_clientdata(client, tps6586x); + + mutex_init(&tps6586x->lock); + + ret = tps6586x_add_subdevs(tps6586x, pdata); + if (ret) { + dev_err(&client->dev, "add devices failed: %d\n", ret); + goto err_add_devs; + } + + tps6586x_gpio_init(tps6586x, pdata->gpio_base); + + return 0; + +err_add_devs: + kfree(tps6586x); + return ret; +} + +static int __devexit tps6586x_i2c_remove(struct i2c_client *client) +{ + return 0; +} + +static const struct i2c_device_id tps6586x_id_table[] = { + { "tps6586x", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, tps6586x_id_table); + +static struct i2c_driver tps6586x_driver = { + .driver = { + .name = "tps6586x", + .owner = THIS_MODULE, + }, + .probe = tps6586x_i2c_probe, + .remove = __devexit_p(tps6586x_i2c_remove), + .id_table = tps6586x_id_table, +}; + +static int __init tps6586x_init(void) +{ + return i2c_add_driver(&tps6586x_driver); +} +subsys_initcall(tps6586x_init); + +static void __exit tps6586x_exit(void) +{ + i2c_del_driver(&tps6586x_driver); +} +module_exit(tps6586x_exit); + +MODULE_DESCRIPTION("TPS6586X core driver"); +MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/mfd/twl6030-pwm.c b/drivers/mfd/twl6030-pwm.c new file mode 100644 index 00000000000..5d25bdc7842 --- /dev/null +++ b/drivers/mfd/twl6030-pwm.c @@ -0,0 +1,163 @@ +/* + * twl6030_pwm.c + * Driver for PHOENIX (TWL6030) Pulse Width Modulator + * + * Copyright (C) 2010 Texas Instruments + * Author: Hemanth V <hemanthv@ti.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. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/i2c/twl.h> +#include <linux/slab.h> + +#define LED_PWM_CTRL1 0xF4 +#define LED_PWM_CTRL2 0xF5 + +/* Max value for CTRL1 register */ +#define PWM_CTRL1_MAX 255 + +/* Pull down disable */ +#define PWM_CTRL2_DIS_PD (1 << 6) + +/* Current control 2.5 milli Amps */ +#define PWM_CTRL2_CURR_02 (2 << 4) + +/* LED supply source */ +#define PWM_CTRL2_SRC_VAC (1 << 2) + +/* LED modes */ +#define PWM_CTRL2_MODE_HW (0 << 0) +#define PWM_CTRL2_MODE_SW (1 << 0) +#define PWM_CTRL2_MODE_DIS (2 << 0) + +#define PWM_CTRL2_MODE_MASK 0x3 + +struct pwm_device { + const char *label; + unsigned int pwm_id; +}; + +int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) +{ + u8 duty_cycle; + int ret; + + if (pwm == NULL || period_ns == 0 || duty_ns > period_ns) + return -EINVAL; + + duty_cycle = (duty_ns * PWM_CTRL1_MAX) / period_ns; + + ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, duty_cycle, LED_PWM_CTRL1); + + if (ret < 0) { + pr_err("%s: Failed to configure PWM, Error %d\n", + pwm->label, ret); + return ret; + } + return 0; +} +EXPORT_SYMBOL(pwm_config); + +int pwm_enable(struct pwm_device *pwm) +{ + u8 val; + int ret; + + ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, LED_PWM_CTRL2); + if (ret < 0) { + pr_err("%s: Failed to enable PWM, Error %d\n", pwm->label, ret); + return ret; + } + + /* Change mode to software control */ + val &= ~PWM_CTRL2_MODE_MASK; + val |= PWM_CTRL2_MODE_SW; + + ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, LED_PWM_CTRL2); + if (ret < 0) { + pr_err("%s: Failed to enable PWM, Error %d\n", pwm->label, ret); + return ret; + } + + twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, LED_PWM_CTRL2); + return 0; +} +EXPORT_SYMBOL(pwm_enable); + +void pwm_disable(struct pwm_device *pwm) +{ + u8 val; + int ret; + + ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, LED_PWM_CTRL2); + if (ret < 0) { + pr_err("%s: Failed to disable PWM, Error %d\n", + pwm->label, ret); + return; + } + + val &= ~PWM_CTRL2_MODE_MASK; + val |= PWM_CTRL2_MODE_HW; + + ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, LED_PWM_CTRL2); + if (ret < 0) { + pr_err("%s: Failed to disable PWM, Error %d\n", + pwm->label, ret); + return; + } + return; +} +EXPORT_SYMBOL(pwm_disable); + +struct pwm_device *pwm_request(int pwm_id, const char *label) +{ + u8 val; + int ret; + struct pwm_device *pwm; + + pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL); + if (pwm == NULL) { + pr_err("%s: failed to allocate memory\n", label); + return NULL; + } + + pwm->label = label; + pwm->pwm_id = pwm_id; + + /* Configure PWM */ + val = PWM_CTRL2_DIS_PD | PWM_CTRL2_CURR_02 | PWM_CTRL2_SRC_VAC | + PWM_CTRL2_MODE_HW; + + ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, LED_PWM_CTRL2); + + if (ret < 0) { + pr_err("%s: Failed to configure PWM, Error %d\n", + pwm->label, ret); + + kfree(pwm); + return NULL; + } + + return pwm; +} +EXPORT_SYMBOL(pwm_request); + +void pwm_free(struct pwm_device *pwm) +{ + pwm_disable(pwm); + kfree(pwm); +} +EXPORT_SYMBOL(pwm_free); diff --git a/drivers/mfd/ucb1400_core.c b/drivers/mfd/ucb1400_core.c index dbe280153f9..d73f84ba0f0 100644 --- a/drivers/mfd/ucb1400_core.c +++ b/drivers/mfd/ucb1400_core.c @@ -114,7 +114,7 @@ static int ucb1400_core_probe(struct device *dev) err3: platform_device_put(ucb->ucb1400_ts); err2: - platform_device_unregister(ucb->ucb1400_gpio); + platform_device_del(ucb->ucb1400_gpio); err1: platform_device_put(ucb->ucb1400_gpio); err0: diff --git a/drivers/mfd/wm831x-core.c b/drivers/mfd/wm831x-core.c index 1a968f34d67..1e7aaaf6cc6 100644 --- a/drivers/mfd/wm831x-core.c +++ b/drivers/mfd/wm831x-core.c @@ -95,6 +95,7 @@ enum wm831x_parent { WM8311 = 0x8311, WM8312 = 0x8312, WM8320 = 0x8320, + WM8321 = 0x8321, }; static int wm831x_reg_locked(struct wm831x *wm831x, unsigned short reg) @@ -1533,6 +1534,12 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) dev_info(wm831x->dev, "WM8320 revision %c\n", 'A' + rev); break; + case WM8321: + parent = WM8321; + wm831x->num_gpio = 12; + dev_info(wm831x->dev, "WM8321 revision %c\n", 'A' + rev); + break; + default: dev_err(wm831x->dev, "Unknown WM831x device %04x\n", ret); ret = -EINVAL; @@ -1607,6 +1614,12 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) NULL, 0); break; + case WM8321: + ret = mfd_add_devices(wm831x->dev, -1, + wm8320_devs, ARRAY_SIZE(wm8320_devs), + NULL, 0); + break; + default: /* If this happens the bus probe function is buggy */ BUG(); @@ -1744,10 +1757,8 @@ static int wm831x_i2c_probe(struct i2c_client *i2c, struct wm831x *wm831x; wm831x = kzalloc(sizeof(struct wm831x), GFP_KERNEL); - if (wm831x == NULL) { - kfree(i2c); + if (wm831x == NULL) return -ENOMEM; - } i2c_set_clientdata(i2c, wm831x); wm831x->dev = &i2c->dev; @@ -1779,6 +1790,7 @@ static const struct i2c_device_id wm831x_i2c_id[] = { { "wm8311", WM8311 }, { "wm8312", WM8312 }, { "wm8320", WM8320 }, + { "wm8321", WM8321 }, { } }; MODULE_DEVICE_TABLE(i2c, wm831x_i2c_id); diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c index b5807484b4c..e81cc31e420 100644 --- a/drivers/mfd/wm8350-core.c +++ b/drivers/mfd/wm8350-core.c @@ -536,6 +536,7 @@ static int wm8350_create_cache(struct wm8350 *wm8350, int type, int mode) } out: + kfree(wm8350->reg_cache); return ret; } @@ -700,7 +701,7 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq, ret = wm8350_irq_init(wm8350, irq, pdata); if (ret < 0) - goto err; + goto err_free; if (wm8350->irq_base) { ret = request_threaded_irq(wm8350->irq_base + @@ -738,8 +739,9 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq, err_irq: wm8350_irq_exit(wm8350); -err: +err_free: kfree(wm8350->reg_cache); +err: return ret; } EXPORT_SYMBOL_GPL(wm8350_device_init); diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index ec71c936890..b3b2aaf89db 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -326,8 +326,10 @@ static int wm8994_device_init(struct wm8994 *wm8994, unsigned long id, int irq) wm8994->supplies = kzalloc(sizeof(struct regulator_bulk_data) * ARRAY_SIZE(wm8994_main_supplies), GFP_KERNEL); - if (!wm8994->supplies) + if (!wm8994->supplies) { + ret = -ENOMEM; goto err; + } for (i = 0; i < ARRAY_SIZE(wm8994_main_supplies); i++) wm8994->supplies[i].supply = wm8994_main_supplies[i]; @@ -495,10 +497,8 @@ static int wm8994_i2c_probe(struct i2c_client *i2c, struct wm8994 *wm8994; wm8994 = kzalloc(sizeof(struct wm8994), GFP_KERNEL); - if (wm8994 == NULL) { - kfree(i2c); + if (wm8994 == NULL) return -ENOMEM; - } i2c_set_clientdata(i2c, wm8994); wm8994->dev = &i2c->dev; diff --git a/drivers/misc/cs5535-mfgpt.c b/drivers/misc/cs5535-mfgpt.c index 2d44b330010..6f6218061b0 100644 --- a/drivers/misc/cs5535-mfgpt.c +++ b/drivers/misc/cs5535-mfgpt.c @@ -211,6 +211,17 @@ EXPORT_SYMBOL_GPL(cs5535_mfgpt_alloc_timer); */ void cs5535_mfgpt_free_timer(struct cs5535_mfgpt_timer *timer) { + unsigned long flags; + uint16_t val; + + /* timer can be made available again only if never set up */ + val = cs5535_mfgpt_read(timer, MFGPT_REG_SETUP); + if (!(val & MFGPT_SETUP_SETUP)) { + spin_lock_irqsave(&timer->chip->lock, flags); + __set_bit(timer->nr, timer->chip->avail); + spin_unlock_irqrestore(&timer->chip->lock, flags); + } + kfree(timer); } EXPORT_SYMBOL_GPL(cs5535_mfgpt_free_timer); diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 8433cde29c8..d545f79f600 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -247,7 +247,76 @@ static u32 get_card_status(struct mmc_card *card, struct request *req) return cmd.resp[0]; } -static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) +static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req) +{ + struct mmc_blk_data *md = mq->data; + struct mmc_card *card = md->queue.card; + unsigned int from, nr, arg; + int err = 0; + + mmc_claim_host(card->host); + + if (!mmc_can_erase(card)) { + err = -EOPNOTSUPP; + goto out; + } + + from = blk_rq_pos(req); + nr = blk_rq_sectors(req); + + if (mmc_can_trim(card)) + arg = MMC_TRIM_ARG; + else + arg = MMC_ERASE_ARG; + + err = mmc_erase(card, from, nr, arg); +out: + spin_lock_irq(&md->lock); + __blk_end_request(req, err, blk_rq_bytes(req)); + spin_unlock_irq(&md->lock); + + mmc_release_host(card->host); + + return err ? 0 : 1; +} + +static int mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq, + struct request *req) +{ + struct mmc_blk_data *md = mq->data; + struct mmc_card *card = md->queue.card; + unsigned int from, nr, arg; + int err = 0; + + mmc_claim_host(card->host); + + if (!mmc_can_secure_erase_trim(card)) { + err = -EOPNOTSUPP; + goto out; + } + + from = blk_rq_pos(req); + nr = blk_rq_sectors(req); + + if (mmc_can_trim(card) && !mmc_erase_group_aligned(card, from, nr)) + arg = MMC_SECURE_TRIM1_ARG; + else + arg = MMC_SECURE_ERASE_ARG; + + err = mmc_erase(card, from, nr, arg); + if (!err && arg == MMC_SECURE_TRIM1_ARG) + err = mmc_erase(card, from, nr, MMC_SECURE_TRIM2_ARG); +out: + spin_lock_irq(&md->lock); + __blk_end_request(req, err, blk_rq_bytes(req)); + spin_unlock_irq(&md->lock); + + mmc_release_host(card->host); + + return err ? 0 : 1; +} + +static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req) { struct mmc_blk_data *md = mq->data; struct mmc_card *card = md->queue.card; @@ -475,6 +544,17 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) return 0; } +static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) +{ + if (req->cmd_flags & REQ_DISCARD) { + if (req->cmd_flags & REQ_SECURE) + return mmc_blk_issue_secdiscard_rq(mq, req); + else + return mmc_blk_issue_discard_rq(mq, req); + } else { + return mmc_blk_issue_rw_rq(mq, req); + } +} static inline int mmc_blk_readonly(struct mmc_card *card) { diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c index 445d7db2277..5dd8576b5c1 100644 --- a/drivers/mmc/card/mmc_test.c +++ b/drivers/mmc/card/mmc_test.c @@ -16,6 +16,7 @@ #include <linux/slab.h> #include <linux/scatterlist.h> +#include <linux/swap.h> /* For nr_free_buffer_pages() */ #define RESULT_OK 0 #define RESULT_FAIL 1 @@ -25,6 +26,60 @@ #define BUFFER_ORDER 2 #define BUFFER_SIZE (PAGE_SIZE << BUFFER_ORDER) +/* + * Limit the test area size to the maximum MMC HC erase group size. Note that + * the maximum SD allocation unit size is just 4MiB. + */ +#define TEST_AREA_MAX_SIZE (128 * 1024 * 1024) + +/** + * struct mmc_test_pages - pages allocated by 'alloc_pages()'. + * @page: first page in the allocation + * @order: order of the number of pages allocated + */ +struct mmc_test_pages { + struct page *page; + unsigned int order; +}; + +/** + * struct mmc_test_mem - allocated memory. + * @arr: array of allocations + * @cnt: number of allocations + */ +struct mmc_test_mem { + struct mmc_test_pages *arr; + unsigned int cnt; +}; + +/** + * struct mmc_test_area - information for performance tests. + * @max_sz: test area size (in bytes) + * @dev_addr: address on card at which to do performance tests + * @max_segs: maximum segments in scatterlist @sg + * @blocks: number of (512 byte) blocks currently mapped by @sg + * @sg_len: length of currently mapped scatterlist @sg + * @mem: allocated memory + * @sg: scatterlist + */ +struct mmc_test_area { + unsigned long max_sz; + unsigned int dev_addr; + unsigned int max_segs; + unsigned int blocks; + unsigned int sg_len; + struct mmc_test_mem *mem; + struct scatterlist *sg; +}; + +/** + * struct mmc_test_card - test information. + * @card: card under test + * @scratch: transfer buffer + * @buffer: transfer buffer + * @highmem: buffer for highmem tests + * @area: information for performance tests + */ struct mmc_test_card { struct mmc_card *card; @@ -33,6 +88,7 @@ struct mmc_test_card { #ifdef CONFIG_HIGHMEM struct page *highmem; #endif + struct mmc_test_area area; }; /*******************************************************************/ @@ -97,6 +153,12 @@ static void mmc_test_prepare_mrq(struct mmc_test_card *test, mmc_set_data_timeout(mrq->data, test->card); } +static int mmc_test_busy(struct mmc_command *cmd) +{ + return !(cmd->resp[0] & R1_READY_FOR_DATA) || + (R1_CURRENT_STATE(cmd->resp[0]) == 7); +} + /* * Wait for the card to finish the busy state */ @@ -117,13 +179,13 @@ static int mmc_test_wait_busy(struct mmc_test_card *test) if (ret) break; - if (!busy && !(cmd.resp[0] & R1_READY_FOR_DATA)) { + if (!busy && mmc_test_busy(&cmd)) { busy = 1; printk(KERN_INFO "%s: Warning: Host did not " "wait for busy state to end.\n", mmc_hostname(test->card->host)); } - } while (!(cmd.resp[0] & R1_READY_FOR_DATA)); + } while (mmc_test_busy(&cmd)); return ret; } @@ -170,6 +232,248 @@ static int mmc_test_buffer_transfer(struct mmc_test_card *test, return 0; } +static void mmc_test_free_mem(struct mmc_test_mem *mem) +{ + if (!mem) + return; + while (mem->cnt--) + __free_pages(mem->arr[mem->cnt].page, + mem->arr[mem->cnt].order); + kfree(mem->arr); + kfree(mem); +} + +/* + * Allocate a lot of memory, preferrably max_sz but at least min_sz. In case + * there isn't much memory do not exceed 1/16th total lowmem pages. + */ +static struct mmc_test_mem *mmc_test_alloc_mem(unsigned long min_sz, + unsigned long max_sz) +{ + unsigned long max_page_cnt = DIV_ROUND_UP(max_sz, PAGE_SIZE); + unsigned long min_page_cnt = DIV_ROUND_UP(min_sz, PAGE_SIZE); + unsigned long page_cnt = 0; + unsigned long limit = nr_free_buffer_pages() >> 4; + struct mmc_test_mem *mem; + + if (max_page_cnt > limit) + max_page_cnt = limit; + if (max_page_cnt < min_page_cnt) + max_page_cnt = min_page_cnt; + + mem = kzalloc(sizeof(struct mmc_test_mem), GFP_KERNEL); + if (!mem) + return NULL; + + mem->arr = kzalloc(sizeof(struct mmc_test_pages) * max_page_cnt, + GFP_KERNEL); + if (!mem->arr) + goto out_free; + + while (max_page_cnt) { + struct page *page; + unsigned int order; + gfp_t flags = GFP_KERNEL | GFP_DMA | __GFP_NOWARN | + __GFP_NORETRY; + + order = get_order(max_page_cnt << PAGE_SHIFT); + while (1) { + page = alloc_pages(flags, order); + if (page || !order) + break; + order -= 1; + } + if (!page) { + if (page_cnt < min_page_cnt) + goto out_free; + break; + } + mem->arr[mem->cnt].page = page; + mem->arr[mem->cnt].order = order; + mem->cnt += 1; + if (max_page_cnt <= (1UL << order)) + break; + max_page_cnt -= 1UL << order; + page_cnt += 1UL << order; + } + + return mem; + +out_free: + mmc_test_free_mem(mem); + return NULL; +} + +/* + * Map memory into a scatterlist. Optionally allow the same memory to be + * mapped more than once. + */ +static int mmc_test_map_sg(struct mmc_test_mem *mem, unsigned long sz, + struct scatterlist *sglist, int repeat, + unsigned int max_segs, unsigned int *sg_len) +{ + struct scatterlist *sg = NULL; + unsigned int i; + + sg_init_table(sglist, max_segs); + + *sg_len = 0; + do { + for (i = 0; i < mem->cnt; i++) { + unsigned long len = PAGE_SIZE << mem->arr[i].order; + + if (sz < len) + len = sz; + if (sg) + sg = sg_next(sg); + else + sg = sglist; + if (!sg) + return -EINVAL; + sg_set_page(sg, mem->arr[i].page, len, 0); + sz -= len; + *sg_len += 1; + if (!sz) + break; + } + } while (sz && repeat); + + if (sz) + return -EINVAL; + + if (sg) + sg_mark_end(sg); + + return 0; +} + +/* + * Map memory into a scatterlist so that no pages are contiguous. Allow the + * same memory to be mapped more than once. + */ +static int mmc_test_map_sg_max_scatter(struct mmc_test_mem *mem, + unsigned long sz, + struct scatterlist *sglist, + unsigned int max_segs, + unsigned int *sg_len) +{ + struct scatterlist *sg = NULL; + unsigned int i = mem->cnt, cnt; + unsigned long len; + void *base, *addr, *last_addr = NULL; + + sg_init_table(sglist, max_segs); + + *sg_len = 0; + while (sz && i) { + base = page_address(mem->arr[--i].page); + cnt = 1 << mem->arr[i].order; + while (sz && cnt) { + addr = base + PAGE_SIZE * --cnt; + if (last_addr && last_addr + PAGE_SIZE == addr) + continue; + last_addr = addr; + len = PAGE_SIZE; + if (sz < len) + len = sz; + if (sg) + sg = sg_next(sg); + else + sg = sglist; + if (!sg) + return -EINVAL; + sg_set_page(sg, virt_to_page(addr), len, 0); + sz -= len; + *sg_len += 1; + } + } + + if (sg) + sg_mark_end(sg); + + return 0; +} + +/* + * Calculate transfer rate in bytes per second. + */ +static unsigned int mmc_test_rate(uint64_t bytes, struct timespec *ts) +{ + uint64_t ns; + + ns = ts->tv_sec; + ns *= 1000000000; + ns += ts->tv_nsec; + + bytes *= 1000000000; + + while (ns > UINT_MAX) { + bytes >>= 1; + ns >>= 1; + } + + if (!ns) + return 0; + + do_div(bytes, (uint32_t)ns); + + return bytes; +} + +/* + * Print the transfer rate. + */ +static void mmc_test_print_rate(struct mmc_test_card *test, uint64_t bytes, + struct timespec *ts1, struct timespec *ts2) +{ + unsigned int rate, sectors = bytes >> 9; + struct timespec ts; + + ts = timespec_sub(*ts2, *ts1); + + rate = mmc_test_rate(bytes, &ts); + + printk(KERN_INFO "%s: Transfer of %u sectors (%u%s KiB) took %lu.%09lu " + "seconds (%u kB/s, %u KiB/s)\n", + mmc_hostname(test->card->host), sectors, sectors >> 1, + (sectors == 1 ? ".5" : ""), (unsigned long)ts.tv_sec, + (unsigned long)ts.tv_nsec, rate / 1000, rate / 1024); +} + +/* + * Print the average transfer rate. + */ +static void mmc_test_print_avg_rate(struct mmc_test_card *test, uint64_t bytes, + unsigned int count, struct timespec *ts1, + struct timespec *ts2) +{ + unsigned int rate, sectors = bytes >> 9; + uint64_t tot = bytes * count; + struct timespec ts; + + ts = timespec_sub(*ts2, *ts1); + + rate = mmc_test_rate(tot, &ts); + + printk(KERN_INFO "%s: Transfer of %u x %u sectors (%u x %u%s KiB) took " + "%lu.%09lu seconds (%u kB/s, %u KiB/s)\n", + mmc_hostname(test->card->host), count, sectors, count, + sectors >> 1, (sectors == 1 ? ".5" : ""), + (unsigned long)ts.tv_sec, (unsigned long)ts.tv_nsec, + rate / 1000, rate / 1024); +} + +/* + * Return the card size in sectors. + */ +static unsigned int mmc_test_capacity(struct mmc_card *card) +{ + if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) + return card->ext_csd.sectors; + else + return card->csd.capacity << (card->csd.read_blkbits - 9); +} + /*******************************************************************/ /* Test preparation and cleanup */ /*******************************************************************/ @@ -893,8 +1197,419 @@ static int mmc_test_multi_read_high(struct mmc_test_card *test) return 0; } +#else + +static int mmc_test_no_highmem(struct mmc_test_card *test) +{ + printk(KERN_INFO "%s: Highmem not configured - test skipped\n", + mmc_hostname(test->card->host)); + return 0; +} + #endif /* CONFIG_HIGHMEM */ +/* + * Map sz bytes so that it can be transferred. + */ +static int mmc_test_area_map(struct mmc_test_card *test, unsigned long sz, + int max_scatter) +{ + struct mmc_test_area *t = &test->area; + + t->blocks = sz >> 9; + + if (max_scatter) { + return mmc_test_map_sg_max_scatter(t->mem, sz, t->sg, + t->max_segs, &t->sg_len); + } else { + return mmc_test_map_sg(t->mem, sz, t->sg, 1, t->max_segs, + &t->sg_len); + } +} + +/* + * Transfer bytes mapped by mmc_test_area_map(). + */ +static int mmc_test_area_transfer(struct mmc_test_card *test, + unsigned int dev_addr, int write) +{ + struct mmc_test_area *t = &test->area; + + return mmc_test_simple_transfer(test, t->sg, t->sg_len, dev_addr, + t->blocks, 512, write); +} + +/* + * Map and transfer bytes. + */ +static int mmc_test_area_io(struct mmc_test_card *test, unsigned long sz, + unsigned int dev_addr, int write, int max_scatter, + int timed) +{ + struct timespec ts1, ts2; + int ret; + + ret = mmc_test_area_map(test, sz, max_scatter); + if (ret) + return ret; + + if (timed) + getnstimeofday(&ts1); + + ret = mmc_test_area_transfer(test, dev_addr, write); + if (ret) + return ret; + + if (timed) + getnstimeofday(&ts2); + + if (timed) + mmc_test_print_rate(test, sz, &ts1, &ts2); + + return 0; +} + +/* + * Write the test area entirely. + */ +static int mmc_test_area_fill(struct mmc_test_card *test) +{ + return mmc_test_area_io(test, test->area.max_sz, test->area.dev_addr, + 1, 0, 0); +} + +/* + * Erase the test area entirely. + */ +static int mmc_test_area_erase(struct mmc_test_card *test) +{ + struct mmc_test_area *t = &test->area; + + if (!mmc_can_erase(test->card)) + return 0; + + return mmc_erase(test->card, t->dev_addr, test->area.max_sz >> 9, + MMC_ERASE_ARG); +} + +/* + * Cleanup struct mmc_test_area. + */ +static int mmc_test_area_cleanup(struct mmc_test_card *test) +{ + struct mmc_test_area *t = &test->area; + + kfree(t->sg); + mmc_test_free_mem(t->mem); + + return 0; +} + +/* + * Initialize an area for testing large transfers. The size of the area is the + * preferred erase size which is a good size for optimal transfer speed. Note + * that is typically 4MiB for modern cards. The test area is set to the middle + * of the card because cards may have different charateristics at the front + * (for FAT file system optimization). Optionally, the area is erased (if the + * card supports it) which may improve write performance. Optionally, the area + * is filled with data for subsequent read tests. + */ +static int mmc_test_area_init(struct mmc_test_card *test, int erase, int fill) +{ + struct mmc_test_area *t = &test->area; + unsigned long min_sz = 64 * 1024; + int ret; + + ret = mmc_test_set_blksize(test, 512); + if (ret) + return ret; + + if (test->card->pref_erase > TEST_AREA_MAX_SIZE >> 9) + t->max_sz = TEST_AREA_MAX_SIZE; + else + t->max_sz = (unsigned long)test->card->pref_erase << 9; + /* + * Try to allocate enough memory for the whole area. Less is OK + * because the same memory can be mapped into the scatterlist more than + * once. + */ + t->mem = mmc_test_alloc_mem(min_sz, t->max_sz); + if (!t->mem) + return -ENOMEM; + + t->max_segs = DIV_ROUND_UP(t->max_sz, PAGE_SIZE); + t->sg = kmalloc(sizeof(struct scatterlist) * t->max_segs, GFP_KERNEL); + if (!t->sg) { + ret = -ENOMEM; + goto out_free; + } + + t->dev_addr = mmc_test_capacity(test->card) / 2; + t->dev_addr -= t->dev_addr % (t->max_sz >> 9); + + if (erase) { + ret = mmc_test_area_erase(test); + if (ret) + goto out_free; + } + + if (fill) { + ret = mmc_test_area_fill(test); + if (ret) + goto out_free; + } + + return 0; + +out_free: + mmc_test_area_cleanup(test); + return ret; +} + +/* + * Prepare for large transfers. Do not erase the test area. + */ +static int mmc_test_area_prepare(struct mmc_test_card *test) +{ + return mmc_test_area_init(test, 0, 0); +} + +/* + * Prepare for large transfers. Do erase the test area. + */ +static int mmc_test_area_prepare_erase(struct mmc_test_card *test) +{ + return mmc_test_area_init(test, 1, 0); +} + +/* + * Prepare for large transfers. Erase and fill the test area. + */ +static int mmc_test_area_prepare_fill(struct mmc_test_card *test) +{ + return mmc_test_area_init(test, 1, 1); +} + +/* + * Test best-case performance. Best-case performance is expected from + * a single large transfer. + * + * An additional option (max_scatter) allows the measurement of the same + * transfer but with no contiguous pages in the scatter list. This tests + * the efficiency of DMA to handle scattered pages. + */ +static int mmc_test_best_performance(struct mmc_test_card *test, int write, + int max_scatter) +{ + return mmc_test_area_io(test, test->area.max_sz, test->area.dev_addr, + write, max_scatter, 1); +} + +/* + * Best-case read performance. + */ +static int mmc_test_best_read_performance(struct mmc_test_card *test) +{ + return mmc_test_best_performance(test, 0, 0); +} + +/* + * Best-case write performance. + */ +static int mmc_test_best_write_performance(struct mmc_test_card *test) +{ + return mmc_test_best_performance(test, 1, 0); +} + +/* + * Best-case read performance into scattered pages. + */ +static int mmc_test_best_read_perf_max_scatter(struct mmc_test_card *test) +{ + return mmc_test_best_performance(test, 0, 1); +} + +/* + * Best-case write performance from scattered pages. + */ +static int mmc_test_best_write_perf_max_scatter(struct mmc_test_card *test) +{ + return mmc_test_best_performance(test, 1, 1); +} + +/* + * Single read performance by transfer size. + */ +static int mmc_test_profile_read_perf(struct mmc_test_card *test) +{ + unsigned long sz; + unsigned int dev_addr; + int ret; + + for (sz = 512; sz < test->area.max_sz; sz <<= 1) { + dev_addr = test->area.dev_addr + (sz >> 9); + ret = mmc_test_area_io(test, sz, dev_addr, 0, 0, 1); + if (ret) + return ret; + } + dev_addr = test->area.dev_addr; + return mmc_test_area_io(test, sz, dev_addr, 0, 0, 1); +} + +/* + * Single write performance by transfer size. + */ +static int mmc_test_profile_write_perf(struct mmc_test_card *test) +{ + unsigned long sz; + unsigned int dev_addr; + int ret; + + ret = mmc_test_area_erase(test); + if (ret) + return ret; + for (sz = 512; sz < test->area.max_sz; sz <<= 1) { + dev_addr = test->area.dev_addr + (sz >> 9); + ret = mmc_test_area_io(test, sz, dev_addr, 1, 0, 1); + if (ret) + return ret; + } + ret = mmc_test_area_erase(test); + if (ret) + return ret; + dev_addr = test->area.dev_addr; + return mmc_test_area_io(test, sz, dev_addr, 1, 0, 1); +} + +/* + * Single trim performance by transfer size. + */ +static int mmc_test_profile_trim_perf(struct mmc_test_card *test) +{ + unsigned long sz; + unsigned int dev_addr; + struct timespec ts1, ts2; + int ret; + + if (!mmc_can_trim(test->card)) + return RESULT_UNSUP_CARD; + + if (!mmc_can_erase(test->card)) + return RESULT_UNSUP_HOST; + + for (sz = 512; sz < test->area.max_sz; sz <<= 1) { + dev_addr = test->area.dev_addr + (sz >> 9); + getnstimeofday(&ts1); + ret = mmc_erase(test->card, dev_addr, sz >> 9, MMC_TRIM_ARG); + if (ret) + return ret; + getnstimeofday(&ts2); + mmc_test_print_rate(test, sz, &ts1, &ts2); + } + dev_addr = test->area.dev_addr; + getnstimeofday(&ts1); + ret = mmc_erase(test->card, dev_addr, sz >> 9, MMC_TRIM_ARG); + if (ret) + return ret; + getnstimeofday(&ts2); + mmc_test_print_rate(test, sz, &ts1, &ts2); + return 0; +} + +/* + * Consecutive read performance by transfer size. + */ +static int mmc_test_profile_seq_read_perf(struct mmc_test_card *test) +{ + unsigned long sz; + unsigned int dev_addr, i, cnt; + struct timespec ts1, ts2; + int ret; + + for (sz = 512; sz <= test->area.max_sz; sz <<= 1) { + cnt = test->area.max_sz / sz; + dev_addr = test->area.dev_addr; + getnstimeofday(&ts1); + for (i = 0; i < cnt; i++) { + ret = mmc_test_area_io(test, sz, dev_addr, 0, 0, 0); + if (ret) + return ret; + dev_addr += (sz >> 9); + } + getnstimeofday(&ts2); + mmc_test_print_avg_rate(test, sz, cnt, &ts1, &ts2); + } + return 0; +} + +/* + * Consecutive write performance by transfer size. + */ +static int mmc_test_profile_seq_write_perf(struct mmc_test_card *test) +{ + unsigned long sz; + unsigned int dev_addr, i, cnt; + struct timespec ts1, ts2; + int ret; + + for (sz = 512; sz <= test->area.max_sz; sz <<= 1) { + ret = mmc_test_area_erase(test); + if (ret) + return ret; + cnt = test->area.max_sz / sz; + dev_addr = test->area.dev_addr; + getnstimeofday(&ts1); + for (i = 0; i < cnt; i++) { + ret = mmc_test_area_io(test, sz, dev_addr, 1, 0, 0); + if (ret) + return ret; + dev_addr += (sz >> 9); + } + getnstimeofday(&ts2); + mmc_test_print_avg_rate(test, sz, cnt, &ts1, &ts2); + } + return 0; +} + +/* + * Consecutive trim performance by transfer size. + */ +static int mmc_test_profile_seq_trim_perf(struct mmc_test_card *test) +{ + unsigned long sz; + unsigned int dev_addr, i, cnt; + struct timespec ts1, ts2; + int ret; + + if (!mmc_can_trim(test->card)) + return RESULT_UNSUP_CARD; + + if (!mmc_can_erase(test->card)) + return RESULT_UNSUP_HOST; + + for (sz = 512; sz <= test->area.max_sz; sz <<= 1) { + ret = mmc_test_area_erase(test); + if (ret) + return ret; + ret = mmc_test_area_fill(test); + if (ret) + return ret; + cnt = test->area.max_sz / sz; + dev_addr = test->area.dev_addr; + getnstimeofday(&ts1); + for (i = 0; i < cnt; i++) { + ret = mmc_erase(test->card, dev_addr, sz >> 9, + MMC_TRIM_ARG); + if (ret) + return ret; + dev_addr += (sz >> 9); + } + getnstimeofday(&ts2); + mmc_test_print_avg_rate(test, sz, cnt, &ts1, &ts2); + } + return 0; +} + static const struct mmc_test_case mmc_test_cases[] = { { .name = "Basic write (no data verification)", @@ -1040,8 +1755,100 @@ static const struct mmc_test_case mmc_test_cases[] = { .cleanup = mmc_test_cleanup, }, +#else + + { + .name = "Highmem write", + .run = mmc_test_no_highmem, + }, + + { + .name = "Highmem read", + .run = mmc_test_no_highmem, + }, + + { + .name = "Multi-block highmem write", + .run = mmc_test_no_highmem, + }, + + { + .name = "Multi-block highmem read", + .run = mmc_test_no_highmem, + }, + #endif /* CONFIG_HIGHMEM */ + { + .name = "Best-case read performance", + .prepare = mmc_test_area_prepare_fill, + .run = mmc_test_best_read_performance, + .cleanup = mmc_test_area_cleanup, + }, + + { + .name = "Best-case write performance", + .prepare = mmc_test_area_prepare_erase, + .run = mmc_test_best_write_performance, + .cleanup = mmc_test_area_cleanup, + }, + + { + .name = "Best-case read performance into scattered pages", + .prepare = mmc_test_area_prepare_fill, + .run = mmc_test_best_read_perf_max_scatter, + .cleanup = mmc_test_area_cleanup, + }, + + { + .name = "Best-case write performance from scattered pages", + .prepare = mmc_test_area_prepare_erase, + .run = mmc_test_best_write_perf_max_scatter, + .cleanup = mmc_test_area_cleanup, + }, + + { + .name = "Single read performance by transfer size", + .prepare = mmc_test_area_prepare_fill, + .run = mmc_test_profile_read_perf, + .cleanup = mmc_test_area_cleanup, + }, + + { + .name = "Single write performance by transfer size", + .prepare = mmc_test_area_prepare, + .run = mmc_test_profile_write_perf, + .cleanup = mmc_test_area_cleanup, + }, + + { + .name = "Single trim performance by transfer size", + .prepare = mmc_test_area_prepare_fill, + .run = mmc_test_profile_trim_perf, + .cleanup = mmc_test_area_cleanup, + }, + + { + .name = "Consecutive read performance by transfer size", + .prepare = mmc_test_area_prepare_fill, + .run = mmc_test_profile_seq_read_perf, + .cleanup = mmc_test_area_cleanup, + }, + + { + .name = "Consecutive write performance by transfer size", + .prepare = mmc_test_area_prepare, + .run = mmc_test_profile_seq_write_perf, + .cleanup = mmc_test_area_cleanup, + }, + + { + .name = "Consecutive trim performance by transfer size", + .prepare = mmc_test_area_prepare, + .run = mmc_test_profile_seq_trim_perf, + .cleanup = mmc_test_area_cleanup, + }, + }; static DEFINE_MUTEX(mmc_test_lock); diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c index c77eb49eda0..e876678176b 100644 --- a/drivers/mmc/card/queue.c +++ b/drivers/mmc/card/queue.c @@ -30,9 +30,9 @@ static int mmc_prep_request(struct request_queue *q, struct request *req) { /* - * We only like normal block requests. + * We only like normal block requests and discards. */ - if (req->cmd_type != REQ_TYPE_FS) { + if (req->cmd_type != REQ_TYPE_FS && !(req->cmd_flags & REQ_DISCARD)) { blk_dump_rq_flags(req, "MMC bad request"); return BLKPREP_KILL; } @@ -130,6 +130,21 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock blk_queue_prep_rq(mq->queue, mmc_prep_request); blk_queue_ordered(mq->queue, QUEUE_ORDERED_DRAIN); queue_flag_set_unlocked(QUEUE_FLAG_NONROT, mq->queue); + if (mmc_can_erase(card)) { + queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, mq->queue); + mq->queue->limits.max_discard_sectors = UINT_MAX; + if (card->erased_byte == 0) + mq->queue->limits.discard_zeroes_data = 1; + if (!mmc_can_trim(card) && is_power_of_2(card->erase_size)) { + mq->queue->limits.discard_granularity = + card->erase_size << 9; + mq->queue->limits.discard_alignment = + card->erase_size << 9; + } + if (mmc_can_secure_erase_trim(card)) + queue_flag_set_unlocked(QUEUE_FLAG_SECDISCARD, + mq->queue); + } #ifdef CONFIG_MMC_BLOCK_BOUNCE if (host->max_hw_segs == 1) { diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index 49d9dcaeca4..7cd9749dc21 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -37,6 +37,8 @@ static ssize_t mmc_type_show(struct device *dev, return sprintf(buf, "SD\n"); case MMC_TYPE_SDIO: return sprintf(buf, "SDIO\n"); + case MMC_TYPE_SD_COMBO: + return sprintf(buf, "SDcombo\n"); default: return -EFAULT; } @@ -74,6 +76,9 @@ mmc_bus_uevent(struct device *dev, struct kobj_uevent_env *env) case MMC_TYPE_SDIO: type = "SDIO"; break; + case MMC_TYPE_SD_COMBO: + type = "SDcombo"; + break; default: type = NULL; } @@ -239,6 +244,10 @@ int mmc_add_card(struct mmc_card *card) case MMC_TYPE_SDIO: type = "SDIO"; break; + case MMC_TYPE_SD_COMBO: + type = "SD-combo"; + if (mmc_card_blockaddr(card)) + type = "SDHC-combo"; default: type = "?"; break; diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 569e94da844..5db49b124ff 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1050,6 +1050,352 @@ void mmc_detect_change(struct mmc_host *host, unsigned long delay) EXPORT_SYMBOL(mmc_detect_change); +void mmc_init_erase(struct mmc_card *card) +{ + unsigned int sz; + + if (is_power_of_2(card->erase_size)) + card->erase_shift = ffs(card->erase_size) - 1; + else + card->erase_shift = 0; + + /* + * It is possible to erase an arbitrarily large area of an SD or MMC + * card. That is not desirable because it can take a long time + * (minutes) potentially delaying more important I/O, and also the + * timeout calculations become increasingly hugely over-estimated. + * Consequently, 'pref_erase' is defined as a guide to limit erases + * to that size and alignment. + * + * For SD cards that define Allocation Unit size, limit erases to one + * Allocation Unit at a time. For MMC cards that define High Capacity + * Erase Size, whether it is switched on or not, limit to that size. + * Otherwise just have a stab at a good value. For modern cards it + * will end up being 4MiB. Note that if the value is too small, it + * can end up taking longer to erase. + */ + if (mmc_card_sd(card) && card->ssr.au) { + card->pref_erase = card->ssr.au; + card->erase_shift = ffs(card->ssr.au) - 1; + } else if (card->ext_csd.hc_erase_size) { + card->pref_erase = card->ext_csd.hc_erase_size; + } else { + sz = (card->csd.capacity << (card->csd.read_blkbits - 9)) >> 11; + if (sz < 128) + card->pref_erase = 512 * 1024 / 512; + else if (sz < 512) + card->pref_erase = 1024 * 1024 / 512; + else if (sz < 1024) + card->pref_erase = 2 * 1024 * 1024 / 512; + else + card->pref_erase = 4 * 1024 * 1024 / 512; + if (card->pref_erase < card->erase_size) + card->pref_erase = card->erase_size; + else { + sz = card->pref_erase % card->erase_size; + if (sz) + card->pref_erase += card->erase_size - sz; + } + } +} + +static void mmc_set_mmc_erase_timeout(struct mmc_card *card, + struct mmc_command *cmd, + unsigned int arg, unsigned int qty) +{ + unsigned int erase_timeout; + + if (card->ext_csd.erase_group_def & 1) { + /* High Capacity Erase Group Size uses HC timeouts */ + if (arg == MMC_TRIM_ARG) + erase_timeout = card->ext_csd.trim_timeout; + else + erase_timeout = card->ext_csd.hc_erase_timeout; + } else { + /* CSD Erase Group Size uses write timeout */ + unsigned int mult = (10 << card->csd.r2w_factor); + unsigned int timeout_clks = card->csd.tacc_clks * mult; + unsigned int timeout_us; + + /* Avoid overflow: e.g. tacc_ns=80000000 mult=1280 */ + if (card->csd.tacc_ns < 1000000) + timeout_us = (card->csd.tacc_ns * mult) / 1000; + else + timeout_us = (card->csd.tacc_ns / 1000) * mult; + + /* + * ios.clock is only a target. The real clock rate might be + * less but not that much less, so fudge it by multiplying by 2. + */ + timeout_clks <<= 1; + timeout_us += (timeout_clks * 1000) / + (card->host->ios.clock / 1000); + + erase_timeout = timeout_us / 1000; + + /* + * Theoretically, the calculation could underflow so round up + * to 1ms in that case. + */ + if (!erase_timeout) + erase_timeout = 1; + } + + /* Multiplier for secure operations */ + if (arg & MMC_SECURE_ARGS) { + if (arg == MMC_SECURE_ERASE_ARG) + erase_timeout *= card->ext_csd.sec_erase_mult; + else + erase_timeout *= card->ext_csd.sec_trim_mult; + } + + erase_timeout *= qty; + + /* + * Ensure at least a 1 second timeout for SPI as per + * 'mmc_set_data_timeout()' + */ + if (mmc_host_is_spi(card->host) && erase_timeout < 1000) + erase_timeout = 1000; + + cmd->erase_timeout = erase_timeout; +} + +static void mmc_set_sd_erase_timeout(struct mmc_card *card, + struct mmc_command *cmd, unsigned int arg, + unsigned int qty) +{ + if (card->ssr.erase_timeout) { + /* Erase timeout specified in SD Status Register (SSR) */ + cmd->erase_timeout = card->ssr.erase_timeout * qty + + card->ssr.erase_offset; + } else { + /* + * Erase timeout not specified in SD Status Register (SSR) so + * use 250ms per write block. + */ + cmd->erase_timeout = 250 * qty; + } + + /* Must not be less than 1 second */ + if (cmd->erase_timeout < 1000) + cmd->erase_timeout = 1000; +} + +static void mmc_set_erase_timeout(struct mmc_card *card, + struct mmc_command *cmd, unsigned int arg, + unsigned int qty) +{ + if (mmc_card_sd(card)) + mmc_set_sd_erase_timeout(card, cmd, arg, qty); + else + mmc_set_mmc_erase_timeout(card, cmd, arg, qty); +} + +static int mmc_do_erase(struct mmc_card *card, unsigned int from, + unsigned int to, unsigned int arg) +{ + struct mmc_command cmd; + unsigned int qty = 0; + int err; + + /* + * qty is used to calculate the erase timeout which depends on how many + * erase groups (or allocation units in SD terminology) are affected. + * We count erasing part of an erase group as one erase group. + * For SD, the allocation units are always a power of 2. For MMC, the + * erase group size is almost certainly also power of 2, but it does not + * seem to insist on that in the JEDEC standard, so we fall back to + * division in that case. SD may not specify an allocation unit size, + * in which case the timeout is based on the number of write blocks. + * + * Note that the timeout for secure trim 2 will only be correct if the + * number of erase groups specified is the same as the total of all + * preceding secure trim 1 commands. Since the power may have been + * lost since the secure trim 1 commands occurred, it is generally + * impossible to calculate the secure trim 2 timeout correctly. + */ + if (card->erase_shift) + qty += ((to >> card->erase_shift) - + (from >> card->erase_shift)) + 1; + else if (mmc_card_sd(card)) + qty += to - from + 1; + else + qty += ((to / card->erase_size) - + (from / card->erase_size)) + 1; + + if (!mmc_card_blockaddr(card)) { + from <<= 9; + to <<= 9; + } + + memset(&cmd, 0, sizeof(struct mmc_command)); + if (mmc_card_sd(card)) + cmd.opcode = SD_ERASE_WR_BLK_START; + else + cmd.opcode = MMC_ERASE_GROUP_START; + cmd.arg = from; + cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC; + err = mmc_wait_for_cmd(card->host, &cmd, 0); + if (err) { + printk(KERN_ERR "mmc_erase: group start error %d, " + "status %#x\n", err, cmd.resp[0]); + err = -EINVAL; + goto out; + } + + memset(&cmd, 0, sizeof(struct mmc_command)); + if (mmc_card_sd(card)) + cmd.opcode = SD_ERASE_WR_BLK_END; + else + cmd.opcode = MMC_ERASE_GROUP_END; + cmd.arg = to; + cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC; + err = mmc_wait_for_cmd(card->host, &cmd, 0); + if (err) { + printk(KERN_ERR "mmc_erase: group end error %d, status %#x\n", + err, cmd.resp[0]); + err = -EINVAL; + goto out; + } + + memset(&cmd, 0, sizeof(struct mmc_command)); + cmd.opcode = MMC_ERASE; + cmd.arg = arg; + cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; + mmc_set_erase_timeout(card, &cmd, arg, qty); + err = mmc_wait_for_cmd(card->host, &cmd, 0); + if (err) { + printk(KERN_ERR "mmc_erase: erase error %d, status %#x\n", + err, cmd.resp[0]); + err = -EIO; + goto out; + } + + if (mmc_host_is_spi(card->host)) + goto out; + + do { + memset(&cmd, 0, sizeof(struct mmc_command)); + cmd.opcode = MMC_SEND_STATUS; + cmd.arg = card->rca << 16; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + /* Do not retry else we can't see errors */ + err = mmc_wait_for_cmd(card->host, &cmd, 0); + if (err || (cmd.resp[0] & 0xFDF92000)) { + printk(KERN_ERR "error %d requesting status %#x\n", + err, cmd.resp[0]); + err = -EIO; + goto out; + } + } while (!(cmd.resp[0] & R1_READY_FOR_DATA) || + R1_CURRENT_STATE(cmd.resp[0]) == 7); +out: + return err; +} + +/** + * mmc_erase - erase sectors. + * @card: card to erase + * @from: first sector to erase + * @nr: number of sectors to erase + * @arg: erase command argument (SD supports only %MMC_ERASE_ARG) + * + * Caller must claim host before calling this function. + */ +int mmc_erase(struct mmc_card *card, unsigned int from, unsigned int nr, + unsigned int arg) +{ + unsigned int rem, to = from + nr; + + if (!(card->host->caps & MMC_CAP_ERASE) || + !(card->csd.cmdclass & CCC_ERASE)) + return -EOPNOTSUPP; + + if (!card->erase_size) + return -EOPNOTSUPP; + + if (mmc_card_sd(card) && arg != MMC_ERASE_ARG) + return -EOPNOTSUPP; + + if ((arg & MMC_SECURE_ARGS) && + !(card->ext_csd.sec_feature_support & EXT_CSD_SEC_ER_EN)) + return -EOPNOTSUPP; + + if ((arg & MMC_TRIM_ARGS) && + !(card->ext_csd.sec_feature_support & EXT_CSD_SEC_GB_CL_EN)) + return -EOPNOTSUPP; + + if (arg == MMC_SECURE_ERASE_ARG) { + if (from % card->erase_size || nr % card->erase_size) + return -EINVAL; + } + + if (arg == MMC_ERASE_ARG) { + rem = from % card->erase_size; + if (rem) { + rem = card->erase_size - rem; + from += rem; + if (nr > rem) + nr -= rem; + else + return 0; + } + rem = nr % card->erase_size; + if (rem) + nr -= rem; + } + + if (nr == 0) + return 0; + + to = from + nr; + + if (to <= from) + return -EINVAL; + + /* 'from' and 'to' are inclusive */ + to -= 1; + + return mmc_do_erase(card, from, to, arg); +} +EXPORT_SYMBOL(mmc_erase); + +int mmc_can_erase(struct mmc_card *card) +{ + if ((card->host->caps & MMC_CAP_ERASE) && + (card->csd.cmdclass & CCC_ERASE) && card->erase_size) + return 1; + return 0; +} +EXPORT_SYMBOL(mmc_can_erase); + +int mmc_can_trim(struct mmc_card *card) +{ + if (card->ext_csd.sec_feature_support & EXT_CSD_SEC_GB_CL_EN) + return 1; + return 0; +} +EXPORT_SYMBOL(mmc_can_trim); + +int mmc_can_secure_erase_trim(struct mmc_card *card) +{ + if (card->ext_csd.sec_feature_support & EXT_CSD_SEC_ER_EN) + return 1; + return 0; +} +EXPORT_SYMBOL(mmc_can_secure_erase_trim); + +int mmc_erase_group_aligned(struct mmc_card *card, unsigned int from, + unsigned int nr) +{ + if (!card->erase_size) + return 0; + if (from % card->erase_size || nr % card->erase_size) + return 0; + return 1; +} +EXPORT_SYMBOL(mmc_erase_group_aligned); void mmc_rescan(struct work_struct *work) { @@ -1057,6 +1403,17 @@ void mmc_rescan(struct work_struct *work) container_of(work, struct mmc_host, detect.work); u32 ocr; int err; + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + + if (host->rescan_disable) { + spin_unlock_irqrestore(&host->lock, flags); + return; + } + + spin_unlock_irqrestore(&host->lock, flags); + mmc_bus_get(host); @@ -1099,8 +1456,15 @@ void mmc_rescan(struct work_struct *work) */ err = mmc_send_io_op_cond(host, 0, &ocr); if (!err) { - if (mmc_attach_sdio(host, ocr)) - mmc_power_off(host); + if (mmc_attach_sdio(host, ocr)) { + mmc_claim_host(host); + /* try SDMEM (but not MMC) even if SDIO is broken */ + if (mmc_send_app_op_cond(host, 0, &ocr)) + goto out_fail; + + if (mmc_attach_sd(host, ocr)) + mmc_power_off(host); + } goto out; } @@ -1124,6 +1488,7 @@ void mmc_rescan(struct work_struct *work) goto out; } +out_fail: mmc_release_host(host); mmc_power_off(host); @@ -1266,19 +1631,6 @@ int mmc_suspend_host(struct mmc_host *host) if (host->bus_ops && !host->bus_dead) { if (host->bus_ops->suspend) err = host->bus_ops->suspend(host); - if (err == -ENOSYS || !host->bus_ops->resume) { - /* - * We simply "remove" the card in this case. - * It will be redetected on resume. - */ - if (host->bus_ops->remove) - host->bus_ops->remove(host); - mmc_claim_host(host); - mmc_detach_bus(host); - mmc_release_host(host); - host->pm_flags = 0; - err = 0; - } } mmc_bus_put(host); @@ -1310,28 +1662,61 @@ int mmc_resume_host(struct mmc_host *host) printk(KERN_WARNING "%s: error %d during resume " "(card was removed?)\n", mmc_hostname(host), err); - if (host->bus_ops->remove) - host->bus_ops->remove(host); - mmc_claim_host(host); - mmc_detach_bus(host); - mmc_release_host(host); - /* no need to bother upper layers */ err = 0; } } mmc_bus_put(host); - /* - * We add a slight delay here so that resume can progress - * in parallel. - */ - mmc_detect_change(host, 1); - return err; } - EXPORT_SYMBOL(mmc_resume_host); +/* Do the card removal on suspend if card is assumed removeable + * Do that in pm notifier while userspace isn't yet frozen, so we will be able + to sync the card. +*/ +int mmc_pm_notify(struct notifier_block *notify_block, + unsigned long mode, void *unused) +{ + struct mmc_host *host = container_of( + notify_block, struct mmc_host, pm_notify); + unsigned long flags; + + + switch (mode) { + case PM_HIBERNATION_PREPARE: + case PM_SUSPEND_PREPARE: + + spin_lock_irqsave(&host->lock, flags); + host->rescan_disable = 1; + spin_unlock_irqrestore(&host->lock, flags); + cancel_delayed_work_sync(&host->detect); + + if (!host->bus_ops || host->bus_ops->suspend) + break; + + mmc_claim_host(host); + + if (host->bus_ops->remove) + host->bus_ops->remove(host); + + mmc_detach_bus(host); + mmc_release_host(host); + host->pm_flags = 0; + break; + + case PM_POST_SUSPEND: + case PM_POST_HIBERNATION: + + spin_lock_irqsave(&host->lock, flags); + host->rescan_disable = 0; + spin_unlock_irqrestore(&host->lock, flags); + mmc_detect_change(host, 0); + + } + + return 0; +} #endif static int __init mmc_init(void) diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index a811c52a165..9d9eef50e5d 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -29,6 +29,8 @@ struct mmc_bus_ops { void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops); void mmc_detach_bus(struct mmc_host *host); +void mmc_init_erase(struct mmc_card *card); + void mmc_set_chip_select(struct mmc_host *host, int mode); void mmc_set_clock(struct mmc_host *host, unsigned int hz); void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode); diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 47353909e34..0efe631e50c 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -17,6 +17,7 @@ #include <linux/pagemap.h> #include <linux/leds.h> #include <linux/slab.h> +#include <linux/suspend.h> #include <linux/mmc/host.h> @@ -85,6 +86,7 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) init_waitqueue_head(&host->wq); INIT_DELAYED_WORK(&host->detect, mmc_rescan); INIT_DELAYED_WORK_DEFERRABLE(&host->disable, mmc_host_deeper_disable); + host->pm_notify.notifier_call = mmc_pm_notify; /* * By default, hosts do not support SGIO or large requests. @@ -133,6 +135,7 @@ int mmc_add_host(struct mmc_host *host) #endif mmc_start_host(host); + register_pm_notifier(&host->pm_notify); return 0; } @@ -149,6 +152,7 @@ EXPORT_SYMBOL(mmc_add_host); */ void mmc_remove_host(struct mmc_host *host) { + unregister_pm_notifier(&host->pm_notify); mmc_stop_host(host); #ifdef CONFIG_DEBUG_FS diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 89f7a25b7ac..6909a54c39b 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -108,23 +108,34 @@ static int mmc_decode_cid(struct mmc_card *card) return 0; } +static void mmc_set_erase_size(struct mmc_card *card) +{ + if (card->ext_csd.erase_group_def & 1) + card->erase_size = card->ext_csd.hc_erase_size; + else + card->erase_size = card->csd.erase_size; + + mmc_init_erase(card); +} + /* * Given a 128-bit response, decode to our card CSD structure. */ static int mmc_decode_csd(struct mmc_card *card) { struct mmc_csd *csd = &card->csd; - unsigned int e, m, csd_struct; + unsigned int e, m, a, b; u32 *resp = card->raw_csd; /* * We only understand CSD structure v1.1 and v1.2. * v1.2 has extra information in bits 15, 11 and 10. + * We also support eMMC v4.4 & v4.41. */ - csd_struct = UNSTUFF_BITS(resp, 126, 2); - if (csd_struct != 1 && csd_struct != 2) { + csd->structure = UNSTUFF_BITS(resp, 126, 2); + if (csd->structure == 0) { printk(KERN_ERR "%s: unrecognised CSD structure version %d\n", - mmc_hostname(card->host), csd_struct); + mmc_hostname(card->host), csd->structure); return -EINVAL; } @@ -151,6 +162,13 @@ static int mmc_decode_csd(struct mmc_card *card) csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4); csd->write_partial = UNSTUFF_BITS(resp, 21, 1); + if (csd->write_blkbits >= 9) { + a = UNSTUFF_BITS(resp, 42, 5); + b = UNSTUFF_BITS(resp, 37, 5); + csd->erase_size = (a + 1) * (b + 1); + csd->erase_size <<= csd->write_blkbits - 9; + } + return 0; } @@ -207,11 +225,22 @@ static int mmc_read_ext_csd(struct mmc_card *card) goto out; } + /* Version is coded in the CSD_STRUCTURE byte in the EXT_CSD register */ + if (card->csd.structure == 3) { + int ext_csd_struct = ext_csd[EXT_CSD_STRUCTURE]; + if (ext_csd_struct > 2) { + printk(KERN_ERR "%s: unrecognised EXT_CSD structure " + "version %d\n", mmc_hostname(card->host), + ext_csd_struct); + err = -EINVAL; + goto out; + } + } + card->ext_csd.rev = ext_csd[EXT_CSD_REV]; if (card->ext_csd.rev > 5) { - printk(KERN_ERR "%s: unrecognised EXT_CSD structure " - "version %d\n", mmc_hostname(card->host), - card->ext_csd.rev); + printk(KERN_ERR "%s: unrecognised EXT_CSD revision %d\n", + mmc_hostname(card->host), card->ext_csd.rev); err = -EINVAL; goto out; } @@ -222,7 +251,9 @@ static int mmc_read_ext_csd(struct mmc_card *card) ext_csd[EXT_CSD_SEC_CNT + 1] << 8 | ext_csd[EXT_CSD_SEC_CNT + 2] << 16 | ext_csd[EXT_CSD_SEC_CNT + 3] << 24; - if (card->ext_csd.sectors) + + /* Cards with density > 2GiB are sector addressed */ + if (card->ext_csd.sectors > (2u * 1024 * 1024 * 1024) / 512) mmc_card_set_blockaddr(card); } @@ -247,8 +278,30 @@ static int mmc_read_ext_csd(struct mmc_card *card) if (sa_shift > 0 && sa_shift <= 0x17) card->ext_csd.sa_timeout = 1 << ext_csd[EXT_CSD_S_A_TIMEOUT]; + card->ext_csd.erase_group_def = + ext_csd[EXT_CSD_ERASE_GROUP_DEF]; + card->ext_csd.hc_erase_timeout = 300 * + ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT]; + card->ext_csd.hc_erase_size = + ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] << 10; } + if (card->ext_csd.rev >= 4) { + card->ext_csd.sec_trim_mult = + ext_csd[EXT_CSD_SEC_TRIM_MULT]; + card->ext_csd.sec_erase_mult = + ext_csd[EXT_CSD_SEC_ERASE_MULT]; + card->ext_csd.sec_feature_support = + ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT]; + card->ext_csd.trim_timeout = 300 * + ext_csd[EXT_CSD_TRIM_MULT]; + } + + if (ext_csd[EXT_CSD_ERASED_MEM_CONT]) + card->erased_byte = 0xFF; + else + card->erased_byte = 0x0; + out: kfree(ext_csd); @@ -260,6 +313,8 @@ MMC_DEV_ATTR(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1], MMC_DEV_ATTR(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1], card->raw_csd[2], card->raw_csd[3]); MMC_DEV_ATTR(date, "%02d/%04d\n", card->cid.month, card->cid.year); +MMC_DEV_ATTR(erase_size, "%u\n", card->erase_size << 9); +MMC_DEV_ATTR(preferred_erase_size, "%u\n", card->pref_erase << 9); MMC_DEV_ATTR(fwrev, "0x%x\n", card->cid.fwrev); MMC_DEV_ATTR(hwrev, "0x%x\n", card->cid.hwrev); MMC_DEV_ATTR(manfid, "0x%06x\n", card->cid.manfid); @@ -271,6 +326,8 @@ static struct attribute *mmc_std_attrs[] = { &dev_attr_cid.attr, &dev_attr_csd.attr, &dev_attr_date.attr, + &dev_attr_erase_size.attr, + &dev_attr_preferred_erase_size.attr, &dev_attr_fwrev.attr, &dev_attr_hwrev.attr, &dev_attr_manfid.attr, @@ -407,6 +464,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, err = mmc_read_ext_csd(card); if (err) goto free_card; + /* Erase size depends on CSD and Extended CSD */ + mmc_set_erase_size(card); } /* diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 5eac21df480..0f524108555 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -59,7 +59,7 @@ static const unsigned int tacc_mant[] = { /* * Given the decoded CSD structure, decode the raw CID to our CID structure. */ -static void mmc_decode_cid(struct mmc_card *card) +void mmc_decode_cid(struct mmc_card *card) { u32 *resp = card->raw_cid; @@ -119,6 +119,13 @@ static int mmc_decode_csd(struct mmc_card *card) csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3); csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4); csd->write_partial = UNSTUFF_BITS(resp, 21, 1); + + if (UNSTUFF_BITS(resp, 46, 1)) { + csd->erase_size = 1; + } else if (csd->write_blkbits >= 9) { + csd->erase_size = UNSTUFF_BITS(resp, 39, 7) + 1; + csd->erase_size <<= csd->write_blkbits - 9; + } break; case 1: /* @@ -147,6 +154,7 @@ static int mmc_decode_csd(struct mmc_card *card) csd->r2w_factor = 4; /* Unused */ csd->write_blkbits = 9; csd->write_partial = 0; + csd->erase_size = 1; break; default: printk(KERN_ERR "%s: unrecognised CSD structure version %d\n", @@ -154,6 +162,8 @@ static int mmc_decode_csd(struct mmc_card *card) return -EINVAL; } + card->erase_size = csd->erase_size; + return 0; } @@ -179,10 +189,68 @@ static int mmc_decode_scr(struct mmc_card *card) scr->sda_vsn = UNSTUFF_BITS(resp, 56, 4); scr->bus_widths = UNSTUFF_BITS(resp, 48, 4); + if (UNSTUFF_BITS(resp, 55, 1)) + card->erased_byte = 0xFF; + else + card->erased_byte = 0x0; + return 0; } /* + * Fetch and process SD Status register. + */ +static int mmc_read_ssr(struct mmc_card *card) +{ + unsigned int au, es, et, eo; + int err, i; + u32 *ssr; + + if (!(card->csd.cmdclass & CCC_APP_SPEC)) { + printk(KERN_WARNING "%s: card lacks mandatory SD Status " + "function.\n", mmc_hostname(card->host)); + return 0; + } + + ssr = kmalloc(64, GFP_KERNEL); + if (!ssr) + return -ENOMEM; + + err = mmc_app_sd_status(card, ssr); + if (err) { + printk(KERN_WARNING "%s: problem reading SD Status " + "register.\n", mmc_hostname(card->host)); + err = 0; + goto out; + } + + for (i = 0; i < 16; i++) + ssr[i] = be32_to_cpu(ssr[i]); + + /* + * UNSTUFF_BITS only works with four u32s so we have to offset the + * bitfield positions accordingly. + */ + au = UNSTUFF_BITS(ssr, 428 - 384, 4); + if (au > 0 || au <= 9) { + card->ssr.au = 1 << (au + 4); + es = UNSTUFF_BITS(ssr, 408 - 384, 16); + et = UNSTUFF_BITS(ssr, 402 - 384, 6); + eo = UNSTUFF_BITS(ssr, 400 - 384, 2); + if (es && et) { + card->ssr.erase_timeout = (et * 1000) / es; + card->ssr.erase_offset = eo * 1000; + } + } else { + printk(KERN_WARNING "%s: SD Status: Invalid Allocation Unit " + "size.\n", mmc_hostname(card->host)); + } +out: + kfree(ssr); + return err; +} + +/* * Fetches and decodes switch information */ static int mmc_read_switch(struct mmc_card *card) @@ -238,7 +306,7 @@ out: /* * Test if the card supports high-speed mode and, if so, switch to it. */ -static int mmc_switch_hs(struct mmc_card *card) +int mmc_sd_switch_hs(struct mmc_card *card) { int err; u8 *status; @@ -272,9 +340,9 @@ static int mmc_switch_hs(struct mmc_card *card) printk(KERN_WARNING "%s: Problem switching card " "into high-speed mode!\n", mmc_hostname(card->host)); + err = 0; } else { - mmc_card_set_highspeed(card); - mmc_set_timing(card->host, MMC_TIMING_SD_HS); + err = 1; } out: @@ -289,6 +357,8 @@ MMC_DEV_ATTR(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1], card->raw_csd[2], card->raw_csd[3]); MMC_DEV_ATTR(scr, "%08x%08x\n", card->raw_scr[0], card->raw_scr[1]); MMC_DEV_ATTR(date, "%02d/%04d\n", card->cid.month, card->cid.year); +MMC_DEV_ATTR(erase_size, "%u\n", card->erase_size << 9); +MMC_DEV_ATTR(preferred_erase_size, "%u\n", card->pref_erase << 9); MMC_DEV_ATTR(fwrev, "0x%x\n", card->cid.fwrev); MMC_DEV_ATTR(hwrev, "0x%x\n", card->cid.hwrev); MMC_DEV_ATTR(manfid, "0x%06x\n", card->cid.manfid); @@ -302,6 +372,8 @@ static struct attribute *sd_std_attrs[] = { &dev_attr_csd.attr, &dev_attr_scr.attr, &dev_attr_date.attr, + &dev_attr_erase_size.attr, + &dev_attr_preferred_erase_size.attr, &dev_attr_fwrev.attr, &dev_attr_hwrev.attr, &dev_attr_manfid.attr, @@ -320,26 +392,16 @@ static const struct attribute_group *sd_attr_groups[] = { NULL, }; -static struct device_type sd_type = { +struct device_type sd_type = { .groups = sd_attr_groups, }; /* - * Handle the detection and initialisation of a card. - * - * In the case of a resume, "oldcard" will contain the card - * we're trying to reinitialise. + * Fetch CID from card. */ -static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, - struct mmc_card *oldcard) +int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid) { - struct mmc_card *card; int err; - u32 cid[4]; - unsigned int max_dtr; - - BUG_ON(!host); - WARN_ON(!host->claimed); /* * Since we're changing the OCR value, we seem to @@ -361,92 +423,67 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, err = mmc_send_app_op_cond(host, ocr, NULL); if (err) - goto err; + return err; - /* - * Fetch CID from card. - */ if (mmc_host_is_spi(host)) err = mmc_send_cid(host, cid); else err = mmc_all_send_cid(host, cid); - if (err) - goto err; - if (oldcard) { - if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) { - err = -ENOENT; - goto err; - } - - card = oldcard; - } else { - /* - * Allocate card structure. - */ - card = mmc_alloc_card(host, &sd_type); - if (IS_ERR(card)) { - err = PTR_ERR(card); - goto err; - } + return err; +} - card->type = MMC_TYPE_SD; - memcpy(card->raw_cid, cid, sizeof(card->raw_cid)); - } +int mmc_sd_get_csd(struct mmc_host *host, struct mmc_card *card) +{ + int err; /* - * For native busses: get card RCA and quit open drain mode. + * Fetch CSD from card. */ - if (!mmc_host_is_spi(host)) { - err = mmc_send_relative_addr(host, &card->rca); - if (err) - goto free_card; + err = mmc_send_csd(card, card->raw_csd); + if (err) + return err; - mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); - } + err = mmc_decode_csd(card); + if (err) + return err; - if (!oldcard) { + return 0; +} + +int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card, + bool reinit) +{ + int err; + + if (!reinit) { /* - * Fetch CSD from card. + * Fetch SCR from card. */ - err = mmc_send_csd(card, card->raw_csd); - if (err) - goto free_card; - - err = mmc_decode_csd(card); + err = mmc_app_send_scr(card, card->raw_scr); if (err) - goto free_card; - - mmc_decode_cid(card); - } + return err; - /* - * Select card, as all following commands rely on that. - */ - if (!mmc_host_is_spi(host)) { - err = mmc_select_card(card); + err = mmc_decode_scr(card); if (err) - goto free_card; - } + return err; - if (!oldcard) { /* - * Fetch SCR from card. + * Fetch and process SD Status register. */ - err = mmc_app_send_scr(card, card->raw_scr); + err = mmc_read_ssr(card); if (err) - goto free_card; + return err; - err = mmc_decode_scr(card); - if (err < 0) - goto free_card; + /* Erase init depends on CSD and SSR */ + mmc_init_erase(card); /* * Fetch switch information from card. */ err = mmc_read_switch(card); if (err) - goto free_card; + return err; } /* @@ -458,20 +495,34 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, if (mmc_host_is_spi(host)) { err = mmc_spi_set_crc(host, use_spi_crc); if (err) - goto free_card; + return err; } /* - * Attempt to change to high-speed (if supported) + * Check if read-only switch is active. */ - err = mmc_switch_hs(card); - if (err) - goto free_card; + if (!reinit) { + int ro = -1; - /* - * Compute bus speed. - */ - max_dtr = (unsigned int)-1; + if (host->ops->get_ro) + ro = host->ops->get_ro(host); + + if (ro < 0) { + printk(KERN_WARNING "%s: host does not " + "support reading read-only " + "switch. assuming write-enable.\n", + mmc_hostname(host)); + } else if (ro > 0) { + mmc_card_set_readonly(card); + } + } + + return 0; +} + +unsigned mmc_sd_get_max_clock(struct mmc_card *card) +{ + unsigned max_dtr = (unsigned int)-1; if (mmc_card_highspeed(card)) { if (max_dtr > card->sw_caps.hs_max_dtr) @@ -480,7 +531,97 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, max_dtr = card->csd.max_dtr; } - mmc_set_clock(host, max_dtr); + return max_dtr; +} + +void mmc_sd_go_highspeed(struct mmc_card *card) +{ + mmc_card_set_highspeed(card); + mmc_set_timing(card->host, MMC_TIMING_SD_HS); +} + +/* + * Handle the detection and initialisation of a card. + * + * In the case of a resume, "oldcard" will contain the card + * we're trying to reinitialise. + */ +static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, + struct mmc_card *oldcard) +{ + struct mmc_card *card; + int err; + u32 cid[4]; + + BUG_ON(!host); + WARN_ON(!host->claimed); + + err = mmc_sd_get_cid(host, ocr, cid); + if (err) + return err; + + if (oldcard) { + if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) + return -ENOENT; + + card = oldcard; + } else { + /* + * Allocate card structure. + */ + card = mmc_alloc_card(host, &sd_type); + if (IS_ERR(card)) + return PTR_ERR(card); + + card->type = MMC_TYPE_SD; + memcpy(card->raw_cid, cid, sizeof(card->raw_cid)); + } + + /* + * For native busses: get card RCA and quit open drain mode. + */ + if (!mmc_host_is_spi(host)) { + err = mmc_send_relative_addr(host, &card->rca); + if (err) + return err; + + mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); + } + + if (!oldcard) { + err = mmc_sd_get_csd(host, card); + if (err) + return err; + + mmc_decode_cid(card); + } + + /* + * Select card, as all following commands rely on that. + */ + if (!mmc_host_is_spi(host)) { + err = mmc_select_card(card); + if (err) + return err; + } + + err = mmc_sd_setup_card(host, card, oldcard != NULL); + if (err) + goto free_card; + + /* + * Attempt to change to high-speed (if supported) + */ + err = mmc_sd_switch_hs(card); + if (err > 0) + mmc_sd_go_highspeed(card); + else if (err) + goto free_card; + + /* + * Set bus speed. + */ + mmc_set_clock(host, mmc_sd_get_max_clock(card)); /* * Switch to wider bus (if supported). @@ -494,30 +635,12 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, mmc_set_bus_width(host, MMC_BUS_WIDTH_4); } - /* - * Check if read-only switch is active. - */ - if (!oldcard) { - if (!host->ops->get_ro || host->ops->get_ro(host) < 0) { - printk(KERN_WARNING "%s: host does not " - "support reading read-only " - "switch. assuming write-enable.\n", - mmc_hostname(host)); - } else { - if (host->ops->get_ro(host) > 0) - mmc_card_set_readonly(card); - } - } - - if (!oldcard) - host->card = card; - + host->card = card; return 0; free_card: if (!oldcard) mmc_remove_card(card); -err: return err; } diff --git a/drivers/mmc/core/sd.h b/drivers/mmc/core/sd.h new file mode 100644 index 00000000000..3d8800fa760 --- /dev/null +++ b/drivers/mmc/core/sd.h @@ -0,0 +1,17 @@ +#ifndef _MMC_CORE_SD_H +#define _MMC_CORE_SD_H + +#include <linux/mmc/card.h> + +extern struct device_type sd_type; + +int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid); +int mmc_sd_get_csd(struct mmc_host *host, struct mmc_card *card); +void mmc_decode_cid(struct mmc_card *card); +int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card, + bool reinit); +unsigned mmc_sd_get_max_clock(struct mmc_card *card); +int mmc_sd_switch_hs(struct mmc_card *card); +void mmc_sd_go_highspeed(struct mmc_card *card); + +#endif diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c index 63772e7e760..797cdb5887f 100644 --- a/drivers/mmc/core/sd_ops.c +++ b/drivers/mmc/core/sd_ops.c @@ -346,3 +346,51 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group, return 0; } +int mmc_app_sd_status(struct mmc_card *card, void *ssr) +{ + int err; + struct mmc_request mrq; + struct mmc_command cmd; + struct mmc_data data; + struct scatterlist sg; + + BUG_ON(!card); + BUG_ON(!card->host); + BUG_ON(!ssr); + + /* NOTE: caller guarantees ssr is heap-allocated */ + + err = mmc_app_cmd(card->host, card); + if (err) + return err; + + memset(&mrq, 0, sizeof(struct mmc_request)); + memset(&cmd, 0, sizeof(struct mmc_command)); + memset(&data, 0, sizeof(struct mmc_data)); + + mrq.cmd = &cmd; + mrq.data = &data; + + cmd.opcode = SD_APP_SD_STATUS; + cmd.arg = 0; + cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_ADTC; + + data.blksz = 64; + data.blocks = 1; + data.flags = MMC_DATA_READ; + data.sg = &sg; + data.sg_len = 1; + + sg_init_one(&sg, ssr, 64); + + mmc_set_data_timeout(&data, card); + + mmc_wait_for_req(card->host, &mrq); + + if (cmd.error) + return cmd.error; + if (data.error) + return data.error; + + return 0; +} diff --git a/drivers/mmc/core/sd_ops.h b/drivers/mmc/core/sd_ops.h index 9742d8a3066..ffc2305d905 100644 --- a/drivers/mmc/core/sd_ops.h +++ b/drivers/mmc/core/sd_ops.h @@ -19,6 +19,7 @@ int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca); int mmc_app_send_scr(struct mmc_card *card, u32 *scr); int mmc_sd_switch(struct mmc_card *card, int mode, int group, u8 value, u8 *resp); +int mmc_app_sd_status(struct mmc_card *card, void *ssr); #endif diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index b9dee28ee7d..bd2755e8d9a 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -18,6 +18,7 @@ #include "core.h" #include "bus.h" +#include "sd.h" #include "sdio_bus.h" #include "mmc_ops.h" #include "sd_ops.h" @@ -62,13 +63,19 @@ static int sdio_init_func(struct mmc_card *card, unsigned int fn) func->num = fn; - ret = sdio_read_fbr(func); - if (ret) - goto fail; + if (!(card->quirks & MMC_QUIRK_NONSTD_SDIO)) { + ret = sdio_read_fbr(func); + if (ret) + goto fail; - ret = sdio_read_func_cis(func); - if (ret) - goto fail; + ret = sdio_read_func_cis(func); + if (ret) + goto fail; + } else { + func->vendor = func->card->cis.vendor; + func->device = func->card->cis.device; + func->max_blksize = func->card->cis.blksize; + } card->sdio_func[fn - 1] = func; @@ -159,9 +166,7 @@ static int sdio_enable_wide(struct mmc_card *card) if (ret) return ret; - mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4); - - return 0; + return 1; } /* @@ -221,10 +226,34 @@ static int sdio_disable_wide(struct mmc_card *card) return 0; } + +static int sdio_enable_4bit_bus(struct mmc_card *card) +{ + int err; + + if (card->type == MMC_TYPE_SDIO) + return sdio_enable_wide(card); + + if ((card->host->caps & MMC_CAP_4_BIT_DATA) && + (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) { + err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4); + if (err) + return err; + } else + return 0; + + err = sdio_enable_wide(card); + if (err <= 0) + mmc_app_set_bus_width(card, MMC_BUS_WIDTH_1); + + return err; +} + + /* * Test if the card supports high-speed mode and, if so, switch to it. */ -static int sdio_enable_hs(struct mmc_card *card) +static int mmc_sdio_switch_hs(struct mmc_card *card, int enable) { int ret; u8 speed; @@ -239,16 +268,56 @@ static int sdio_enable_hs(struct mmc_card *card) if (ret) return ret; - speed |= SDIO_SPEED_EHS; + if (enable) + speed |= SDIO_SPEED_EHS; + else + speed &= ~SDIO_SPEED_EHS; ret = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_SPEED, speed, NULL); if (ret) return ret; - mmc_card_set_highspeed(card); - mmc_set_timing(card->host, MMC_TIMING_SD_HS); + return 1; +} - return 0; +/* + * Enable SDIO/combo card's high-speed mode. Return 0/1 if [not]supported. + */ +static int sdio_enable_hs(struct mmc_card *card) +{ + int ret; + + ret = mmc_sdio_switch_hs(card, true); + if (ret <= 0 || card->type == MMC_TYPE_SDIO) + return ret; + + ret = mmc_sd_switch_hs(card); + if (ret <= 0) + mmc_sdio_switch_hs(card, false); + + return ret; +} + +static unsigned mmc_sdio_get_max_clock(struct mmc_card *card) +{ + unsigned max_dtr; + + if (mmc_card_highspeed(card)) { + /* + * The SDIO specification doesn't mention how + * the CIS transfer speed register relates to + * high-speed, but it seems that 50 MHz is + * mandatory. + */ + max_dtr = 50000000; + } else { + max_dtr = card->cis.max_dtr; + } + + if (card->type == MMC_TYPE_SD_COMBO) + max_dtr = min(max_dtr, mmc_sd_get_max_clock(card)); + + return max_dtr; } /* @@ -293,7 +362,24 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, goto err; } - card->type = MMC_TYPE_SDIO; + err = mmc_sd_get_cid(host, host->ocr & ocr, card->raw_cid); + + if (!err) { + card->type = MMC_TYPE_SD_COMBO; + + if (oldcard && (oldcard->type != MMC_TYPE_SD_COMBO || + memcmp(card->raw_cid, oldcard->raw_cid, sizeof(card->raw_cid)) != 0)) { + mmc_remove_card(card); + return -ENOENT; + } + } else { + card->type = MMC_TYPE_SDIO; + + if (oldcard && oldcard->type != MMC_TYPE_SDIO) { + mmc_remove_card(card); + return -ENOENT; + } + } /* * Call the optional HC's init_card function to handle quirks. @@ -313,6 +399,17 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, } /* + * Read CSD, before selecting the card + */ + if (!oldcard && card->type == MMC_TYPE_SD_COMBO) { + err = mmc_sd_get_csd(host, card); + if (err) + return err; + + mmc_decode_cid(card); + } + + /* * Select card, as all following commands rely on that. */ if (!powered_resume && !mmc_host_is_spi(host)) { @@ -321,6 +418,23 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, goto remove; } + if (card->quirks & MMC_QUIRK_NONSTD_SDIO) { + /* + * This is non-standard SDIO device, meaning it doesn't + * have any CIA (Common I/O area) registers present. + * It's host's responsibility to fill cccr and cis + * structures in init_card(). + */ + mmc_set_clock(host, card->cis.max_dtr); + + if (card->cccr.high_speed) { + mmc_card_set_highspeed(card); + mmc_set_timing(card->host, MMC_TIMING_SD_HS); + } + + goto finish; + } + /* * Read the common registers. */ @@ -339,43 +453,57 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, int same = (card->cis.vendor == oldcard->cis.vendor && card->cis.device == oldcard->cis.device); mmc_remove_card(card); - if (!same) { - err = -ENOENT; - goto err; - } + if (!same) + return -ENOENT; + card = oldcard; return 0; } + if (card->type == MMC_TYPE_SD_COMBO) { + err = mmc_sd_setup_card(host, card, oldcard != NULL); + /* handle as SDIO-only card if memory init failed */ + if (err) { + mmc_go_idle(host); + if (mmc_host_is_spi(host)) + /* should not fail, as it worked previously */ + mmc_spi_set_crc(host, use_spi_crc); + card->type = MMC_TYPE_SDIO; + } else + card->dev.type = &sd_type; + } + + /* + * If needed, disconnect card detection pull-up resistor. + */ + err = sdio_disable_cd(card); + if (err) + goto remove; + /* * Switch to high-speed (if supported). */ err = sdio_enable_hs(card); - if (err) + if (err > 0) + mmc_sd_go_highspeed(card); + else if (err) goto remove; /* * Change to the card's maximum speed. */ - if (mmc_card_highspeed(card)) { - /* - * The SDIO specification doesn't mention how - * the CIS transfer speed register relates to - * high-speed, but it seems that 50 MHz is - * mandatory. - */ - mmc_set_clock(host, 50000000); - } else { - mmc_set_clock(host, card->cis.max_dtr); - } + mmc_set_clock(host, mmc_sdio_get_max_clock(card)); /* * Switch to wider bus (if supported). */ - err = sdio_enable_wide(card); - if (err) + err = sdio_enable_4bit_bus(card); + if (err > 0) + mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4); + else if (err) goto remove; +finish: if (!oldcard) host->card = card; return 0; @@ -487,9 +615,14 @@ static int mmc_sdio_resume(struct mmc_host *host) mmc_claim_host(host); err = mmc_sdio_init_card(host, host->ocr, host->card, (host->pm_flags & MMC_PM_KEEP_POWER)); - if (!err) + if (!err) { /* We may have switched to 1-bit mode during suspend. */ - err = sdio_enable_wide(host->card); + err = sdio_enable_4bit_bus(host->card); + if (err > 0) { + mmc_set_bus_width(host, MMC_BUS_WIDTH_4); + err = 0; + } + } if (!err && host->sdio_irqs) mmc_signal_sdio_irq(host); mmc_release_host(host); @@ -574,13 +707,6 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr) card->sdio_funcs = 0; /* - * If needed, disconnect card detection pull-up resistor. - */ - err = sdio_disable_cd(card); - if (err) - goto remove; - - /* * Initialize (but don't add) all present functions. */ for (i = 0; i < funcs; i++, card->sdio_funcs++) { diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index d25e22cee4c..c997474c649 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -121,6 +121,15 @@ config MMC_SDHCI_PLTFM If unsure, say N. +config MMC_SDHCI_CNS3XXX + bool "SDHCI support on the Cavium Networks CNS3xxx SoC" + depends on ARCH_CNS3XXX + depends on MMC_SDHCI_PLTFM + help + This selects the SDHCI support for CNS3xxx System-on-Chip devices. + + If unsure, say N. + config MMC_SDHCI_S3C tristate "SDHCI support on Samsung S3C SoC" depends on MMC_SDHCI && (PLAT_S3C24XX || PLAT_S3C64XX) diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index f4e53c98d94..fe0ba4e2b8b 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -12,7 +12,6 @@ obj-$(CONFIG_MMC_IMX) += imxmmc.o obj-$(CONFIG_MMC_MXC) += mxcmmc.o obj-$(CONFIG_MMC_SDHCI) += sdhci.o obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o -obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-pltfm.o obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c.o obj-$(CONFIG_MMC_SDHCI_SPEAR) += sdhci-spear.o obj-$(CONFIG_MMC_WBSD) += wbsd.o @@ -38,6 +37,10 @@ obj-$(CONFIG_SDH_BFIN) += bfin_sdh.o obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o +obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-platform.o +sdhci-platform-y := sdhci-pltfm.o +sdhci-platform-$(CONFIG_MMC_SDHCI_CNS3XXX) += sdhci-cns3xxx.o + obj-$(CONFIG_MMC_SDHCI_OF) += sdhci-of.o sdhci-of-y := sdhci-of-core.o sdhci-of-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c index 7b0f3ef50f9..1145ea0792e 100644 --- a/drivers/mmc/host/mmc_spi.c +++ b/drivers/mmc/host/mmc_spi.c @@ -1536,6 +1536,7 @@ static int __devexit mmc_spi_remove(struct spi_device *spi) #if defined(CONFIG_OF) static struct of_device_id mmc_spi_of_match_table[] __devinitdata = { { .compatible = "mmc-spi-slot", }, + {}, }; #endif diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 24e09454e52..3bd8ff71ec2 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c @@ -1057,22 +1057,6 @@ msmsdcc_init_dma(struct msmsdcc_host *host) return 0; } -#ifdef CONFIG_MMC_MSM7X00A_RESUME_IN_WQ -static void -do_resume_work(struct work_struct *work) -{ - struct msmsdcc_host *host = - container_of(work, struct msmsdcc_host, resume_task); - struct mmc_host *mmc = host->mmc; - - if (mmc) { - mmc_resume_host(mmc); - if (host->stat_irq) - enable_irq(host->stat_irq); - } -} -#endif - static int msmsdcc_probe(struct platform_device *pdev) { @@ -1145,15 +1129,6 @@ msmsdcc_probe(struct platform_device *pdev) host->dmares = dmares; spin_lock_init(&host->lock); -#ifdef CONFIG_MMC_EMBEDDED_SDIO - if (plat->embedded_sdio) - mmc_set_embedded_sdio_data(mmc, - &plat->embedded_sdio->cis, - &plat->embedded_sdio->cccr, - plat->embedded_sdio->funcs, - plat->embedded_sdio->num_funcs); -#endif - /* * Setup DMA */ diff --git a/drivers/mmc/host/msm_sdcc.h b/drivers/mmc/host/msm_sdcc.h index da0039c9285..d7dedcf33b5 100644 --- a/drivers/mmc/host/msm_sdcc.h +++ b/drivers/mmc/host/msm_sdcc.h @@ -235,10 +235,6 @@ struct msmsdcc_host { int cmdpoll; struct msmsdcc_stats stats; -#ifdef CONFIG_MMC_MSM7X00A_RESUME_IN_WQ - struct work_struct resume_task; -#endif - /* Command parameters */ unsigned int cmd_timeout; unsigned int cmd_pio_irqmask; diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index b032828c612..4a8776f8afd 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -28,6 +28,7 @@ #include <linux/clk.h> #include <linux/mmc/host.h> #include <linux/mmc/core.h> +#include <linux/mmc/mmc.h> #include <linux/io.h> #include <linux/semaphore.h> #include <linux/gpio.h> @@ -78,6 +79,7 @@ #define INT_EN_MASK 0x307F0033 #define BWR_ENABLE (1 << 4) #define BRR_ENABLE (1 << 5) +#define DTO_ENABLE (1 << 20) #define INIT_STREAM (1 << 1) #define DP_SELECT (1 << 21) #define DDIR (1 << 4) @@ -523,7 +525,8 @@ static void omap_hsmmc_stop_clock(struct omap_hsmmc_host *host) dev_dbg(mmc_dev(host->mmc), "MMC Clock is not stoped\n"); } -static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host) +static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host, + struct mmc_command *cmd) { unsigned int irq_mask; @@ -532,6 +535,10 @@ static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host) else irq_mask = INT_EN_MASK; + /* Disable timeout for erases */ + if (cmd->opcode == MMC_ERASE) + irq_mask &= ~DTO_ENABLE; + OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR); OMAP_HSMMC_WRITE(host->base, ISE, irq_mask); OMAP_HSMMC_WRITE(host->base, IE, irq_mask); @@ -782,7 +789,7 @@ omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd, mmc_hostname(host->mmc), cmd->opcode, cmd->arg); host->cmd = cmd; - omap_hsmmc_enable_irq(host); + omap_hsmmc_enable_irq(host, cmd); host->response_busy = 0; if (cmd->flags & MMC_RSP_PRESENT) { @@ -1273,8 +1280,11 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data) struct mmc_data *data = host->mrq->data; int dma_ch, req_in_progress; - if (ch_status & OMAP2_DMA_MISALIGNED_ERR_IRQ) - dev_dbg(mmc_dev(host->mmc), "MISALIGNED_ADRS_ERR\n"); + if (!(ch_status & OMAP_DMA_BLOCK_IRQ)) { + dev_warn(mmc_dev(host->mmc), "unexpected dma status %x\n", + ch_status); + return; + } spin_lock(&host->irq_lock); if (host->dma_ch < 0) { @@ -1598,6 +1608,14 @@ static int omap_hsmmc_get_ro(struct mmc_host *mmc) return mmc_slot(host).get_ro(host->dev, 0); } +static void omap_hsmmc_init_card(struct mmc_host *mmc, struct mmc_card *card) +{ + struct omap_hsmmc_host *host = mmc_priv(mmc); + + if (mmc_slot(host).init_card) + mmc_slot(host).init_card(card); +} + static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host) { u32 hctl, capa, value; @@ -1869,6 +1887,7 @@ static const struct mmc_host_ops omap_hsmmc_ops = { .set_ios = omap_hsmmc_set_ios, .get_cd = omap_hsmmc_get_cd, .get_ro = omap_hsmmc_get_ro, + .init_card = omap_hsmmc_init_card, /* NYET -- enable_sdio_irq */ }; @@ -1879,6 +1898,7 @@ static const struct mmc_host_ops omap_hsmmc_ps_ops = { .set_ios = omap_hsmmc_set_ios, .get_cd = omap_hsmmc_get_cd, .get_ro = omap_hsmmc_get_ro, + .init_card = omap_hsmmc_init_card, /* NYET -- enable_sdio_irq */ }; @@ -2094,12 +2114,25 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) mmc->max_seg_size = mmc->max_req_size; mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED | - MMC_CAP_WAIT_WHILE_BUSY; + MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_ERASE; - if (mmc_slot(host).wires >= 8) + switch (mmc_slot(host).wires) { + case 8: mmc->caps |= MMC_CAP_8_BIT_DATA; - else if (mmc_slot(host).wires >= 4) + /* Fall through */ + case 4: mmc->caps |= MMC_CAP_4_BIT_DATA; + break; + case 1: + /* Nothing to crib here */ + case 0: + /* Assuming nothing was given by board, Core use's 1-Bit */ + break; + default: + /* Completely unexpected.. Core goes with 1-Bit Width */ + dev_crit(mmc_dev(host->mmc), "Invalid width %d\n used!" + "using 1 instead\n", mmc_slot(host).wires); + } if (mmc_slot(host).nonremovable) mmc->caps |= MMC_CAP_NONREMOVABLE; diff --git a/drivers/mmc/host/sdhci-cns3xxx.c b/drivers/mmc/host/sdhci-cns3xxx.c new file mode 100644 index 00000000000..b7050b380d5 --- /dev/null +++ b/drivers/mmc/host/sdhci-cns3xxx.c @@ -0,0 +1,97 @@ +/* + * SDHCI support for CNS3xxx SoC + * + * Copyright 2008 Cavium Networks + * Copyright 2010 MontaVista Software, LLC. + * + * Authors: Scott Shu + * Anton Vorontsov <avorontsov@mvista.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/mmc/host.h> +#include <linux/sdhci-pltfm.h> +#include <mach/cns3xxx.h> +#include "sdhci.h" +#include "sdhci-pltfm.h" + +static unsigned int sdhci_cns3xxx_get_max_clk(struct sdhci_host *host) +{ + return 150000000; +} + +static void sdhci_cns3xxx_set_clock(struct sdhci_host *host, unsigned int clock) +{ + struct device *dev = mmc_dev(host->mmc); + int div = 1; + u16 clk; + unsigned long timeout; + + if (clock == host->clock) + return; + + sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); + + if (clock == 0) + goto out; + + while (host->max_clk / div > clock) { + /* + * On CNS3xxx divider grows linearly up to 4, and then + * exponentially up to 256. + */ + if (div < 4) + div += 1; + else if (div < 256) + div *= 2; + else + break; + } + + dev_dbg(dev, "desired SD clock: %d, actual: %d\n", + clock, host->max_clk / div); + + /* Divide by 3 is special. */ + if (div != 3) + div >>= 1; + + clk = div << SDHCI_DIVIDER_SHIFT; + clk |= SDHCI_CLOCK_INT_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + + timeout = 20; + while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL)) + & SDHCI_CLOCK_INT_STABLE)) { + if (timeout == 0) { + dev_warn(dev, "clock is unstable"); + break; + } + timeout--; + mdelay(1); + } + + clk |= SDHCI_CLOCK_CARD_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); +out: + host->clock = clock; +} + +static struct sdhci_ops sdhci_cns3xxx_ops = { + .get_max_clock = sdhci_cns3xxx_get_max_clk, + .set_clock = sdhci_cns3xxx_set_clock, +}; + +struct sdhci_pltfm_data sdhci_cns3xxx_pdata = { + .ops = &sdhci_cns3xxx_ops, + .quirks = SDHCI_QUIRK_BROKEN_DMA | + SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | + SDHCI_QUIRK_INVERTED_WRITE_PROTECT | + SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | + SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | + SDHCI_QUIRK_NONSTANDARD_CLOCK, +}; diff --git a/drivers/mmc/host/sdhci-of-core.c b/drivers/mmc/host/sdhci-of-core.c index a2e9820cd42..c51b71174c1 100644 --- a/drivers/mmc/host/sdhci-of-core.c +++ b/drivers/mmc/host/sdhci-of-core.c @@ -85,14 +85,14 @@ void sdhci_be32bs_writeb(struct sdhci_host *host, u8 val, int reg) #ifdef CONFIG_PM -static int sdhci_of_suspend(struct of_device *ofdev, pm_message_t state) +static int sdhci_of_suspend(struct platform_device *ofdev, pm_message_t state) { struct sdhci_host *host = dev_get_drvdata(&ofdev->dev); return mmc_suspend_host(host->mmc); } -static int sdhci_of_resume(struct of_device *ofdev) +static int sdhci_of_resume(struct platform_device *ofdev) { struct sdhci_host *host = dev_get_drvdata(&ofdev->dev); @@ -115,7 +115,7 @@ static bool __devinit sdhci_of_wp_inverted(struct device_node *np) return machine_is(mpc837x_rdb) || machine_is(mpc837x_mds); } -static int __devinit sdhci_of_probe(struct of_device *ofdev, +static int __devinit sdhci_of_probe(struct platform_device *ofdev, const struct of_device_id *match) { struct device_node *np = ofdev->dev.of_node; @@ -154,6 +154,10 @@ static int __devinit sdhci_of_probe(struct of_device *ofdev, host->ops = &sdhci_of_data->ops; } + if (of_get_property(np, "sdhci,auto-cmd12", NULL)) + host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12; + + if (of_get_property(np, "sdhci,1-bit-only", NULL)) host->quirks |= SDHCI_QUIRK_FORCE_1_BIT_DATA; @@ -179,7 +183,7 @@ err_addr_map: return ret; } -static int __devexit sdhci_of_remove(struct of_device *ofdev) +static int __devexit sdhci_of_remove(struct platform_device *ofdev) { struct sdhci_host *host = dev_get_drvdata(&ofdev->dev); diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 65483fdea45..e8aa99deae9 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -17,6 +17,7 @@ #include <linux/pci.h> #include <linux/dma-mapping.h> #include <linux/slab.h> +#include <linux/device.h> #include <linux/mmc/host.h> @@ -84,7 +85,30 @@ static int ricoh_probe(struct sdhci_pci_chip *chip) if (chip->pdev->subsystem_vendor == PCI_VENDOR_ID_SAMSUNG || chip->pdev->subsystem_vendor == PCI_VENDOR_ID_SONY) chip->quirks |= SDHCI_QUIRK_NO_CARD_NO_RESET; + return 0; +} + +static int ricoh_mmc_probe_slot(struct sdhci_pci_slot *slot) +{ + slot->host->caps = + ((0x21 << SDHCI_TIMEOUT_CLK_SHIFT) + & SDHCI_TIMEOUT_CLK_MASK) | + + ((0x21 << SDHCI_CLOCK_BASE_SHIFT) + & SDHCI_CLOCK_BASE_MASK) | + SDHCI_TIMEOUT_CLK_UNIT | + SDHCI_CAN_VDD_330 | + SDHCI_CAN_DO_SDMA; + return 0; +} + +static int ricoh_mmc_resume(struct sdhci_pci_chip *chip) +{ + /* Apply a delay to allow controller to settle */ + /* Otherwise it becomes confused if card state changed + during suspend */ + msleep(500); return 0; } @@ -95,6 +119,15 @@ static const struct sdhci_pci_fixes sdhci_ricoh = { SDHCI_QUIRK_CLOCK_BEFORE_RESET, }; +static const struct sdhci_pci_fixes sdhci_ricoh_mmc = { + .probe_slot = ricoh_mmc_probe_slot, + .resume = ricoh_mmc_resume, + .quirks = SDHCI_QUIRK_32BIT_DMA_ADDR | + SDHCI_QUIRK_CLOCK_BEFORE_RESET | + SDHCI_QUIRK_NO_CARD_NO_RESET | + SDHCI_QUIRK_MISSING_CAPS +}; + static const struct sdhci_pci_fixes sdhci_ene_712 = { .quirks = SDHCI_QUIRK_SINGLE_POWER_WRITE | SDHCI_QUIRK_BROKEN_DMA, @@ -374,6 +407,22 @@ static const struct pci_device_id pci_ids[] __devinitdata = { }, { + .vendor = PCI_VENDOR_ID_RICOH, + .device = 0x843, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_ricoh_mmc, + }, + + { + .vendor = PCI_VENDOR_ID_RICOH, + .device = 0xe822, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_ricoh_mmc, + }, + + { .vendor = PCI_VENDOR_ID_ENE, .device = PCI_DEVICE_ID_ENE_CB712_SD, .subvendor = PCI_ANY_ID, diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index b6ee0d71969..e045e3c61dd 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -24,6 +24,7 @@ #include <linux/delay.h> #include <linux/highmem.h> +#include <linux/mod_devicetable.h> #include <linux/platform_device.h> #include <linux/mmc/host.h> @@ -32,6 +33,7 @@ #include <linux/sdhci-pltfm.h> #include "sdhci.h" +#include "sdhci-pltfm.h" /*****************************************************************************\ * * @@ -51,10 +53,14 @@ static struct sdhci_ops sdhci_pltfm_ops = { static int __devinit sdhci_pltfm_probe(struct platform_device *pdev) { struct sdhci_pltfm_data *pdata = pdev->dev.platform_data; + const struct platform_device_id *platid = platform_get_device_id(pdev); struct sdhci_host *host; struct resource *iomem; int ret; + if (!pdata && platid && platid->driver_data) + pdata = (void *)platid->driver_data; + iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!iomem) { ret = -ENOMEM; @@ -150,6 +156,15 @@ static int __devexit sdhci_pltfm_remove(struct platform_device *pdev) return 0; } +static const struct platform_device_id sdhci_pltfm_ids[] = { + { "sdhci", }, +#ifdef CONFIG_MMC_SDHCI_CNS3XXX + { "sdhci-cns3xxx", (kernel_ulong_t)&sdhci_cns3xxx_pdata }, +#endif + { }, +}; +MODULE_DEVICE_TABLE(platform, sdhci_pltfm_ids); + static struct platform_driver sdhci_pltfm_driver = { .driver = { .name = "sdhci", @@ -157,6 +172,7 @@ static struct platform_driver sdhci_pltfm_driver = { }, .probe = sdhci_pltfm_probe, .remove = __devexit_p(sdhci_pltfm_remove), + .id_table = sdhci_pltfm_ids, }; /*****************************************************************************\ @@ -181,4 +197,3 @@ module_exit(sdhci_drv_exit); MODULE_DESCRIPTION("Secure Digital Host Controller Interface platform driver"); MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:sdhci"); diff --git a/drivers/mmc/host/sdhci-pltfm.h b/drivers/mmc/host/sdhci-pltfm.h new file mode 100644 index 00000000000..900f32902f7 --- /dev/null +++ b/drivers/mmc/host/sdhci-pltfm.h @@ -0,0 +1,18 @@ +/* + * Copyright 2010 MontaVista Software, LLC. + * + * Author: Anton Vorontsov <avorontsov@ru.mvista.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. + */ + +#ifndef _DRIVERS_MMC_SDHCI_PLTFM_H +#define _DRIVERS_MMC_SDHCI_PLTFM_H + +#include <linux/sdhci-pltfm.h> + +extern struct sdhci_pltfm_data sdhci_cns3xxx_pdata; + +#endif /* _DRIVERS_MMC_SDHCI_PLTFM_H */ diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index ad30f074ee1..0a7f2614c6f 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -18,6 +18,7 @@ #include <linux/slab.h> #include <linux/clk.h> #include <linux/io.h> +#include <linux/gpio.h> #include <linux/mmc/host.h> @@ -44,6 +45,8 @@ struct sdhci_s3c { struct resource *ioarea; struct s3c_sdhci_platdata *pdata; unsigned int cur_clk; + int ext_cd_irq; + int ext_cd_gpio; struct clk *clk_io; struct clk *clk_bus[MAX_BUS_CLK]; @@ -110,11 +113,6 @@ static unsigned int sdhci_s3c_get_max_clk(struct sdhci_host *host) return max; } -static unsigned int sdhci_s3c_get_timeout_clk(struct sdhci_host *host) -{ - return sdhci_s3c_get_max_clk(host) / 1000000; -} - /** * sdhci_s3c_consider_clock - consider one the bus clocks for current setting * @ourhost: Our SDHCI instance. @@ -188,7 +186,6 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock) ourhost->cur_clk = best_src; host->max_clk = clk_get_rate(clk); - host->timeout_clk = sdhci_s3c_get_timeout_clk(host); ctrl = readl(host->ioaddr + S3C_SDHCI_CONTROL2); ctrl &= ~S3C_SDHCI_CTRL2_SELBASECLK_MASK; @@ -209,12 +206,93 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock) } } +/** + * sdhci_s3c_get_min_clock - callback to get minimal supported clock value + * @host: The SDHCI host being queried + * + * To init mmc host properly a minimal clock value is needed. For high system + * bus clock's values the standard formula gives values out of allowed range. + * The clock still can be set to lower values, if clock source other then + * system bus is selected. +*/ +static unsigned int sdhci_s3c_get_min_clock(struct sdhci_host *host) +{ + struct sdhci_s3c *ourhost = to_s3c(host); + unsigned int delta, min = UINT_MAX; + int src; + + for (src = 0; src < MAX_BUS_CLK; src++) { + delta = sdhci_s3c_consider_clock(ourhost, src, 0); + if (delta == UINT_MAX) + continue; + /* delta is a negative value in this case */ + if (-delta < min) + min = -delta; + } + return min; +} + static struct sdhci_ops sdhci_s3c_ops = { .get_max_clock = sdhci_s3c_get_max_clk, - .get_timeout_clock = sdhci_s3c_get_timeout_clk, .set_clock = sdhci_s3c_set_clock, + .get_min_clock = sdhci_s3c_get_min_clock, }; +static void sdhci_s3c_notify_change(struct platform_device *dev, int state) +{ + struct sdhci_host *host = platform_get_drvdata(dev); + if (host) { + mutex_lock(&host->lock); + if (state) { + dev_dbg(&dev->dev, "card inserted.\n"); + host->flags &= ~SDHCI_DEVICE_DEAD; + host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; + } else { + dev_dbg(&dev->dev, "card removed.\n"); + host->flags |= SDHCI_DEVICE_DEAD; + host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION; + } + sdhci_card_detect(host); + mutex_unlock(&host->lock); + } +} + +static irqreturn_t sdhci_s3c_gpio_card_detect_thread(int irq, void *dev_id) +{ + struct sdhci_s3c *sc = dev_id; + int status = gpio_get_value(sc->ext_cd_gpio); + if (sc->pdata->ext_cd_gpio_invert) + status = !status; + sdhci_s3c_notify_change(sc->pdev, status); + return IRQ_HANDLED; +} + +static void sdhci_s3c_setup_card_detect_gpio(struct sdhci_s3c *sc) +{ + struct s3c_sdhci_platdata *pdata = sc->pdata; + struct device *dev = &sc->pdev->dev; + + if (gpio_request(pdata->ext_cd_gpio, "SDHCI EXT CD") == 0) { + sc->ext_cd_gpio = pdata->ext_cd_gpio; + sc->ext_cd_irq = gpio_to_irq(pdata->ext_cd_gpio); + if (sc->ext_cd_irq && + request_threaded_irq(sc->ext_cd_irq, NULL, + sdhci_s3c_gpio_card_detect_thread, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + dev_name(dev), sc) == 0) { + int status = gpio_get_value(sc->ext_cd_gpio); + if (pdata->ext_cd_gpio_invert) + status = !status; + sdhci_s3c_notify_change(sc->pdev, status); + } else { + dev_warn(dev, "cannot request irq for card detect\n"); + sc->ext_cd_irq = 0; + } + } else { + dev_err(dev, "cannot request gpio for card detect\n"); + } +} + static int __devinit sdhci_s3c_probe(struct platform_device *pdev) { struct s3c_sdhci_platdata *pdata = pdev->dev.platform_data; @@ -252,6 +330,7 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) sc->host = host; sc->pdev = pdev; sc->pdata = pdata; + sc->ext_cd_gpio = -1; /* invalid gpio number */ platform_set_drvdata(pdev, host); @@ -318,6 +397,7 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) /* Setup quirks for the controller */ host->quirks |= SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC; + host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT; #ifndef CONFIG_MMC_SDHCI_S3C_DMA @@ -332,15 +412,34 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) * SDHCI block, or a missing configuration that needs to be set. */ host->quirks |= SDHCI_QUIRK_NO_BUSY_IRQ; + if (pdata->cd_type == S3C_SDHCI_CD_NONE || + pdata->cd_type == S3C_SDHCI_CD_PERMANENT) + host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; + + if (pdata->cd_type == S3C_SDHCI_CD_PERMANENT) + host->mmc->caps = MMC_CAP_NONREMOVABLE; + host->quirks |= (SDHCI_QUIRK_32BIT_DMA_ADDR | SDHCI_QUIRK_32BIT_DMA_SIZE); + /* HSMMC on Samsung SoCs uses SDCLK as timeout clock */ + host->quirks |= SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK; + ret = sdhci_add_host(host); if (ret) { dev_err(dev, "sdhci_add_host() failed\n"); goto err_add_host; } + /* The following two methods of card detection might call + sdhci_s3c_notify_change() immediately, so they can be called + only after sdhci_add_host(). Setup errors are ignored. */ + if (pdata->cd_type == S3C_SDHCI_CD_EXTERNAL && pdata->ext_cd_init) + pdata->ext_cd_init(&sdhci_s3c_notify_change); + if (pdata->cd_type == S3C_SDHCI_CD_GPIO && + gpio_is_valid(pdata->ext_cd_gpio)) + sdhci_s3c_setup_card_detect_gpio(sc); + return 0; err_add_host: @@ -365,10 +464,20 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) static int __devexit sdhci_s3c_remove(struct platform_device *pdev) { + struct s3c_sdhci_platdata *pdata = pdev->dev.platform_data; struct sdhci_host *host = platform_get_drvdata(pdev); struct sdhci_s3c *sc = sdhci_priv(host); int ptr; + if (pdata->cd_type == S3C_SDHCI_CD_EXTERNAL && pdata->ext_cd_cleanup) + pdata->ext_cd_cleanup(&sdhci_s3c_notify_change); + + if (sc->ext_cd_irq) + free_irq(sc->ext_cd_irq, sc); + + if (gpio_is_valid(sc->ext_cd_gpio)) + gpio_free(sc->ext_cd_gpio); + sdhci_remove_host(host, 1); for (ptr = 0; ptr < 3; ptr++) { diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index c6d1bd8d4ac..785512133b5 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -19,6 +19,7 @@ #include <linux/dma-mapping.h> #include <linux/slab.h> #include <linux/scatterlist.h> +#include <linux/regulator/consumer.h> #include <linux/leds.h> @@ -817,8 +818,12 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host, WARN_ON(!host->data); mode = SDHCI_TRNS_BLK_CNT_EN; - if (data->blocks > 1) - mode |= SDHCI_TRNS_MULTI; + if (data->blocks > 1) { + if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12) + mode |= SDHCI_TRNS_MULTI | SDHCI_TRNS_ACMD12; + else + mode |= SDHCI_TRNS_MULTI; + } if (data->flags & MMC_DATA_READ) mode |= SDHCI_TRNS_READ; if (host->flags & SDHCI_REQ_USE_DMA) @@ -1108,6 +1113,12 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) #ifndef SDHCI_USE_LEDS_CLASS sdhci_activate_led(host); #endif + if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12) { + if (mrq->stop) { + mrq->data->stop = NULL; + mrq->stop = NULL; + } + } host->mrq = mrq; @@ -1159,6 +1170,11 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); + if (ios->bus_width == MMC_BUS_WIDTH_8) + ctrl |= SDHCI_CTRL_8BITBUS; + else + ctrl &= ~SDHCI_CTRL_8BITBUS; + if (ios->bus_width == MMC_BUS_WIDTH_4) ctrl |= SDHCI_CTRL_4BITBUS; else @@ -1603,7 +1619,10 @@ int sdhci_suspend_host(struct sdhci_host *host, pm_message_t state) free_irq(host->irq, host); - return 0; + if (host->vmmc) + ret = regulator_disable(host->vmmc); + + return ret; } EXPORT_SYMBOL_GPL(sdhci_suspend_host); @@ -1612,6 +1631,13 @@ int sdhci_resume_host(struct sdhci_host *host) { int ret; + if (host->vmmc) { + int ret = regulator_enable(host->vmmc); + if (ret) + return ret; + } + + if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { if (host->ops->enable_dma) host->ops->enable_dma(host); @@ -1687,7 +1713,8 @@ int sdhci_add_host(struct sdhci_host *host) host->version); } - caps = sdhci_readl(host, SDHCI_CAPABILITIES); + caps = (host->quirks & SDHCI_QUIRK_MISSING_CAPS) ? host->caps : + sdhci_readl(host, SDHCI_CAPABILITIES); if (host->quirks & SDHCI_QUIRK_FORCE_DMA) host->flags |= SDHCI_USE_SDMA; @@ -1785,13 +1812,12 @@ int sdhci_add_host(struct sdhci_host *host) * Set host parameters. */ mmc->ops = &sdhci_ops; - if (host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK && - host->ops->set_clock && host->ops->get_min_clock) + if (host->ops->get_min_clock) mmc->f_min = host->ops->get_min_clock(host); else mmc->f_min = host->max_clk / 256; mmc->f_max = host->max_clk; - mmc->caps = MMC_CAP_SDIO_IRQ; + mmc->caps |= MMC_CAP_SDIO_IRQ; if (!(host->quirks & SDHCI_QUIRK_FORCE_1_BIT_DATA)) mmc->caps |= MMC_CAP_4_BIT_DATA; @@ -1884,6 +1910,14 @@ int sdhci_add_host(struct sdhci_host *host) if (ret) goto untasklet; + host->vmmc = regulator_get(mmc_dev(mmc), "vmmc"); + if (IS_ERR(host->vmmc)) { + printk(KERN_INFO "%s: no vmmc regulator found\n", mmc_hostname(mmc)); + host->vmmc = NULL; + } else { + regulator_enable(host->vmmc); + } + sdhci_init(host, 0); #ifdef CONFIG_MMC_DEBUG @@ -1968,6 +2002,11 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) tasklet_kill(&host->card_tasklet); tasklet_kill(&host->finish_tasklet); + if (host->vmmc) { + regulator_disable(host->vmmc); + regulator_put(host->vmmc); + } + kfree(host->adma_desc); kfree(host->align_buffer); diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index c8468134adc..036cfae7636 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -72,6 +72,7 @@ #define SDHCI_CTRL_ADMA1 0x08 #define SDHCI_CTRL_ADMA32 0x10 #define SDHCI_CTRL_ADMA64 0x18 +#define SDHCI_CTRL_8BITBUS 0x20 #define SDHCI_POWER_CONTROL 0x29 #define SDHCI_POWER_ON 0x01 @@ -240,12 +241,18 @@ struct sdhci_host { #define SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN (1<<25) /* Controller cannot support End Attribute in NOP ADMA descriptor */ #define SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC (1<<26) +/* Controller is missing device caps. Use caps provided by host */ +#define SDHCI_QUIRK_MISSING_CAPS (1<<27) +/* Controller uses Auto CMD12 command to stop the transfer */ +#define SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12 (1<<28) int irq; /* Device IRQ */ void __iomem * ioaddr; /* Mapped address */ const struct sdhci_ops *ops; /* Low level hw interface */ + struct regulator *vmmc; /* Power regulator */ + /* Internal data */ struct mmc_host *mmc; /* MMC structure */ u64 dma_mask; /* custom DMA mask */ @@ -292,6 +299,8 @@ struct sdhci_host { struct timer_list timer; /* Timer for timeouts */ + unsigned int caps; /* Alternative capabilities */ + unsigned long private[0] ____cacheline_aligned; }; @@ -407,6 +416,7 @@ static inline void *sdhci_priv(struct sdhci_host *host) return (void *)host->private; } +extern void sdhci_card_detect(struct sdhci_host *host); extern int sdhci_add_host(struct sdhci_host *host); extern void sdhci_remove_host(struct sdhci_host *host, int dead); diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c index 6ac5f9f28ac..00af55d7afb 100644 --- a/drivers/mtd/maps/physmap_of.c +++ b/drivers/mtd/maps/physmap_of.c @@ -43,7 +43,7 @@ struct of_flash { #ifdef CONFIG_MTD_PARTITIONS #define OF_FLASH_PARTS(info) ((info)->parts) -static int parse_obsolete_partitions(struct of_device *dev, +static int parse_obsolete_partitions(struct platform_device *dev, struct of_flash *info, struct device_node *dp) { @@ -93,7 +93,7 @@ static int parse_obsolete_partitions(struct of_device *dev, #define parse_partitions(info, dev) (0) #endif /* MTD_PARTITIONS */ -static int of_flash_remove(struct of_device *dev) +static int of_flash_remove(struct platform_device *dev) { struct of_flash *info; int i; @@ -140,7 +140,7 @@ static int of_flash_remove(struct of_device *dev) /* Helper function to handle probing of the obsolete "direct-mapped" * compatible binding, which has an extra "probe-type" property * describing the type of flash probe necessary. */ -static struct mtd_info * __devinit obsolete_probe(struct of_device *dev, +static struct mtd_info * __devinit obsolete_probe(struct platform_device *dev, struct map_info *map) { struct device_node *dp = dev->dev.of_node; @@ -215,7 +215,7 @@ static void __devinit of_free_probes(const char **probes) } #endif -static int __devinit of_flash_probe(struct of_device *dev, +static int __devinit of_flash_probe(struct platform_device *dev, const struct of_device_id *match) { #ifdef CONFIG_MTD_PARTITIONS diff --git a/drivers/mtd/maps/sun_uflash.c b/drivers/mtd/maps/sun_uflash.c index 8984236a8d0..3582ba1f9b0 100644 --- a/drivers/mtd/maps/sun_uflash.c +++ b/drivers/mtd/maps/sun_uflash.c @@ -48,7 +48,7 @@ struct map_info uflash_map_templ = { .bankwidth = UFLASH_BUSWIDTH, }; -int uflash_devinit(struct of_device *op, struct device_node *dp) +int uflash_devinit(struct platform_device *op, struct device_node *dp) { struct uflash_dev *up; @@ -108,7 +108,7 @@ int uflash_devinit(struct of_device *op, struct device_node *dp) return 0; } -static int __devinit uflash_probe(struct of_device *op, const struct of_device_id *match) +static int __devinit uflash_probe(struct platform_device *op, const struct of_device_id *match) { struct device_node *dp = op->dev.of_node; @@ -121,7 +121,7 @@ static int __devinit uflash_probe(struct of_device *op, const struct of_device_i return uflash_devinit(op, dp); } -static int __devexit uflash_remove(struct of_device *op) +static int __devexit uflash_remove(struct platform_device *op) { struct uflash_dev *up = dev_get_drvdata(&op->dev); diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index 5084cc51794..80de0bff6c3 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -958,7 +958,7 @@ static int __devinit fsl_elbc_ctrl_init(struct fsl_elbc_ctrl *ctrl) return 0; } -static int fsl_elbc_ctrl_remove(struct of_device *ofdev) +static int fsl_elbc_ctrl_remove(struct platform_device *ofdev) { struct fsl_elbc_ctrl *ctrl = dev_get_drvdata(&ofdev->dev); int i; @@ -1013,7 +1013,7 @@ static irqreturn_t fsl_elbc_ctrl_irq(int irqno, void *data) * in the chip probe function. */ -static int __devinit fsl_elbc_ctrl_probe(struct of_device *ofdev, +static int __devinit fsl_elbc_ctrl_probe(struct platform_device *ofdev, const struct of_device_id *match) { struct device_node *child; diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c index 1312eda57ba..4eff8b25e5a 100644 --- a/drivers/mtd/nand/fsl_upm.c +++ b/drivers/mtd/nand/fsl_upm.c @@ -217,7 +217,7 @@ err: return ret; } -static int __devinit fun_probe(struct of_device *ofdev, +static int __devinit fun_probe(struct platform_device *ofdev, const struct of_device_id *ofid) { struct fsl_upm_nand *fun; @@ -335,7 +335,7 @@ err1: return ret; } -static int __devexit fun_remove(struct of_device *ofdev) +static int __devexit fun_remove(struct platform_device *ofdev) { struct fsl_upm_nand *fun = dev_get_drvdata(&ofdev->dev); int i; diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nfc.c index 0a130dcaa12..df0c1da4ff4 100644 --- a/drivers/mtd/nand/mpc5121_nfc.c +++ b/drivers/mtd/nand/mpc5121_nfc.c @@ -647,7 +647,7 @@ static void mpc5121_nfc_free(struct device *dev, struct mtd_info *mtd) iounmap(prv->csreg); } -static int __devinit mpc5121_nfc_probe(struct of_device *op, +static int __devinit mpc5121_nfc_probe(struct platform_device *op, const struct of_device_id *match) { struct device_node *rootnode, *dn = op->dev.of_node; @@ -869,7 +869,7 @@ error: return retval; } -static int __devexit mpc5121_nfc_remove(struct of_device *op) +static int __devexit mpc5121_nfc_remove(struct platform_device *op) { struct device *dev = &op->dev; struct mtd_info *mtd = dev_get_drvdata(dev); diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 16a1714df00..a3c7473dd40 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -889,17 +889,17 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) } /** - * __nand_unlock - [REPLACABLE] unlocks specified locked blockes + * __nand_unlock - [REPLACEABLE] unlocks specified locked blocks * - * @param mtd - mtd info - * @param ofs - offset to start unlock from - * @param len - length to unlock - * @invert - when = 0, unlock the range of blocks within the lower and + * @mtd: mtd info + * @ofs: offset to start unlock from + * @len: length to unlock + * @invert: when = 0, unlock the range of blocks within the lower and * upper boundary address - * whne = 1, unlock the range of blocks outside the boundaries + * when = 1, unlock the range of blocks outside the boundaries * of the lower and upper boundary address * - * @return - unlock status + * return - unlock status */ static int __nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len, int invert) @@ -931,13 +931,13 @@ static int __nand_unlock(struct mtd_info *mtd, loff_t ofs, } /** - * nand_unlock - [REPLACABLE] unlocks specified locked blockes + * nand_unlock - [REPLACEABLE] unlocks specified locked blocks * - * @param mtd - mtd info - * @param ofs - offset to start unlock from - * @param len - length to unlock + * @mtd: mtd info + * @ofs: offset to start unlock from + * @len: length to unlock * - * @return - unlock status + * return - unlock status */ int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) { @@ -982,16 +982,16 @@ out: } /** - * nand_lock - [REPLACABLE] locks all blockes present in the device + * nand_lock - [REPLACEABLE] locks all blocks present in the device * - * @param mtd - mtd info - * @param ofs - offset to start unlock from - * @param len - length to unlock + * @mtd: mtd info + * @ofs: offset to start unlock from + * @len: length to unlock * - * @return - lock status + * return - lock status * - * This feature is not support in many NAND parts. 'Micron' NAND parts - * do have this feature, but it allows only to lock all blocks not for + * This feature is not supported in many NAND parts. 'Micron' NAND parts + * do have this feature, but it allows only to lock all blocks, not for * specified range for block. * * Implementing 'lock' feature by making use of 'unlock', for now. @@ -2093,6 +2093,7 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, * nand_fill_oob - [Internal] Transfer client buffer to oob * @chip: nand chip structure * @oob: oob data buffer + * @len: oob data write length * @ops: oob ops structure */ static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, size_t len, diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c index 98fd2bdf8be..510554e6c11 100644 --- a/drivers/mtd/nand/ndfc.c +++ b/drivers/mtd/nand/ndfc.c @@ -35,7 +35,7 @@ struct ndfc_controller { - struct of_device *ofdev; + struct platform_device *ofdev; void __iomem *ndfcbase; struct mtd_info mtd; struct nand_chip chip; @@ -225,7 +225,7 @@ err: return ret; } -static int __devinit ndfc_probe(struct of_device *ofdev, +static int __devinit ndfc_probe(struct platform_device *ofdev, const struct of_device_id *match) { struct ndfc_controller *ndfc = &ndfc_ctrl; @@ -277,7 +277,7 @@ static int __devinit ndfc_probe(struct of_device *ofdev, return 0; } -static int __devexit ndfc_remove(struct of_device *ofdev) +static int __devexit ndfc_remove(struct platform_device *ofdev) { struct ndfc_controller *ndfc = dev_get_drvdata(&ofdev->dev); diff --git a/drivers/mtd/nand/pasemi_nand.c b/drivers/mtd/nand/pasemi_nand.c index f02af24d033..6ddb2461d74 100644 --- a/drivers/mtd/nand/pasemi_nand.c +++ b/drivers/mtd/nand/pasemi_nand.c @@ -89,7 +89,7 @@ int pasemi_device_ready(struct mtd_info *mtd) return !!(inl(lpcctl) & LBICTRL_LPCCTL_NR); } -static int __devinit pasemi_nand_probe(struct of_device *ofdev, +static int __devinit pasemi_nand_probe(struct platform_device *ofdev, const struct of_device_id *match) { struct pci_dev *pdev; @@ -185,7 +185,7 @@ static int __devinit pasemi_nand_probe(struct of_device *ofdev, return err; } -static int __devexit pasemi_nand_remove(struct of_device *ofdev) +static int __devexit pasemi_nand_remove(struct platform_device *ofdev) { struct nand_chip *chip; diff --git a/drivers/mtd/nand/socrates_nand.c b/drivers/mtd/nand/socrates_nand.c index cc728b12de8..a8e403eebed 100644 --- a/drivers/mtd/nand/socrates_nand.c +++ b/drivers/mtd/nand/socrates_nand.c @@ -162,7 +162,7 @@ static const char *part_probes[] = { "cmdlinepart", NULL }; /* * Probe for the NAND device. */ -static int __devinit socrates_nand_probe(struct of_device *ofdev, +static int __devinit socrates_nand_probe(struct platform_device *ofdev, const struct of_device_id *ofid) { struct socrates_nand_host *host; @@ -276,7 +276,7 @@ out: /* * Remove a NAND device. */ -static int __devexit socrates_nand_remove(struct of_device *ofdev) +static int __devexit socrates_nand_remove(struct platform_device *ofdev) { struct socrates_nand_host *host = dev_get_drvdata(&ofdev->dev); struct mtd_info *mtd = &host->mtd; diff --git a/drivers/net/arm/ixp4xx_eth.c b/drivers/net/arm/ixp4xx_eth.c index 4f1cc7164ad..6028226a727 100644 --- a/drivers/net/arm/ixp4xx_eth.c +++ b/drivers/net/arm/ixp4xx_eth.c @@ -241,7 +241,7 @@ static inline void memcpy_swab32(u32 *dest, u32 *src, int cnt) static spinlock_t mdio_lock; static struct eth_regs __iomem *mdio_regs; /* mdio command and status only */ -struct mii_bus *mdio_bus; +static struct mii_bus *mdio_bus; static int ports_open; static struct port *npe_port_tab[MAX_NPES]; static struct dma_pool *dma_pool; diff --git a/drivers/net/can/mscan/mpc5xxx_can.c b/drivers/net/can/mscan/mpc5xxx_can.c index af753936e83..b1bdc909090 100644 --- a/drivers/net/can/mscan/mpc5xxx_can.c +++ b/drivers/net/can/mscan/mpc5xxx_can.c @@ -38,7 +38,7 @@ struct mpc5xxx_can_data { unsigned int type; - u32 (*get_clock)(struct of_device *ofdev, const char *clock_name, + u32 (*get_clock)(struct platform_device *ofdev, const char *clock_name, int *mscan_clksrc); }; @@ -48,7 +48,7 @@ static struct of_device_id __devinitdata mpc52xx_cdm_ids[] = { {} }; -static u32 __devinit mpc52xx_can_get_clock(struct of_device *ofdev, +static u32 __devinit mpc52xx_can_get_clock(struct platform_device *ofdev, const char *clock_name, int *mscan_clksrc) { @@ -101,7 +101,7 @@ static u32 __devinit mpc52xx_can_get_clock(struct of_device *ofdev, return freq; } #else /* !CONFIG_PPC_MPC52xx */ -static u32 __devinit mpc52xx_can_get_clock(struct of_device *ofdev, +static u32 __devinit mpc52xx_can_get_clock(struct platform_device *ofdev, const char *clock_name, int *mscan_clksrc) { @@ -129,7 +129,7 @@ static struct of_device_id __devinitdata mpc512x_clock_ids[] = { {} }; -static u32 __devinit mpc512x_can_get_clock(struct of_device *ofdev, +static u32 __devinit mpc512x_can_get_clock(struct platform_device *ofdev, const char *clock_name, int *mscan_clksrc) { @@ -239,7 +239,7 @@ exit_unmap: return freq; } #else /* !CONFIG_PPC_MPC512x */ -static u32 __devinit mpc512x_can_get_clock(struct of_device *ofdev, +static u32 __devinit mpc512x_can_get_clock(struct platform_device *ofdev, const char *clock_name, int *mscan_clksrc) { @@ -247,7 +247,7 @@ static u32 __devinit mpc512x_can_get_clock(struct of_device *ofdev, } #endif /* CONFIG_PPC_MPC512x */ -static int __devinit mpc5xxx_can_probe(struct of_device *ofdev, +static int __devinit mpc5xxx_can_probe(struct platform_device *ofdev, const struct of_device_id *id) { struct mpc5xxx_can_data *data = (struct mpc5xxx_can_data *)id->data; @@ -317,7 +317,7 @@ exit_unmap_mem: return err; } -static int __devexit mpc5xxx_can_remove(struct of_device *ofdev) +static int __devexit mpc5xxx_can_remove(struct platform_device *ofdev) { struct net_device *dev = dev_get_drvdata(&ofdev->dev); struct mscan_priv *priv = netdev_priv(dev); @@ -334,7 +334,7 @@ static int __devexit mpc5xxx_can_remove(struct of_device *ofdev) #ifdef CONFIG_PM static struct mscan_regs saved_regs; -static int mpc5xxx_can_suspend(struct of_device *ofdev, pm_message_t state) +static int mpc5xxx_can_suspend(struct platform_device *ofdev, pm_message_t state) { struct net_device *dev = dev_get_drvdata(&ofdev->dev); struct mscan_priv *priv = netdev_priv(dev); @@ -345,7 +345,7 @@ static int mpc5xxx_can_suspend(struct of_device *ofdev, pm_message_t state) return 0; } -static int mpc5xxx_can_resume(struct of_device *ofdev) +static int mpc5xxx_can_resume(struct platform_device *ofdev) { struct net_device *dev = dev_get_drvdata(&ofdev->dev); struct mscan_priv *priv = netdev_priv(dev); diff --git a/drivers/net/can/sja1000/sja1000_of_platform.c b/drivers/net/can/sja1000/sja1000_of_platform.c index ac1a83d7c20..5bfccfdf3bb 100644 --- a/drivers/net/can/sja1000/sja1000_of_platform.c +++ b/drivers/net/can/sja1000/sja1000_of_platform.c @@ -67,7 +67,7 @@ static void sja1000_ofp_write_reg(const struct sja1000_priv *priv, out_8(priv->reg_base + reg, val); } -static int __devexit sja1000_ofp_remove(struct of_device *ofdev) +static int __devexit sja1000_ofp_remove(struct platform_device *ofdev) { struct net_device *dev = dev_get_drvdata(&ofdev->dev); struct sja1000_priv *priv = netdev_priv(dev); @@ -87,7 +87,7 @@ static int __devexit sja1000_ofp_remove(struct of_device *ofdev) return 0; } -static int __devinit sja1000_ofp_probe(struct of_device *ofdev, +static int __devinit sja1000_ofp_probe(struct platform_device *ofdev, const struct of_device_id *id) { struct device_node *np = ofdev->dev.of_node; diff --git a/drivers/net/ehea/ehea.h b/drivers/net/ehea/ehea.h index 0060e422f17..99a929964e3 100644 --- a/drivers/net/ehea/ehea.h +++ b/drivers/net/ehea/ehea.h @@ -413,7 +413,7 @@ struct ehea_port_res { struct ehea_adapter { u64 handle; - struct of_device *ofdev; + struct platform_device *ofdev; struct ehea_port *port[EHEA_MAX_PORTS]; struct ehea_eq *neq; /* notification event queue */ struct tasklet_struct neq_tasklet; @@ -465,7 +465,7 @@ struct ehea_port { struct net_device *netdev; struct net_device_stats stats; struct ehea_port_res port_res[EHEA_MAX_PORT_RES]; - struct of_device ofdev; /* Open Firmware Device */ + struct platform_device ofdev; /* Open Firmware Device */ struct ehea_mc_list *mc_list; /* Multicast MAC addresses */ struct vlan_group *vgrp; struct ehea_eq *qp_eq; diff --git a/drivers/net/ehea/ehea_main.c b/drivers/net/ehea/ehea_main.c index 3beba70b7de..897719b49f9 100644 --- a/drivers/net/ehea/ehea_main.c +++ b/drivers/net/ehea/ehea_main.c @@ -107,10 +107,10 @@ struct ehea_fw_handle_array ehea_fw_handles; struct ehea_bcmc_reg_array ehea_bcmc_regs; -static int __devinit ehea_probe_adapter(struct of_device *dev, +static int __devinit ehea_probe_adapter(struct platform_device *dev, const struct of_device_id *id); -static int __devexit ehea_remove(struct of_device *dev); +static int __devexit ehea_remove(struct platform_device *dev); static struct of_device_id ehea_device_table[] = { { @@ -3376,7 +3376,7 @@ static ssize_t ehea_remove_port(struct device *dev, static DEVICE_ATTR(probe_port, S_IWUSR, NULL, ehea_probe_port); static DEVICE_ATTR(remove_port, S_IWUSR, NULL, ehea_remove_port); -int ehea_create_device_sysfs(struct of_device *dev) +int ehea_create_device_sysfs(struct platform_device *dev) { int ret = device_create_file(&dev->dev, &dev_attr_probe_port); if (ret) @@ -3387,13 +3387,13 @@ out: return ret; } -void ehea_remove_device_sysfs(struct of_device *dev) +void ehea_remove_device_sysfs(struct platform_device *dev) { device_remove_file(&dev->dev, &dev_attr_probe_port); device_remove_file(&dev->dev, &dev_attr_remove_port); } -static int __devinit ehea_probe_adapter(struct of_device *dev, +static int __devinit ehea_probe_adapter(struct platform_device *dev, const struct of_device_id *id) { struct ehea_adapter *adapter; @@ -3492,7 +3492,7 @@ out: return ret; } -static int __devexit ehea_remove(struct of_device *dev) +static int __devexit ehea_remove(struct platform_device *dev) { struct ehea_adapter *adapter = dev_get_drvdata(&dev->dev); int i; diff --git a/drivers/net/fec_mpc52xx.c b/drivers/net/fec_mpc52xx.c index d1a5b17b2a9..e3e10b4add9 100644 --- a/drivers/net/fec_mpc52xx.c +++ b/drivers/net/fec_mpc52xx.c @@ -850,7 +850,7 @@ static const struct net_device_ops mpc52xx_fec_netdev_ops = { /* ======================================================================== */ static int __devinit -mpc52xx_fec_probe(struct of_device *op, const struct of_device_id *match) +mpc52xx_fec_probe(struct platform_device *op, const struct of_device_id *match) { int rv; struct net_device *ndev; @@ -995,7 +995,7 @@ err_netdev: } static int -mpc52xx_fec_remove(struct of_device *op) +mpc52xx_fec_remove(struct platform_device *op) { struct net_device *ndev; struct mpc52xx_fec_priv *priv; @@ -1025,7 +1025,7 @@ mpc52xx_fec_remove(struct of_device *op) } #ifdef CONFIG_PM -static int mpc52xx_fec_of_suspend(struct of_device *op, pm_message_t state) +static int mpc52xx_fec_of_suspend(struct platform_device *op, pm_message_t state) { struct net_device *dev = dev_get_drvdata(&op->dev); @@ -1035,7 +1035,7 @@ static int mpc52xx_fec_of_suspend(struct of_device *op, pm_message_t state) return 0; } -static int mpc52xx_fec_of_resume(struct of_device *op) +static int mpc52xx_fec_of_resume(struct platform_device *op) { struct net_device *dev = dev_get_drvdata(&op->dev); diff --git a/drivers/net/fec_mpc52xx_phy.c b/drivers/net/fec_mpc52xx_phy.c index dbaf72cbb23..0b4cb6f1598 100644 --- a/drivers/net/fec_mpc52xx_phy.c +++ b/drivers/net/fec_mpc52xx_phy.c @@ -61,7 +61,7 @@ static int mpc52xx_fec_mdio_write(struct mii_bus *bus, int phy_id, int reg, data | FEC_MII_WRITE_FRAME); } -static int mpc52xx_fec_mdio_probe(struct of_device *of, +static int mpc52xx_fec_mdio_probe(struct platform_device *of, const struct of_device_id *match) { struct device *dev = &of->dev; @@ -122,7 +122,7 @@ static int mpc52xx_fec_mdio_probe(struct of_device *of, return err; } -static int mpc52xx_fec_mdio_remove(struct of_device *of) +static int mpc52xx_fec_mdio_remove(struct platform_device *of) { struct device *dev = &of->dev; struct mii_bus *bus = dev_get_drvdata(dev); diff --git a/drivers/net/fs_enet/fs_enet-main.c b/drivers/net/fs_enet/fs_enet-main.c index f08cff9020b..d6e3111959a 100644 --- a/drivers/net/fs_enet/fs_enet-main.c +++ b/drivers/net/fs_enet/fs_enet-main.c @@ -997,7 +997,7 @@ static const struct net_device_ops fs_enet_netdev_ops = { #endif }; -static int __devinit fs_enet_probe(struct of_device *ofdev, +static int __devinit fs_enet_probe(struct platform_device *ofdev, const struct of_device_id *match) { struct net_device *ndev; @@ -1105,7 +1105,7 @@ out_free_fpi: return ret; } -static int fs_enet_remove(struct of_device *ofdev) +static int fs_enet_remove(struct platform_device *ofdev) { struct net_device *ndev = dev_get_drvdata(&ofdev->dev); struct fs_enet_private *fep = netdev_priv(ndev); diff --git a/drivers/net/fs_enet/mac-fcc.c b/drivers/net/fs_enet/mac-fcc.c index 48e91b6242c..7a84e45487e 100644 --- a/drivers/net/fs_enet/mac-fcc.c +++ b/drivers/net/fs_enet/mac-fcc.c @@ -84,7 +84,7 @@ static inline int fcc_cr_cmd(struct fs_enet_private *fep, u32 op) static int do_pd_setup(struct fs_enet_private *fep) { - struct of_device *ofdev = to_of_device(fep->dev); + struct platform_device *ofdev = to_platform_device(fep->dev); struct fs_platform_info *fpi = fep->fpi; int ret = -EINVAL; diff --git a/drivers/net/fs_enet/mac-fec.c b/drivers/net/fs_enet/mac-fec.c index 7ca1642276d..61035fc5599 100644 --- a/drivers/net/fs_enet/mac-fec.c +++ b/drivers/net/fs_enet/mac-fec.c @@ -96,7 +96,7 @@ static int whack_reset(struct fec __iomem *fecp) static int do_pd_setup(struct fs_enet_private *fep) { - struct of_device *ofdev = to_of_device(fep->dev); + struct platform_device *ofdev = to_platform_device(fep->dev); fep->interrupt = of_irq_to_resource(ofdev->dev.of_node, 0, NULL); if (fep->interrupt == NO_IRQ) diff --git a/drivers/net/fs_enet/mac-scc.c b/drivers/net/fs_enet/mac-scc.c index a3c44544846..22a02a76706 100644 --- a/drivers/net/fs_enet/mac-scc.c +++ b/drivers/net/fs_enet/mac-scc.c @@ -96,7 +96,7 @@ static inline int scc_cr_cmd(struct fs_enet_private *fep, u32 op) static int do_pd_setup(struct fs_enet_private *fep) { - struct of_device *ofdev = to_of_device(fep->dev); + struct platform_device *ofdev = to_platform_device(fep->dev); fep->interrupt = of_irq_to_resource(ofdev->dev.of_node, 0, NULL); if (fep->interrupt == NO_IRQ) diff --git a/drivers/net/fs_enet/mii-bitbang.c b/drivers/net/fs_enet/mii-bitbang.c index 3607340f3da..3cda2b51547 100644 --- a/drivers/net/fs_enet/mii-bitbang.c +++ b/drivers/net/fs_enet/mii-bitbang.c @@ -150,7 +150,7 @@ static int __devinit fs_mii_bitbang_init(struct mii_bus *bus, return 0; } -static int __devinit fs_enet_mdio_probe(struct of_device *ofdev, +static int __devinit fs_enet_mdio_probe(struct platform_device *ofdev, const struct of_device_id *match) { struct mii_bus *new_bus; @@ -200,7 +200,7 @@ out: return ret; } -static int fs_enet_mdio_remove(struct of_device *ofdev) +static int fs_enet_mdio_remove(struct platform_device *ofdev) { struct mii_bus *bus = dev_get_drvdata(&ofdev->dev); struct bb_info *bitbang = bus->priv; diff --git a/drivers/net/fs_enet/mii-fec.c b/drivers/net/fs_enet/mii-fec.c index bddffd169b9..dbb9c48623d 100644 --- a/drivers/net/fs_enet/mii-fec.c +++ b/drivers/net/fs_enet/mii-fec.c @@ -101,7 +101,7 @@ static int fs_enet_fec_mii_reset(struct mii_bus *bus) return 0; } -static int __devinit fs_enet_mdio_probe(struct of_device *ofdev, +static int __devinit fs_enet_mdio_probe(struct platform_device *ofdev, const struct of_device_id *match) { struct resource res; @@ -192,7 +192,7 @@ out: return ret; } -static int fs_enet_mdio_remove(struct of_device *ofdev) +static int fs_enet_mdio_remove(struct platform_device *ofdev) { struct mii_bus *bus = dev_get_drvdata(&ofdev->dev); struct fec_info *fec = bus->priv; diff --git a/drivers/net/fsl_pq_mdio.c b/drivers/net/fsl_pq_mdio.c index f53f850b641..d4bf91aac25 100644 --- a/drivers/net/fsl_pq_mdio.c +++ b/drivers/net/fsl_pq_mdio.c @@ -265,7 +265,7 @@ static int get_ucc_id_for_range(u64 start, u64 end, u32 *ucc_id) #endif -static int fsl_pq_mdio_probe(struct of_device *ofdev, +static int fsl_pq_mdio_probe(struct platform_device *ofdev, const struct of_device_id *match) { struct device_node *np = ofdev->dev.of_node; @@ -425,7 +425,7 @@ err_free_priv: } -static int fsl_pq_mdio_remove(struct of_device *ofdev) +static int fsl_pq_mdio_remove(struct platform_device *ofdev) { struct device *device = &ofdev->dev; struct mii_bus *bus = dev_get_drvdata(device); diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c index a1b6301bc67..4f7c3f3ca23 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c @@ -122,9 +122,9 @@ static irqreturn_t gfar_interrupt(int irq, void *dev_id); static void adjust_link(struct net_device *dev); static void init_registers(struct net_device *dev); static int init_phy(struct net_device *dev); -static int gfar_probe(struct of_device *ofdev, +static int gfar_probe(struct platform_device *ofdev, const struct of_device_id *match); -static int gfar_remove(struct of_device *ofdev); +static int gfar_remove(struct platform_device *ofdev); static void free_skb_resources(struct gfar_private *priv); static void gfar_set_multi(struct net_device *dev); static void gfar_set_hash_for_addr(struct net_device *dev, u8 *addr); @@ -605,7 +605,7 @@ static int gfar_parse_group(struct device_node *np, return 0; } -static int gfar_of_init(struct of_device *ofdev, struct net_device **pdev) +static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev) { const char *model; const char *ctype; @@ -959,7 +959,7 @@ static void gfar_detect_errata(struct gfar_private *priv) /* Set up the ethernet device structure, private data, * and anything else we need before we start */ -static int gfar_probe(struct of_device *ofdev, +static int gfar_probe(struct platform_device *ofdev, const struct of_device_id *match) { u32 tempval; @@ -1238,7 +1238,7 @@ register_fail: return err; } -static int gfar_remove(struct of_device *ofdev) +static int gfar_remove(struct platform_device *ofdev) { struct gfar_private *priv = dev_get_drvdata(&ofdev->dev); diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h index 710810e2adb..68984eb88ae 100644 --- a/drivers/net/gianfar.h +++ b/drivers/net/gianfar.h @@ -1054,7 +1054,7 @@ struct gfar_private { struct device_node *node; struct net_device *ndev; - struct of_device *ofdev; + struct platform_device *ofdev; enum gfar_errata errata; struct gfar_priv_grp gfargrp[MAXGROUPS]; diff --git a/drivers/net/greth.c b/drivers/net/greth.c index 4d09eab3548..f15c64f1cd3 100644 --- a/drivers/net/greth.c +++ b/drivers/net/greth.c @@ -1373,7 +1373,7 @@ error: } /* Initialize the GRETH MAC */ -static int __devinit greth_of_probe(struct of_device *ofdev, const struct of_device_id *match) +static int __devinit greth_of_probe(struct platform_device *ofdev, const struct of_device_id *match) { struct net_device *dev; struct greth_private *greth; @@ -1412,7 +1412,7 @@ static int __devinit greth_of_probe(struct of_device *ofdev, const struct of_dev } regs = (struct greth_regs *) greth->regs; - greth->irq = ofdev->irqs[0]; + greth->irq = ofdev->archdata.irqs[0]; dev_set_drvdata(greth->dev, dev); SET_NETDEV_DEV(dev, greth->dev); @@ -1572,7 +1572,7 @@ error1: return err; } -static int __devexit greth_of_remove(struct of_device *of_dev) +static int __devexit greth_of_remove(struct platform_device *of_dev) { struct net_device *ndev = dev_get_drvdata(&of_dev->dev); struct greth_private *greth = netdev_priv(ndev); diff --git a/drivers/net/greth.h b/drivers/net/greth.h index 973388d6abc..03ad903cd67 100644 --- a/drivers/net/greth.h +++ b/drivers/net/greth.h @@ -118,7 +118,7 @@ struct greth_private { int irq; - struct device *dev; /* Pointer to of_device->dev */ + struct device *dev; /* Pointer to platform_device->dev */ struct net_device *netdev; struct napi_struct napi; spinlock_t devlock; diff --git a/drivers/net/ibm_newemac/core.c b/drivers/net/ibm_newemac/core.c index eeec7bc2ce7..3506fd6ad72 100644 --- a/drivers/net/ibm_newemac/core.c +++ b/drivers/net/ibm_newemac/core.c @@ -2245,7 +2245,7 @@ static int emac_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) struct emac_depentry { u32 phandle; struct device_node *node; - struct of_device *ofdev; + struct platform_device *ofdev; void *drvdata; }; @@ -2719,7 +2719,7 @@ static const struct net_device_ops emac_gige_netdev_ops = { .ndo_change_mtu = emac_change_mtu, }; -static int __devinit emac_probe(struct of_device *ofdev, +static int __devinit emac_probe(struct platform_device *ofdev, const struct of_device_id *match) { struct net_device *ndev; @@ -2941,7 +2941,7 @@ static int __devinit emac_probe(struct of_device *ofdev, return err; } -static int __devexit emac_remove(struct of_device *ofdev) +static int __devexit emac_remove(struct platform_device *ofdev) { struct emac_instance *dev = dev_get_drvdata(&ofdev->dev); diff --git a/drivers/net/ibm_newemac/core.h b/drivers/net/ibm_newemac/core.h index b1cbe6fdfc7..9e37e3d9c51 100644 --- a/drivers/net/ibm_newemac/core.h +++ b/drivers/net/ibm_newemac/core.h @@ -170,12 +170,12 @@ struct emac_instance { struct net_device *ndev; struct resource rsrc_regs; struct emac_regs __iomem *emacp; - struct of_device *ofdev; + struct platform_device *ofdev; struct device_node **blist; /* bootlist entry */ /* MAL linkage */ u32 mal_ph; - struct of_device *mal_dev; + struct platform_device *mal_dev; u32 mal_rx_chan; u32 mal_tx_chan; struct mal_instance *mal; @@ -196,24 +196,24 @@ struct emac_instance { /* Shared MDIO if any */ u32 mdio_ph; - struct of_device *mdio_dev; + struct platform_device *mdio_dev; struct emac_instance *mdio_instance; struct mutex mdio_lock; /* ZMII infos if any */ u32 zmii_ph; u32 zmii_port; - struct of_device *zmii_dev; + struct platform_device *zmii_dev; /* RGMII infos if any */ u32 rgmii_ph; u32 rgmii_port; - struct of_device *rgmii_dev; + struct platform_device *rgmii_dev; /* TAH infos if any */ u32 tah_ph; u32 tah_port; - struct of_device *tah_dev; + struct platform_device *tah_dev; /* IRQs */ int wol_irq; diff --git a/drivers/net/ibm_newemac/mal.c b/drivers/net/ibm_newemac/mal.c index fcff9e0bd38..d5717e2123e 100644 --- a/drivers/net/ibm_newemac/mal.c +++ b/drivers/net/ibm_newemac/mal.c @@ -517,7 +517,7 @@ void *mal_dump_regs(struct mal_instance *mal, void *buf) return regs + 1; } -static int __devinit mal_probe(struct of_device *ofdev, +static int __devinit mal_probe(struct platform_device *ofdev, const struct of_device_id *match) { struct mal_instance *mal; @@ -730,7 +730,7 @@ static int __devinit mal_probe(struct of_device *ofdev, return err; } -static int __devexit mal_remove(struct of_device *ofdev) +static int __devexit mal_remove(struct platform_device *ofdev) { struct mal_instance *mal = dev_get_drvdata(&ofdev->dev); diff --git a/drivers/net/ibm_newemac/mal.h b/drivers/net/ibm_newemac/mal.h index 9ededfbf072..66084214bf4 100644 --- a/drivers/net/ibm_newemac/mal.h +++ b/drivers/net/ibm_newemac/mal.h @@ -210,7 +210,7 @@ struct mal_instance { dma_addr_t bd_dma; struct mal_descriptor *bd_virt; - struct of_device *ofdev; + struct platform_device *ofdev; int index; spinlock_t lock; diff --git a/drivers/net/ibm_newemac/rgmii.c b/drivers/net/ibm_newemac/rgmii.c index 108919bcdf1..dd61798897a 100644 --- a/drivers/net/ibm_newemac/rgmii.c +++ b/drivers/net/ibm_newemac/rgmii.c @@ -93,7 +93,7 @@ static inline u32 rgmii_mode_mask(int mode, int input) } } -int __devinit rgmii_attach(struct of_device *ofdev, int input, int mode) +int __devinit rgmii_attach(struct platform_device *ofdev, int input, int mode) { struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev); struct rgmii_regs __iomem *p = dev->base; @@ -122,7 +122,7 @@ int __devinit rgmii_attach(struct of_device *ofdev, int input, int mode) return 0; } -void rgmii_set_speed(struct of_device *ofdev, int input, int speed) +void rgmii_set_speed(struct platform_device *ofdev, int input, int speed) { struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev); struct rgmii_regs __iomem *p = dev->base; @@ -144,7 +144,7 @@ void rgmii_set_speed(struct of_device *ofdev, int input, int speed) mutex_unlock(&dev->lock); } -void rgmii_get_mdio(struct of_device *ofdev, int input) +void rgmii_get_mdio(struct platform_device *ofdev, int input) { struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev); struct rgmii_regs __iomem *p = dev->base; @@ -165,7 +165,7 @@ void rgmii_get_mdio(struct of_device *ofdev, int input) DBG2(dev, " fer = 0x%08x\n", fer); } -void rgmii_put_mdio(struct of_device *ofdev, int input) +void rgmii_put_mdio(struct platform_device *ofdev, int input) { struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev); struct rgmii_regs __iomem *p = dev->base; @@ -186,7 +186,7 @@ void rgmii_put_mdio(struct of_device *ofdev, int input) mutex_unlock(&dev->lock); } -void rgmii_detach(struct of_device *ofdev, int input) +void rgmii_detach(struct platform_device *ofdev, int input) { struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev); struct rgmii_regs __iomem *p; @@ -206,13 +206,13 @@ void rgmii_detach(struct of_device *ofdev, int input) mutex_unlock(&dev->lock); } -int rgmii_get_regs_len(struct of_device *ofdev) +int rgmii_get_regs_len(struct platform_device *ofdev) { return sizeof(struct emac_ethtool_regs_subhdr) + sizeof(struct rgmii_regs); } -void *rgmii_dump_regs(struct of_device *ofdev, void *buf) +void *rgmii_dump_regs(struct platform_device *ofdev, void *buf) { struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev); struct emac_ethtool_regs_subhdr *hdr = buf; @@ -228,7 +228,7 @@ void *rgmii_dump_regs(struct of_device *ofdev, void *buf) } -static int __devinit rgmii_probe(struct of_device *ofdev, +static int __devinit rgmii_probe(struct platform_device *ofdev, const struct of_device_id *match) { struct device_node *np = ofdev->dev.of_node; @@ -293,7 +293,7 @@ static int __devinit rgmii_probe(struct of_device *ofdev, return rc; } -static int __devexit rgmii_remove(struct of_device *ofdev) +static int __devexit rgmii_remove(struct platform_device *ofdev) { struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev); diff --git a/drivers/net/ibm_newemac/rgmii.h b/drivers/net/ibm_newemac/rgmii.h index c4a4b358a27..d6979904986 100644 --- a/drivers/net/ibm_newemac/rgmii.h +++ b/drivers/net/ibm_newemac/rgmii.h @@ -51,20 +51,20 @@ struct rgmii_instance { int users; /* OF device instance */ - struct of_device *ofdev; + struct platform_device *ofdev; }; #ifdef CONFIG_IBM_NEW_EMAC_RGMII extern int rgmii_init(void); extern void rgmii_exit(void); -extern int rgmii_attach(struct of_device *ofdev, int input, int mode); -extern void rgmii_detach(struct of_device *ofdev, int input); -extern void rgmii_get_mdio(struct of_device *ofdev, int input); -extern void rgmii_put_mdio(struct of_device *ofdev, int input); -extern void rgmii_set_speed(struct of_device *ofdev, int input, int speed); -extern int rgmii_get_regs_len(struct of_device *ofdev); -extern void *rgmii_dump_regs(struct of_device *ofdev, void *buf); +extern int rgmii_attach(struct platform_device *ofdev, int input, int mode); +extern void rgmii_detach(struct platform_device *ofdev, int input); +extern void rgmii_get_mdio(struct platform_device *ofdev, int input); +extern void rgmii_put_mdio(struct platform_device *ofdev, int input); +extern void rgmii_set_speed(struct platform_device *ofdev, int input, int speed); +extern int rgmii_get_regs_len(struct platform_device *ofdev); +extern void *rgmii_dump_regs(struct platform_device *ofdev, void *buf); #else diff --git a/drivers/net/ibm_newemac/tah.c b/drivers/net/ibm_newemac/tah.c index 044637144c4..299aa49490c 100644 --- a/drivers/net/ibm_newemac/tah.c +++ b/drivers/net/ibm_newemac/tah.c @@ -23,7 +23,7 @@ #include "emac.h" #include "core.h" -int __devinit tah_attach(struct of_device *ofdev, int channel) +int __devinit tah_attach(struct platform_device *ofdev, int channel) { struct tah_instance *dev = dev_get_drvdata(&ofdev->dev); @@ -35,7 +35,7 @@ int __devinit tah_attach(struct of_device *ofdev, int channel) return 0; } -void tah_detach(struct of_device *ofdev, int channel) +void tah_detach(struct platform_device *ofdev, int channel) { struct tah_instance *dev = dev_get_drvdata(&ofdev->dev); @@ -44,7 +44,7 @@ void tah_detach(struct of_device *ofdev, int channel) mutex_unlock(&dev->lock); } -void tah_reset(struct of_device *ofdev) +void tah_reset(struct platform_device *ofdev) { struct tah_instance *dev = dev_get_drvdata(&ofdev->dev); struct tah_regs __iomem *p = dev->base; @@ -66,13 +66,13 @@ void tah_reset(struct of_device *ofdev) TAH_MR_DIG); } -int tah_get_regs_len(struct of_device *ofdev) +int tah_get_regs_len(struct platform_device *ofdev) { return sizeof(struct emac_ethtool_regs_subhdr) + sizeof(struct tah_regs); } -void *tah_dump_regs(struct of_device *ofdev, void *buf) +void *tah_dump_regs(struct platform_device *ofdev, void *buf) { struct tah_instance *dev = dev_get_drvdata(&ofdev->dev); struct emac_ethtool_regs_subhdr *hdr = buf; @@ -87,7 +87,7 @@ void *tah_dump_regs(struct of_device *ofdev, void *buf) return regs + 1; } -static int __devinit tah_probe(struct of_device *ofdev, +static int __devinit tah_probe(struct platform_device *ofdev, const struct of_device_id *match) { struct device_node *np = ofdev->dev.of_node; @@ -139,7 +139,7 @@ static int __devinit tah_probe(struct of_device *ofdev, return rc; } -static int __devexit tah_remove(struct of_device *ofdev) +static int __devexit tah_remove(struct platform_device *ofdev) { struct tah_instance *dev = dev_get_drvdata(&ofdev->dev); diff --git a/drivers/net/ibm_newemac/tah.h b/drivers/net/ibm_newemac/tah.h index a068b5658da..61dbeca006d 100644 --- a/drivers/net/ibm_newemac/tah.h +++ b/drivers/net/ibm_newemac/tah.h @@ -48,7 +48,7 @@ struct tah_instance { int users; /* OF device instance */ - struct of_device *ofdev; + struct platform_device *ofdev; }; @@ -74,11 +74,11 @@ struct tah_instance { extern int tah_init(void); extern void tah_exit(void); -extern int tah_attach(struct of_device *ofdev, int channel); -extern void tah_detach(struct of_device *ofdev, int channel); -extern void tah_reset(struct of_device *ofdev); -extern int tah_get_regs_len(struct of_device *ofdev); -extern void *tah_dump_regs(struct of_device *ofdev, void *buf); +extern int tah_attach(struct platform_device *ofdev, int channel); +extern void tah_detach(struct platform_device *ofdev, int channel); +extern void tah_reset(struct platform_device *ofdev); +extern int tah_get_regs_len(struct platform_device *ofdev); +extern void *tah_dump_regs(struct platform_device *ofdev, void *buf); #else diff --git a/drivers/net/ibm_newemac/zmii.c b/drivers/net/ibm_newemac/zmii.c index 046dcd069c4..34ed6ee8ca8 100644 --- a/drivers/net/ibm_newemac/zmii.c +++ b/drivers/net/ibm_newemac/zmii.c @@ -82,7 +82,7 @@ static inline u32 zmii_mode_mask(int mode, int input) } } -int __devinit zmii_attach(struct of_device *ofdev, int input, int *mode) +int __devinit zmii_attach(struct platform_device *ofdev, int input, int *mode) { struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev); struct zmii_regs __iomem *p = dev->base; @@ -148,7 +148,7 @@ int __devinit zmii_attach(struct of_device *ofdev, int input, int *mode) return 0; } -void zmii_get_mdio(struct of_device *ofdev, int input) +void zmii_get_mdio(struct platform_device *ofdev, int input) { struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev); u32 fer; @@ -161,7 +161,7 @@ void zmii_get_mdio(struct of_device *ofdev, int input) out_be32(&dev->base->fer, fer | ZMII_FER_MDI(input)); } -void zmii_put_mdio(struct of_device *ofdev, int input) +void zmii_put_mdio(struct platform_device *ofdev, int input) { struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev); @@ -170,7 +170,7 @@ void zmii_put_mdio(struct of_device *ofdev, int input) } -void zmii_set_speed(struct of_device *ofdev, int input, int speed) +void zmii_set_speed(struct platform_device *ofdev, int input, int speed) { struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev); u32 ssr; @@ -191,7 +191,7 @@ void zmii_set_speed(struct of_device *ofdev, int input, int speed) mutex_unlock(&dev->lock); } -void zmii_detach(struct of_device *ofdev, int input) +void zmii_detach(struct platform_device *ofdev, int input) { struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev); @@ -210,13 +210,13 @@ void zmii_detach(struct of_device *ofdev, int input) mutex_unlock(&dev->lock); } -int zmii_get_regs_len(struct of_device *ofdev) +int zmii_get_regs_len(struct platform_device *ofdev) { return sizeof(struct emac_ethtool_regs_subhdr) + sizeof(struct zmii_regs); } -void *zmii_dump_regs(struct of_device *ofdev, void *buf) +void *zmii_dump_regs(struct platform_device *ofdev, void *buf) { struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev); struct emac_ethtool_regs_subhdr *hdr = buf; @@ -231,7 +231,7 @@ void *zmii_dump_regs(struct of_device *ofdev, void *buf) return regs + 1; } -static int __devinit zmii_probe(struct of_device *ofdev, +static int __devinit zmii_probe(struct platform_device *ofdev, const struct of_device_id *match) { struct device_node *np = ofdev->dev.of_node; @@ -286,7 +286,7 @@ static int __devinit zmii_probe(struct of_device *ofdev, return rc; } -static int __devexit zmii_remove(struct of_device *ofdev) +static int __devexit zmii_remove(struct platform_device *ofdev) { struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev); diff --git a/drivers/net/ibm_newemac/zmii.h b/drivers/net/ibm_newemac/zmii.h index 6c9beba0c4b..1333fa2b278 100644 --- a/drivers/net/ibm_newemac/zmii.h +++ b/drivers/net/ibm_newemac/zmii.h @@ -48,20 +48,20 @@ struct zmii_instance { u32 fer_save; /* OF device instance */ - struct of_device *ofdev; + struct platform_device *ofdev; }; #ifdef CONFIG_IBM_NEW_EMAC_ZMII extern int zmii_init(void); extern void zmii_exit(void); -extern int zmii_attach(struct of_device *ofdev, int input, int *mode); -extern void zmii_detach(struct of_device *ofdev, int input); -extern void zmii_get_mdio(struct of_device *ofdev, int input); -extern void zmii_put_mdio(struct of_device *ofdev, int input); -extern void zmii_set_speed(struct of_device *ofdev, int input, int speed); -extern int zmii_get_regs_len(struct of_device *ocpdev); -extern void *zmii_dump_regs(struct of_device *ofdev, void *buf); +extern int zmii_attach(struct platform_device *ofdev, int input, int *mode); +extern void zmii_detach(struct platform_device *ofdev, int input); +extern void zmii_get_mdio(struct platform_device *ofdev, int input); +extern void zmii_put_mdio(struct platform_device *ofdev, int input); +extern void zmii_set_speed(struct platform_device *ofdev, int input, int speed); +extern int zmii_get_regs_len(struct platform_device *ocpdev); +extern void *zmii_dump_regs(struct platform_device *ofdev, void *buf); #else # define zmii_init() 0 diff --git a/drivers/net/irda/sh_irda.c b/drivers/net/irda/sh_irda.c index edd5666f0ff..9e3f4f54281 100644 --- a/drivers/net/irda/sh_irda.c +++ b/drivers/net/irda/sh_irda.c @@ -748,7 +748,6 @@ static int __devinit sh_irda_probe(struct platform_device *pdev) struct net_device *ndev; struct sh_irda_self *self; struct resource *res; - char clk_name[8]; int irq; int err = -ENOMEM; @@ -775,10 +774,9 @@ static int __devinit sh_irda_probe(struct platform_device *pdev) if (err) goto err_mem_2; - snprintf(clk_name, sizeof(clk_name), "irda%d", pdev->id); - self->clk = clk_get(&pdev->dev, clk_name); + self->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(self->clk)) { - dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name); + dev_err(&pdev->dev, "cannot get irda clock\n"); goto err_mem_3; } diff --git a/drivers/net/ll_temac_main.c b/drivers/net/ll_temac_main.c index 4eea3f70c5c..c7b624711f5 100644 --- a/drivers/net/ll_temac_main.c +++ b/drivers/net/ll_temac_main.c @@ -159,7 +159,7 @@ static void temac_dma_dcr_out(struct temac_local *lp, int reg, u32 value) * temac_dcr_setup - If the DMA is DCR based, then setup the address and * I/O functions */ -static int temac_dcr_setup(struct temac_local *lp, struct of_device *op, +static int temac_dcr_setup(struct temac_local *lp, struct platform_device *op, struct device_node *np) { unsigned int dcrs; @@ -184,7 +184,7 @@ static int temac_dcr_setup(struct temac_local *lp, struct of_device *op, * temac_dcr_setup - This is a stub for when DCR is not supported, * such as with MicroBlaze */ -static int temac_dcr_setup(struct temac_local *lp, struct of_device *op, +static int temac_dcr_setup(struct temac_local *lp, struct platform_device *op, struct device_node *np) { return -1; @@ -952,7 +952,7 @@ static const struct attribute_group temac_attr_group = { }; static int __init -temac_of_probe(struct of_device *op, const struct of_device_id *match) +temac_of_probe(struct platform_device *op, const struct of_device_id *match) { struct device_node *np; struct temac_local *lp; @@ -1094,7 +1094,7 @@ temac_of_probe(struct of_device *op, const struct of_device_id *match) return rc; } -static int __devexit temac_of_remove(struct of_device *op) +static int __devexit temac_of_remove(struct platform_device *op) { struct net_device *ndev = dev_get_drvdata(&op->dev); struct temac_local *lp = netdev_priv(ndev); diff --git a/drivers/net/myri_sbus.c b/drivers/net/myri_sbus.c index 04e552aa14e..617f898ba5f 100644 --- a/drivers/net/myri_sbus.c +++ b/drivers/net/myri_sbus.c @@ -926,7 +926,7 @@ static const struct net_device_ops myri_ops = { .ndo_validate_addr = eth_validate_addr, }; -static int __devinit myri_sbus_probe(struct of_device *op, const struct of_device_id *match) +static int __devinit myri_sbus_probe(struct platform_device *op, const struct of_device_id *match) { struct device_node *dp = op->dev.of_node; static unsigned version_printed; @@ -1124,7 +1124,7 @@ err: return -ENODEV; } -static int __devexit myri_sbus_remove(struct of_device *op) +static int __devexit myri_sbus_remove(struct platform_device *op) { struct myri_eth *mp = dev_get_drvdata(&op->dev); struct net_device *net_dev = mp->dev; diff --git a/drivers/net/myri_sbus.h b/drivers/net/myri_sbus.h index ff363e95d9c..80a2fa5cf75 100644 --- a/drivers/net/myri_sbus.h +++ b/drivers/net/myri_sbus.h @@ -288,7 +288,7 @@ struct myri_eth { struct myri_eeprom eeprom; /* Local copy of EEPROM. */ unsigned int reg_size; /* Size of register space. */ unsigned int shmem_base; /* Offset to shared ram. */ - struct of_device *myri_op; /* Our OF device struct. */ + struct platform_device *myri_op; /* Our OF device struct. */ }; /* We use this to acquire receive skb's that we can DMA directly into. */ diff --git a/drivers/net/niu.c b/drivers/net/niu.c index 404f2d55288..bc695d53cdc 100644 --- a/drivers/net/niu.c +++ b/drivers/net/niu.c @@ -9103,7 +9103,7 @@ retry: static int __devinit niu_n2_irq_init(struct niu *np, u8 *ldg_num_map) { #ifdef CONFIG_SPARC64 - struct of_device *op = np->op; + struct platform_device *op = np->op; const u32 *int_prop; int i; @@ -9688,7 +9688,7 @@ static void __devinit niu_driver_version(void) static struct net_device * __devinit niu_alloc_and_init( struct device *gen_dev, struct pci_dev *pdev, - struct of_device *op, const struct niu_ops *ops, + struct platform_device *op, const struct niu_ops *ops, u8 port) { struct net_device *dev; @@ -10064,7 +10064,7 @@ static const struct niu_ops niu_phys_ops = { .unmap_single = niu_phys_unmap_single, }; -static int __devinit niu_of_probe(struct of_device *op, +static int __devinit niu_of_probe(struct platform_device *op, const struct of_device_id *match) { union niu_parent_id parent_id; @@ -10179,7 +10179,7 @@ err_out: return err; } -static int __devexit niu_of_remove(struct of_device *op) +static int __devexit niu_of_remove(struct platform_device *op) { struct net_device *dev = dev_get_drvdata(&op->dev); diff --git a/drivers/net/phy/mdio-gpio.c b/drivers/net/phy/mdio-gpio.c index fc5fef2a817..f62c7b717bc 100644 --- a/drivers/net/phy/mdio-gpio.c +++ b/drivers/net/phy/mdio-gpio.c @@ -188,7 +188,7 @@ static int __devexit mdio_gpio_remove(struct platform_device *pdev) #ifdef CONFIG_OF_GPIO -static int __devinit mdio_ofgpio_probe(struct of_device *ofdev, +static int __devinit mdio_ofgpio_probe(struct platform_device *ofdev, const struct of_device_id *match) { struct mdio_gpio_platform_data *pdata; @@ -224,7 +224,7 @@ out_free: return -ENODEV; } -static int __devexit mdio_ofgpio_remove(struct of_device *ofdev) +static int __devexit mdio_ofgpio_remove(struct platform_device *ofdev) { mdio_gpio_bus_destroy(&ofdev->dev); kfree(ofdev->dev.platform_data); diff --git a/drivers/net/sunbmac.c b/drivers/net/sunbmac.c index 09c071bd6ad..618643e3ca3 100644 --- a/drivers/net/sunbmac.c +++ b/drivers/net/sunbmac.c @@ -97,7 +97,7 @@ static int qec_global_reset(void __iomem *gregs) static void qec_init(struct bigmac *bp) { - struct of_device *qec_op = bp->qec_op; + struct platform_device *qec_op = bp->qec_op; void __iomem *gregs = bp->gregs; u8 bsizes = bp->bigmac_bursts; u32 regval; @@ -1083,8 +1083,8 @@ static const struct net_device_ops bigmac_ops = { .ndo_validate_addr = eth_validate_addr, }; -static int __devinit bigmac_ether_init(struct of_device *op, - struct of_device *qec_op) +static int __devinit bigmac_ether_init(struct platform_device *op, + struct platform_device *qec_op) { static int version_printed; struct net_device *dev; @@ -1242,25 +1242,25 @@ fail_and_cleanup: /* QEC can be the parent of either QuadEthernet or a BigMAC. We want * the latter. */ -static int __devinit bigmac_sbus_probe(struct of_device *op, +static int __devinit bigmac_sbus_probe(struct platform_device *op, const struct of_device_id *match) { struct device *parent = op->dev.parent; - struct of_device *qec_op; + struct platform_device *qec_op; - qec_op = to_of_device(parent); + qec_op = to_platform_device(parent); return bigmac_ether_init(op, qec_op); } -static int __devexit bigmac_sbus_remove(struct of_device *op) +static int __devexit bigmac_sbus_remove(struct platform_device *op) { struct bigmac *bp = dev_get_drvdata(&op->dev); struct device *parent = op->dev.parent; struct net_device *net_dev = bp->dev; - struct of_device *qec_op; + struct platform_device *qec_op; - qec_op = to_of_device(parent); + qec_op = to_platform_device(parent); unregister_netdev(net_dev); diff --git a/drivers/net/sunbmac.h b/drivers/net/sunbmac.h index 8840bc0b840..8db88945b88 100644 --- a/drivers/net/sunbmac.h +++ b/drivers/net/sunbmac.h @@ -329,8 +329,8 @@ struct bigmac { unsigned int timer_ticks; struct net_device_stats enet_stats; - struct of_device *qec_op; - struct of_device *bigmac_op; + struct platform_device *qec_op; + struct platform_device *bigmac_op; struct net_device *dev; }; diff --git a/drivers/net/sunhme.c b/drivers/net/sunhme.c index eec443f6407..bd0df1c1495 100644 --- a/drivers/net/sunhme.c +++ b/drivers/net/sunhme.c @@ -1591,7 +1591,7 @@ static int happy_meal_init(struct happy_meal *hp) */ #ifdef CONFIG_SBUS if ((hp->happy_flags & HFLAG_PCI) == 0) { - struct of_device *op = hp->happy_dev; + struct platform_device *op = hp->happy_dev; if (sbus_can_dma_64bit()) { sbus_set_sbus64(&op->dev, hp->happy_bursts); @@ -2480,7 +2480,7 @@ static void hme_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info #ifdef CONFIG_SBUS else { const struct linux_prom_registers *regs; - struct of_device *op = hp->happy_dev; + struct platform_device *op = hp->happy_dev; regs = of_get_property(op->dev.of_node, "regs", NULL); if (regs) sprintf(info->bus_info, "SBUS:%d", @@ -2515,13 +2515,13 @@ static int hme_version_printed; * * Return NULL on failure. */ -static struct quattro * __devinit quattro_sbus_find(struct of_device *child) +static struct quattro * __devinit quattro_sbus_find(struct platform_device *child) { struct device *parent = child->dev.parent; - struct of_device *op; + struct platform_device *op; struct quattro *qp; - op = to_of_device(parent); + op = to_platform_device(parent); qp = dev_get_drvdata(&op->dev); if (qp) return qp; @@ -2551,7 +2551,7 @@ static int __init quattro_sbus_register_irqs(void) struct quattro *qp; for (qp = qfe_sbus_list; qp != NULL; qp = qp->next) { - struct of_device *op = qp->quattro_dev; + struct platform_device *op = qp->quattro_dev; int err, qfe_slot, skip = 0; for (qfe_slot = 0; qfe_slot < 4; qfe_slot++) { @@ -2580,7 +2580,7 @@ static void quattro_sbus_free_irqs(void) struct quattro *qp; for (qp = qfe_sbus_list; qp != NULL; qp = qp->next) { - struct of_device *op = qp->quattro_dev; + struct platform_device *op = qp->quattro_dev; int qfe_slot, skip = 0; for (qfe_slot = 0; qfe_slot < 4; qfe_slot++) { @@ -2639,7 +2639,7 @@ static const struct net_device_ops hme_netdev_ops = { }; #ifdef CONFIG_SBUS -static int __devinit happy_meal_sbus_probe_one(struct of_device *op, int is_qfe) +static int __devinit happy_meal_sbus_probe_one(struct platform_device *op, int is_qfe) { struct device_node *dp = op->dev.of_node, *sbus_dp; struct quattro *qp = NULL; @@ -2648,7 +2648,7 @@ static int __devinit happy_meal_sbus_probe_one(struct of_device *op, int is_qfe) int i, qfe_slot = -1; int err = -ENODEV; - sbus_dp = to_of_device(op->dev.parent)->dev.of_node; + sbus_dp = op->dev.parent->of_node; /* We can match PCI devices too, do not accept those here. */ if (strcmp(sbus_dp->name, "sbus")) @@ -3235,7 +3235,7 @@ static void happy_meal_pci_exit(void) #endif #ifdef CONFIG_SBUS -static int __devinit hme_sbus_probe(struct of_device *op, const struct of_device_id *match) +static int __devinit hme_sbus_probe(struct platform_device *op, const struct of_device_id *match) { struct device_node *dp = op->dev.of_node; const char *model = of_get_property(dp, "model", NULL); @@ -3247,7 +3247,7 @@ static int __devinit hme_sbus_probe(struct of_device *op, const struct of_device return happy_meal_sbus_probe_one(op, is_qfe); } -static int __devexit hme_sbus_remove(struct of_device *op) +static int __devexit hme_sbus_remove(struct platform_device *op) { struct happy_meal *hp = dev_get_drvdata(&op->dev); struct net_device *net_dev = hp->dev; diff --git a/drivers/net/sunhme.h b/drivers/net/sunhme.h index efd2ca0fcad..756b5bf3aa8 100644 --- a/drivers/net/sunhme.h +++ b/drivers/net/sunhme.h @@ -407,7 +407,7 @@ struct happy_meal { void (*write_rxd)(struct happy_meal_rxd *, u32, u32); #endif - /* This is either an of_device or a pci_dev. */ + /* This is either an platform_device or a pci_dev. */ void *happy_dev; struct device *dma_dev; diff --git a/drivers/net/sunlance.c b/drivers/net/sunlance.c index ee364fa7563..8dcb858f216 100644 --- a/drivers/net/sunlance.c +++ b/drivers/net/sunlance.c @@ -250,7 +250,7 @@ struct lance_private { int rx_new, tx_new; int rx_old, tx_old; - struct of_device *ledma; /* If set this points to ledma */ + struct platform_device *ledma; /* If set this points to ledma */ char tpe; /* cable-selection is TPE */ char auto_select; /* cable-selection by carrier */ char burst_sizes; /* ledma SBus burst sizes */ @@ -265,8 +265,8 @@ struct lance_private { char *name; dma_addr_t init_block_dvma; struct net_device *dev; /* Backpointer */ - struct of_device *op; - struct of_device *lebuffer; + struct platform_device *op; + struct platform_device *lebuffer; struct timer_list multicast_timer; }; @@ -1272,7 +1272,7 @@ static void lance_free_hwresources(struct lance_private *lp) if (lp->lregs) of_iounmap(&lp->op->resource[0], lp->lregs, LANCE_REG_SIZE); if (lp->dregs) { - struct of_device *ledma = lp->ledma; + struct platform_device *ledma = lp->ledma; of_iounmap(&ledma->resource[0], lp->dregs, resource_size(&ledma->resource[0])); @@ -1319,9 +1319,9 @@ static const struct net_device_ops sparc_lance_ops = { .ndo_validate_addr = eth_validate_addr, }; -static int __devinit sparc_lance_probe_one(struct of_device *op, - struct of_device *ledma, - struct of_device *lebuffer) +static int __devinit sparc_lance_probe_one(struct platform_device *op, + struct platform_device *ledma, + struct platform_device *lebuffer) { struct device_node *dp = op->dev.of_node; static unsigned version_printed; @@ -1503,9 +1503,9 @@ fail: return -ENODEV; } -static int __devinit sunlance_sbus_probe(struct of_device *op, const struct of_device_id *match) +static int __devinit sunlance_sbus_probe(struct platform_device *op, const struct of_device_id *match) { - struct of_device *parent = to_of_device(op->dev.parent); + struct platform_device *parent = to_platform_device(op->dev.parent); struct device_node *parent_dp = parent->dev.of_node; int err; @@ -1519,7 +1519,7 @@ static int __devinit sunlance_sbus_probe(struct of_device *op, const struct of_d return err; } -static int __devexit sunlance_sbus_remove(struct of_device *op) +static int __devexit sunlance_sbus_remove(struct platform_device *op) { struct lance_private *lp = dev_get_drvdata(&op->dev); struct net_device *net_dev = lp->dev; diff --git a/drivers/net/sunqe.c b/drivers/net/sunqe.c index 5f84a5daded..72e65d4666e 100644 --- a/drivers/net/sunqe.c +++ b/drivers/net/sunqe.c @@ -689,7 +689,7 @@ static void qe_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { const struct linux_prom_registers *regs; struct sunqe *qep = netdev_priv(dev); - struct of_device *op; + struct platform_device *op; strcpy(info->driver, "sunqe"); strcpy(info->version, "3.0"); @@ -720,7 +720,7 @@ static const struct ethtool_ops qe_ethtool_ops = { }; /* This is only called once at boot time for each card probed. */ -static void qec_init_once(struct sunqec *qecp, struct of_device *op) +static void qec_init_once(struct sunqec *qecp, struct platform_device *op) { u8 bsizes = qecp->qec_bursts; @@ -770,9 +770,9 @@ static u8 __devinit qec_get_burst(struct device_node *dp) return bsizes; } -static struct sunqec * __devinit get_qec(struct of_device *child) +static struct sunqec * __devinit get_qec(struct platform_device *child) { - struct of_device *op = to_of_device(child->dev.parent); + struct platform_device *op = to_platform_device(child->dev.parent); struct sunqec *qecp; qecp = dev_get_drvdata(&op->dev); @@ -836,7 +836,7 @@ static const struct net_device_ops qec_ops = { .ndo_validate_addr = eth_validate_addr, }; -static int __devinit qec_ether_init(struct of_device *op) +static int __devinit qec_ether_init(struct platform_device *op) { static unsigned version_printed; struct net_device *dev; @@ -941,12 +941,12 @@ fail: return res; } -static int __devinit qec_sbus_probe(struct of_device *op, const struct of_device_id *match) +static int __devinit qec_sbus_probe(struct platform_device *op, const struct of_device_id *match) { return qec_ether_init(op); } -static int __devexit qec_sbus_remove(struct of_device *op) +static int __devexit qec_sbus_remove(struct platform_device *op) { struct sunqe *qp = dev_get_drvdata(&op->dev); struct net_device *net_dev = qp->dev; @@ -997,7 +997,7 @@ static void __exit qec_exit(void) while (root_qec_dev) { struct sunqec *next = root_qec_dev->next_module; - struct of_device *op = root_qec_dev->op; + struct platform_device *op = root_qec_dev->op; free_irq(op->archdata.irqs[0], (void *) root_qec_dev); of_iounmap(&op->resource[0], root_qec_dev->gregs, diff --git a/drivers/net/sunqe.h b/drivers/net/sunqe.h index 5813a7b2faa..581781b6b2f 100644 --- a/drivers/net/sunqe.h +++ b/drivers/net/sunqe.h @@ -314,7 +314,7 @@ struct sunqec { void __iomem *gregs; /* QEC Global Registers */ struct sunqe *qes[4]; /* Each child MACE */ unsigned int qec_bursts; /* Support burst sizes */ - struct of_device *op; /* QEC's OF device */ + struct platform_device *op; /* QEC's OF device */ struct sunqec *next_module; /* List of all QECs in system */ }; @@ -342,7 +342,7 @@ struct sunqe { __u32 buffers_dvma; /* DVMA visible address. */ struct sunqec *parent; u8 mconfig; /* Base MACE mconfig value */ - struct of_device *op; /* QE's OF device struct */ + struct platform_device *op; /* QE's OF device struct */ struct net_device *dev; /* QE's netdevice struct */ int channel; /* Who am I? */ }; diff --git a/drivers/net/ucc_geth.c b/drivers/net/ucc_geth.c index 8d532f9b50d..a4c3f570824 100644 --- a/drivers/net/ucc_geth.c +++ b/drivers/net/ucc_geth.c @@ -3601,7 +3601,7 @@ static void ucc_geth_timeout(struct net_device *dev) #ifdef CONFIG_PM -static int ucc_geth_suspend(struct of_device *ofdev, pm_message_t state) +static int ucc_geth_suspend(struct platform_device *ofdev, pm_message_t state) { struct net_device *ndev = dev_get_drvdata(&ofdev->dev); struct ucc_geth_private *ugeth = netdev_priv(ndev); @@ -3629,7 +3629,7 @@ static int ucc_geth_suspend(struct of_device *ofdev, pm_message_t state) return 0; } -static int ucc_geth_resume(struct of_device *ofdev) +static int ucc_geth_resume(struct platform_device *ofdev) { struct net_device *ndev = dev_get_drvdata(&ofdev->dev); struct ucc_geth_private *ugeth = netdev_priv(ndev); @@ -3732,7 +3732,7 @@ static const struct net_device_ops ucc_geth_netdev_ops = { #endif }; -static int ucc_geth_probe(struct of_device* ofdev, const struct of_device_id *match) +static int ucc_geth_probe(struct platform_device* ofdev, const struct of_device_id *match) { struct device *device = &ofdev->dev; struct device_node *np = ofdev->dev.of_node; @@ -3954,7 +3954,7 @@ static int ucc_geth_probe(struct of_device* ofdev, const struct of_device_id *ma return 0; } -static int ucc_geth_remove(struct of_device* ofdev) +static int ucc_geth_remove(struct platform_device* ofdev) { struct device *device = &ofdev->dev; struct net_device *dev = dev_get_drvdata(device); diff --git a/drivers/net/wan/ixp4xx_hss.c b/drivers/net/wan/ixp4xx_hss.c index 88e363033e2..6c571e19883 100644 --- a/drivers/net/wan/ixp4xx_hss.c +++ b/drivers/net/wan/ixp4xx_hss.c @@ -396,7 +396,7 @@ static void hss_config(struct port *port) msg.cmd = PORT_CONFIG_WRITE; msg.hss_port = port->id; msg.index = HSS_CONFIG_TX_PCR; - msg.data32 = PCR_FRM_SYNC_OUTPUT_RISING | PCR_MSB_ENDIAN | + msg.data32 = PCR_FRM_PULSE_DISABLED | PCR_MSB_ENDIAN | PCR_TX_DATA_ENABLE | PCR_SOF_NO_FBIT; if (port->clock_type == CLOCK_INT) msg.data32 |= PCR_SYNC_CLK_DIR_OUTPUT; diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c index 8e9fbfd804b..2372abb29c2 100644 --- a/drivers/net/wireless/libertas/cfg.c +++ b/drivers/net/wireless/libertas/cfg.c @@ -6,6 +6,8 @@ * */ +#include <linux/sched.h> +#include <linux/wait.h> #include <linux/slab.h> #include <linux/ieee80211.h> #include <net/cfg80211.h> diff --git a/drivers/net/xilinx_emaclite.c b/drivers/net/xilinx_emaclite.c index b2c2f391b29..ecbbb688eba 100644 --- a/drivers/net/xilinx_emaclite.c +++ b/drivers/net/xilinx_emaclite.c @@ -1086,7 +1086,7 @@ static void xemaclite_remove_ndev(struct net_device *ndev) * * Return: Value of the parameter if the parameter is found, or 0 otherwise */ -static bool get_bool(struct of_device *ofdev, const char *s) +static bool get_bool(struct platform_device *ofdev, const char *s) { u32 *p = (u32 *)of_get_property(ofdev->dev.of_node, s, NULL); @@ -1115,7 +1115,7 @@ static struct net_device_ops xemaclite_netdev_ops; * Return: 0, if the driver is bound to the Emaclite device, or * a negative error if there is failure. */ -static int __devinit xemaclite_of_probe(struct of_device *ofdev, +static int __devinit xemaclite_of_probe(struct platform_device *ofdev, const struct of_device_id *match) { struct resource r_irq; /* Interrupt resources */ @@ -1240,7 +1240,7 @@ error2: * * Return: 0, always. */ -static int __devexit xemaclite_of_remove(struct of_device *of_dev) +static int __devexit xemaclite_of_remove(struct platform_device *of_dev) { struct device *dev = &of_dev->dev; struct net_device *ndev = dev_get_drvdata(dev); diff --git a/drivers/of/device.c b/drivers/of/device.c index 0d8a0644f54..92de0eb74ae 100644 --- a/drivers/of/device.c +++ b/drivers/of/device.c @@ -14,7 +14,7 @@ * @ids: array of of device match structures to search in * @dev: the of device structure to match against * - * Used by a driver to check whether an of_device present in the + * Used by a driver to check whether an platform_device present in the * system is in its list of supported devices. */ const struct of_device_id *of_match_device(const struct of_device_id *matches, diff --git a/drivers/parport/parport_serial.c b/drivers/parport/parport_serial.c index 40e208d130f..f01e26440f1 100644 --- a/drivers/parport/parport_serial.c +++ b/drivers/parport/parport_serial.c @@ -342,7 +342,6 @@ static int __devinit parport_register (struct pci_dev *dev, dev_dbg(&dev->dev, "PCI parallel port detected: I/O at %#lx(%#lx), IRQ %d\n", io_lo, io_hi, irq); - irq = PARPORT_IRQ_NONE; } port = parport_pc_probe_port (io_lo, io_hi, irq, PARPORT_DMA_NONE, &dev->dev, IRQF_SHARED); diff --git a/drivers/parport/parport_sunbpp.c b/drivers/parport/parport_sunbpp.c index 210a6441a06..55ba118f1cf 100644 --- a/drivers/parport/parport_sunbpp.c +++ b/drivers/parport/parport_sunbpp.c @@ -286,7 +286,7 @@ static struct parport_operations parport_sunbpp_ops = .owner = THIS_MODULE, }; -static int __devinit bpp_probe(struct of_device *op, const struct of_device_id *match) +static int __devinit bpp_probe(struct platform_device *op, const struct of_device_id *match) { struct parport_operations *ops; struct bpp_regs __iomem *regs; @@ -351,7 +351,7 @@ out_unmap: return err; } -static int __devexit bpp_remove(struct of_device *op) +static int __devexit bpp_remove(struct platform_device *op) { struct parport *p = dev_get_drvdata(&op->dev); struct parport_operations *ops = p->ops; diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index c988514eb55..c80a7a6e769 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig @@ -215,7 +215,7 @@ config PCMCIA_PXA2XX depends on (ARCH_LUBBOCK || MACH_MAINSTONE || PXA_SHARPSL \ || MACH_ARMCORE || ARCH_PXA_PALM || TRIZEPS_PCMCIA \ || ARCOM_PCMCIA || ARCH_PXA_ESERIES || MACH_STARGATE2 \ - || MACH_VPAC270) + || MACH_VPAC270 || MACH_BALLOON3) select PCMCIA_SOC_COMMON help Say Y here to include support for the PXA2xx PCMCIA controller diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile index 7a2b1604bf1..8d9386a22eb 100644 --- a/drivers/pcmcia/Makefile +++ b/drivers/pcmcia/Makefile @@ -69,6 +69,7 @@ pxa2xx-obj-$(CONFIG_MACH_PALMLD) += pxa2xx_palmld.o pxa2xx-obj-$(CONFIG_MACH_E740) += pxa2xx_e740.o pxa2xx-obj-$(CONFIG_MACH_STARGATE2) += pxa2xx_stargate2.o pxa2xx-obj-$(CONFIG_MACH_VPAC270) += pxa2xx_vpac270.o +pxa2xx-obj-$(CONFIG_MACH_BALLOON3) += pxa2xx_balloon3.o obj-$(CONFIG_PCMCIA_PXA2XX) += pxa2xx_base.o $(pxa2xx-obj-y) diff --git a/drivers/pcmcia/electra_cf.c b/drivers/pcmcia/electra_cf.c index f94d8281cfb..546d3024b6f 100644 --- a/drivers/pcmcia/electra_cf.c +++ b/drivers/pcmcia/electra_cf.c @@ -44,7 +44,7 @@ struct electra_cf_socket { unsigned present:1; unsigned active:1; - struct of_device *ofdev; + struct platform_device *ofdev; unsigned long mem_phys; void __iomem * mem_base; unsigned long mem_size; @@ -181,7 +181,7 @@ static struct pccard_operations electra_cf_ops = { .set_mem_map = electra_cf_set_mem_map, }; -static int __devinit electra_cf_probe(struct of_device *ofdev, +static int __devinit electra_cf_probe(struct platform_device *ofdev, const struct of_device_id *match) { struct device *device = &ofdev->dev; @@ -325,7 +325,7 @@ fail1: } -static int __devexit electra_cf_remove(struct of_device *ofdev) +static int __devexit electra_cf_remove(struct platform_device *ofdev) { struct device *device = &ofdev->dev; struct electra_cf_socket *cf; diff --git a/drivers/pcmcia/m8xx_pcmcia.c b/drivers/pcmcia/m8xx_pcmcia.c index f2f90a7d3e1..f0ecad99ce8 100644 --- a/drivers/pcmcia/m8xx_pcmcia.c +++ b/drivers/pcmcia/m8xx_pcmcia.c @@ -1149,7 +1149,7 @@ static struct pccard_operations m8xx_services = { .set_mem_map = m8xx_set_mem_map, }; -static int __init m8xx_probe(struct of_device *ofdev, +static int __init m8xx_probe(struct platform_device *ofdev, const struct of_device_id *match) { struct pcmcia_win *w; @@ -1249,7 +1249,7 @@ static int __init m8xx_probe(struct of_device *ofdev, return 0; } -static int m8xx_remove(struct of_device *ofdev) +static int m8xx_remove(struct platform_device *ofdev) { u32 m, i; struct pcmcia_win *w; diff --git a/drivers/pcmcia/pxa2xx_balloon3.c b/drivers/pcmcia/pxa2xx_balloon3.c new file mode 100644 index 00000000000..dbbdd006320 --- /dev/null +++ b/drivers/pcmcia/pxa2xx_balloon3.c @@ -0,0 +1,158 @@ +/* + * linux/drivers/pcmcia/pxa2xx_balloon3.c + * + * Balloon3 PCMCIA specific routines. + * + * Author: Nick Bane + * Created: June, 2006 + * Copyright: Toby Churchill Ltd + * Derived from pxa2xx_mainstone.c, by Nico Pitre + * + * Various modification by Marek Vasut <marek.vasut@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/gpio.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/irq.h> +#include <linux/io.h> + +#include <mach/balloon3.h> + +#include "soc_common.h" + +/* + * These are a list of interrupt sources that provokes a polled + * check of status + */ +static struct pcmcia_irqs irqs[] = { + { 0, BALLOON3_S0_CD_IRQ, "PCMCIA0 CD" }, + { 0, BALLOON3_BP_NSTSCHG_IRQ, "PCMCIA0 STSCHG" }, +}; + +static int balloon3_pcmcia_hw_init(struct soc_pcmcia_socket *skt) +{ + uint16_t ver; + int ret; + static void __iomem *fpga_ver; + + ver = __raw_readw(BALLOON3_FPGA_VER); + if (ver > 0x0201) + pr_warn("The FPGA code, version 0x%04x, is newer than rel-0.3. " + "PCMCIA/CF support might be broken in this version!", + ver); + + skt->socket.pci_irq = BALLOON3_BP_CF_NRDY_IRQ; + return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs)); +} + +static void balloon3_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) +{ + soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs)); +} + +static unsigned long balloon3_pcmcia_status[2] = { + BALLOON3_CF_nSTSCHG_BVD1, + BALLOON3_CF_nSTSCHG_BVD1 +}; + +static void balloon3_pcmcia_socket_state(struct soc_pcmcia_socket *skt, + struct pcmcia_state *state) +{ + uint16_t status; + int flip; + + /* This actually reads the STATUS register */ + status = __raw_readw(BALLOON3_CF_STATUS_REG); + flip = (status ^ balloon3_pcmcia_status[skt->nr]) + & BALLOON3_CF_nSTSCHG_BVD1; + /* + * Workaround for STSCHG which can't be deasserted: + * We therefore disable/enable corresponding IRQs + * as needed to avoid IRQ locks. + */ + if (flip) { + balloon3_pcmcia_status[skt->nr] = status; + if (status & BALLOON3_CF_nSTSCHG_BVD1) + enable_irq(BALLOON3_BP_NSTSCHG_IRQ); + else + disable_irq(BALLOON3_BP_NSTSCHG_IRQ); + } + + state->detect = !gpio_get_value(BALLOON3_GPIO_S0_CD); + state->ready = !!(status & BALLOON3_CF_nIRQ); + state->bvd1 = !!(status & BALLOON3_CF_nSTSCHG_BVD1); + state->bvd2 = 0; /* not available */ + state->vs_3v = 1; /* Always true its a CF card */ + state->vs_Xv = 0; /* not available */ + state->wrprot = 0; /* not available */ +} + +static int balloon3_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, + const socket_state_t *state) +{ + __raw_writew((state->flags & SS_RESET) ? BALLOON3_CF_RESET : 0, + BALLOON3_CF_CONTROL_REG); + return 0; +} + +static void balloon3_pcmcia_socket_init(struct soc_pcmcia_socket *skt) +{ +} + +static void balloon3_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt) +{ +} + +static struct pcmcia_low_level balloon3_pcmcia_ops = { + .owner = THIS_MODULE, + .hw_init = balloon3_pcmcia_hw_init, + .hw_shutdown = balloon3_pcmcia_hw_shutdown, + .socket_state = balloon3_pcmcia_socket_state, + .configure_socket = balloon3_pcmcia_configure_socket, + .socket_init = balloon3_pcmcia_socket_init, + .socket_suspend = balloon3_pcmcia_socket_suspend, + .first = 0, + .nr = 1, +}; + +static struct platform_device *balloon3_pcmcia_device; + +static int __init balloon3_pcmcia_init(void) +{ + int ret; + + balloon3_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1); + if (!balloon3_pcmcia_device) + return -ENOMEM; + + ret = platform_device_add_data(balloon3_pcmcia_device, + &balloon3_pcmcia_ops, sizeof(balloon3_pcmcia_ops)); + + if (!ret) + ret = platform_device_add(balloon3_pcmcia_device); + + if (ret) + platform_device_put(balloon3_pcmcia_device); + + return ret; +} + +static void __exit balloon3_pcmcia_exit(void) +{ + platform_device_unregister(balloon3_pcmcia_device); +} + +module_init(balloon3_pcmcia_init); +module_exit(balloon3_pcmcia_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Nick Bane <nick@cecomputing.co.uk>"); +MODULE_ALIAS("platform:pxa2xx-pcmcia"); +MODULE_DESCRIPTION("Balloon3 board CF/PCMCIA driver"); diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 1e5506be39b..07343568a12 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -136,6 +136,12 @@ config BATTERY_Z2 help Say Y to include support for the battery on the Zipit Z2. +config BATTERY_S3C_ADC + tristate "Battery driver for Samsung ADC based monitoring" + depends on S3C_ADC + help + Say Y here to enable support for iPAQ h1930/h1940/rx1950 battery + config CHARGER_PCF50633 tristate "NXP PCF50633 MBC" depends on MFD_PCF50633 @@ -153,4 +159,11 @@ config BATTERY_JZ4740 This driver can be build as a module. If so, the module will be called jz4740-battery. +config BATTERY_INTEL_MID + tristate "Battery driver for Intel MID platforms" + depends on INTEL_SCU_IPC && SPI + help + Say Y here to enable the battery driver on Intel MID + platforms. + endif # POWER_SUPPLY diff --git a/drivers/power/Makefile b/drivers/power/Makefile index cf95009d9bc..10143aaf4ee 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -33,5 +33,7 @@ obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o obj-$(CONFIG_BATTERY_Z2) += z2_battery.o +obj-$(CONFIG_BATTERY_S3C_ADC) += s3c_adc_battery.o obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o obj-$(CONFIG_BATTERY_JZ4740) += jz4740-battery.o +obj-$(CONFIG_BATTERY_INTEL_MID) += intel_mid_battery.o diff --git a/drivers/power/intel_mid_battery.c b/drivers/power/intel_mid_battery.c new file mode 100644 index 00000000000..c61ffec2ff1 --- /dev/null +++ b/drivers/power/intel_mid_battery.c @@ -0,0 +1,799 @@ +/* + * intel_mid_battery.c - Intel MID PMIC Battery Driver + * + * Copyright (C) 2009 Intel Corporation + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Author: Nithish Mahalingam <nithish.mahalingam@intel.com> + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/workqueue.h> +#include <linux/jiffies.h> +#include <linux/param.h> +#include <linux/device.h> +#include <linux/spi/spi.h> +#include <linux/platform_device.h> +#include <linux/power_supply.h> + +#include <asm/intel_scu_ipc.h> + +#define DRIVER_NAME "pmic_battery" + +/********************************************************************* + * Generic defines + *********************************************************************/ + +static int debug; +module_param(debug, int, 0444); +MODULE_PARM_DESC(debug, "Flag to enable PMIC Battery debug messages."); + +#define PMIC_BATT_DRV_INFO_UPDATED 1 +#define PMIC_BATT_PRESENT 1 +#define PMIC_BATT_NOT_PRESENT 0 +#define PMIC_USB_PRESENT PMIC_BATT_PRESENT +#define PMIC_USB_NOT_PRESENT PMIC_BATT_NOT_PRESENT + +/* pmic battery register related */ +#define PMIC_BATT_CHR_SCHRGINT_ADDR 0xD2 +#define PMIC_BATT_CHR_SBATOVP_MASK (1 << 1) +#define PMIC_BATT_CHR_STEMP_MASK (1 << 2) +#define PMIC_BATT_CHR_SCOMP_MASK (1 << 3) +#define PMIC_BATT_CHR_SUSBDET_MASK (1 << 4) +#define PMIC_BATT_CHR_SBATDET_MASK (1 << 5) +#define PMIC_BATT_CHR_SDCLMT_MASK (1 << 6) +#define PMIC_BATT_CHR_SUSBOVP_MASK (1 << 7) +#define PMIC_BATT_CHR_EXCPT_MASK 0xC6 +#define PMIC_BATT_ADC_ACCCHRG_MASK (1 << 31) +#define PMIC_BATT_ADC_ACCCHRGVAL_MASK 0x7FFFFFFF + +/* pmic ipc related */ +#define PMIC_BATT_CHR_IPC_FCHRG_SUBID 0x4 +#define PMIC_BATT_CHR_IPC_TCHRG_SUBID 0x6 + +/* types of battery charging */ +enum batt_charge_type { + BATT_USBOTG_500MA_CHARGE, + BATT_USBOTG_TRICKLE_CHARGE, +}; + +/* valid battery events */ +enum batt_event { + BATT_EVENT_BATOVP_EXCPT, + BATT_EVENT_USBOVP_EXCPT, + BATT_EVENT_TEMP_EXCPT, + BATT_EVENT_DCLMT_EXCPT, + BATT_EVENT_EXCPT +}; + + +/********************************************************************* + * Battery properties + *********************************************************************/ + +/* + * pmic battery info + */ +struct pmic_power_module_info { + bool is_dev_info_updated; + struct device *dev; + /* pmic battery data */ + unsigned long update_time; /* jiffies when data read */ + unsigned int usb_is_present; + unsigned int batt_is_present; + unsigned int batt_health; + unsigned int usb_health; + unsigned int batt_status; + unsigned int batt_charge_now; /* in mAS */ + unsigned int batt_prev_charge_full; /* in mAS */ + unsigned int batt_charge_rate; /* in units per second */ + + struct power_supply usb; + struct power_supply batt; + int irq; /* GPE_ID or IRQ# */ + struct workqueue_struct *monitor_wqueue; + struct delayed_work monitor_battery; + struct work_struct handler; +}; + +static unsigned int delay_time = 2000; /* in ms */ + +/* + * pmic ac properties + */ +static enum power_supply_property pmic_usb_props[] = { + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_HEALTH, +}; + +/* + * pmic battery properties + */ +static enum power_supply_property pmic_battery_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_CHARGE_NOW, + POWER_SUPPLY_PROP_CHARGE_FULL, +}; + + +/* + * Glue functions for talking to the IPC + */ + +struct battery_property { + u32 capacity; /* Charger capacity */ + u8 crnt; /* Quick charge current value*/ + u8 volt; /* Fine adjustment of constant charge voltage */ + u8 prot; /* CHRGPROT register value */ + u8 prot2; /* CHRGPROT1 register value */ + u8 timer; /* Charging timer */ +}; + +#define IPCMSG_BATTERY 0xEF + +/* Battery coulomb counter accumulator commands */ +#define IPC_CMD_CC_WR 0 /* Update coulomb counter value */ +#define IPC_CMD_CC_RD 1 /* Read coulomb counter value */ +#define IPC_CMD_BATTERY_PROPERTY 2 /* Read Battery property */ + +/** + * pmic_scu_ipc_battery_cc_read - read battery cc + * @value: battery coulomb counter read + * + * Reads the battery couloumb counter value, returns 0 on success, or + * an error code + * + * This function may sleep. Locking for SCU accesses is handled for + * the caller. + */ +static int pmic_scu_ipc_battery_cc_read(u32 *value) +{ + return intel_scu_ipc_command(IPCMSG_BATTERY, IPC_CMD_CC_RD, + NULL, 0, value, 1); +} + +/** + * pmic_scu_ipc_battery_property_get - fetch properties + * @prop: battery properties + * + * Retrieve the battery properties from the power management + * + * This function may sleep. Locking for SCU accesses is handled for + * the caller. + */ +static int pmic_scu_ipc_battery_property_get(struct battery_property *prop) +{ + u32 data[3]; + u8 *p = (u8 *)&data[1]; + int err = intel_scu_ipc_command(IPC_CMD_BATTERY_PROPERTY, + IPCMSG_BATTERY, NULL, 0, data, 3); + + prop->capacity = data[0]; + prop->crnt = *p++; + prop->volt = *p++; + prop->prot = *p++; + prop->prot2 = *p++; + prop->timer = *p++; + + return err; +} + +/** + * pmic_scu_ipc_set_charger - set charger + * @charger: charger to select + * + * Switch the charging mode for the SCU + */ + +static int pmic_scu_ipc_set_charger(int charger) +{ + return intel_scu_ipc_simple_command(charger, IPCMSG_BATTERY); +} + +/** + * pmic_battery_log_event - log battery events + * @event: battery event to be logged + * Context: can sleep + * + * There are multiple battery events which may be of interest to users; + * this battery function logs the different battery events onto the + * kernel log messages. + */ +static void pmic_battery_log_event(enum batt_event event) +{ + printk(KERN_WARNING "pmic-battery: "); + switch (event) { + case BATT_EVENT_BATOVP_EXCPT: + printk(KERN_CONT "battery overvoltage condition\n"); + break; + case BATT_EVENT_USBOVP_EXCPT: + printk(KERN_CONT "usb charger overvoltage condition\n"); + break; + case BATT_EVENT_TEMP_EXCPT: + printk(KERN_CONT "high battery temperature condition\n"); + break; + case BATT_EVENT_DCLMT_EXCPT: + printk(KERN_CONT "over battery charge current condition\n"); + break; + default: + printk(KERN_CONT "charger/battery exception %d\n", event); + break; + } +} + +/** + * pmic_battery_read_status - read battery status information + * @pbi: device info structure to update the read information + * Context: can sleep + * + * PMIC power source information need to be updated based on the data read + * from the PMIC battery registers. + * + */ +static void pmic_battery_read_status(struct pmic_power_module_info *pbi) +{ + unsigned int update_time_intrvl; + unsigned int chrg_val; + u32 ccval; + u8 r8; + struct battery_property batt_prop; + int batt_present = 0; + int usb_present = 0; + int batt_exception = 0; + + /* make sure the last batt_status read happened delay_time before */ + if (pbi->update_time && time_before(jiffies, pbi->update_time + + msecs_to_jiffies(delay_time))) + return; + + update_time_intrvl = jiffies_to_msecs(jiffies - pbi->update_time); + pbi->update_time = jiffies; + + /* read coulomb counter registers and schrgint register */ + if (pmic_scu_ipc_battery_cc_read(&ccval)) { + dev_warn(pbi->dev, "%s(): ipc config cmd failed\n", + __func__); + return; + } + + if (intel_scu_ipc_ioread8(PMIC_BATT_CHR_SCHRGINT_ADDR, &r8)) { + dev_warn(pbi->dev, "%s(): ipc pmic read failed\n", + __func__); + return; + } + + /* + * set pmic_power_module_info members based on pmic register values + * read. + */ + + /* set batt_is_present */ + if (r8 & PMIC_BATT_CHR_SBATDET_MASK) { + pbi->batt_is_present = PMIC_BATT_PRESENT; + batt_present = 1; + } else { + pbi->batt_is_present = PMIC_BATT_NOT_PRESENT; + pbi->batt_health = POWER_SUPPLY_HEALTH_UNKNOWN; + pbi->batt_status = POWER_SUPPLY_STATUS_UNKNOWN; + } + + /* set batt_health */ + if (batt_present) { + if (r8 & PMIC_BATT_CHR_SBATOVP_MASK) { + pbi->batt_health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; + pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING; + pmic_battery_log_event(BATT_EVENT_BATOVP_EXCPT); + batt_exception = 1; + } else if (r8 & PMIC_BATT_CHR_SDCLMT_MASK) { + pbi->batt_health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; + pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING; + pmic_battery_log_event(BATT_EVENT_DCLMT_EXCPT); + batt_exception = 1; + } else if (r8 & PMIC_BATT_CHR_STEMP_MASK) { + pbi->batt_health = POWER_SUPPLY_HEALTH_OVERHEAT; + pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING; + pmic_battery_log_event(BATT_EVENT_TEMP_EXCPT); + batt_exception = 1; + } else { + pbi->batt_health = POWER_SUPPLY_HEALTH_GOOD; + } + } + + /* set usb_is_present */ + if (r8 & PMIC_BATT_CHR_SUSBDET_MASK) { + pbi->usb_is_present = PMIC_USB_PRESENT; + usb_present = 1; + } else { + pbi->usb_is_present = PMIC_USB_NOT_PRESENT; + pbi->usb_health = POWER_SUPPLY_HEALTH_UNKNOWN; + } + + if (usb_present) { + if (r8 & PMIC_BATT_CHR_SUSBOVP_MASK) { + pbi->usb_health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; + pmic_battery_log_event(BATT_EVENT_USBOVP_EXCPT); + } else { + pbi->usb_health = POWER_SUPPLY_HEALTH_GOOD; + } + } + + chrg_val = ccval & PMIC_BATT_ADC_ACCCHRGVAL_MASK; + + /* set batt_prev_charge_full to battery capacity the first time */ + if (!pbi->is_dev_info_updated) { + if (pmic_scu_ipc_battery_property_get(&batt_prop)) { + dev_warn(pbi->dev, "%s(): ipc config cmd failed\n", + __func__); + return; + } + pbi->batt_prev_charge_full = batt_prop.capacity; + } + + /* set batt_status */ + if (batt_present && !batt_exception) { + if (r8 & PMIC_BATT_CHR_SCOMP_MASK) { + pbi->batt_status = POWER_SUPPLY_STATUS_FULL; + pbi->batt_prev_charge_full = chrg_val; + } else if (ccval & PMIC_BATT_ADC_ACCCHRG_MASK) { + pbi->batt_status = POWER_SUPPLY_STATUS_DISCHARGING; + } else { + pbi->batt_status = POWER_SUPPLY_STATUS_CHARGING; + } + } + + /* set batt_charge_rate */ + if (pbi->is_dev_info_updated && batt_present && !batt_exception) { + if (pbi->batt_status == POWER_SUPPLY_STATUS_DISCHARGING) { + if (pbi->batt_charge_now - chrg_val) { + pbi->batt_charge_rate = ((pbi->batt_charge_now - + chrg_val) * 1000 * 60) / + update_time_intrvl; + } + } else if (pbi->batt_status == POWER_SUPPLY_STATUS_CHARGING) { + if (chrg_val - pbi->batt_charge_now) { + pbi->batt_charge_rate = ((chrg_val - + pbi->batt_charge_now) * 1000 * 60) / + update_time_intrvl; + } + } else + pbi->batt_charge_rate = 0; + } else { + pbi->batt_charge_rate = -1; + } + + /* batt_charge_now */ + if (batt_present && !batt_exception) + pbi->batt_charge_now = chrg_val; + else + pbi->batt_charge_now = -1; + + pbi->is_dev_info_updated = PMIC_BATT_DRV_INFO_UPDATED; +} + +/** + * pmic_usb_get_property - usb power source get property + * @psy: usb power supply context + * @psp: usb power source property + * @val: usb power source property value + * Context: can sleep + * + * PMIC usb power source property needs to be provided to power_supply + * subsytem for it to provide the information to users. + */ +static int pmic_usb_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct pmic_power_module_info *pbi = container_of(psy, + struct pmic_power_module_info, usb); + + /* update pmic_power_module_info members */ + pmic_battery_read_status(pbi); + + switch (psp) { + case POWER_SUPPLY_PROP_PRESENT: + val->intval = pbi->usb_is_present; + break; + case POWER_SUPPLY_PROP_HEALTH: + val->intval = pbi->usb_health; + break; + default: + return -EINVAL; + } + + return 0; +} + +static inline unsigned long mAStouAh(unsigned long v) +{ + /* seconds to hours, mA to µA */ + return (v * 1000) / 3600; +} + +/** + * pmic_battery_get_property - battery power source get property + * @psy: battery power supply context + * @psp: battery power source property + * @val: battery power source property value + * Context: can sleep + * + * PMIC battery power source property needs to be provided to power_supply + * subsytem for it to provide the information to users. + */ +static int pmic_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct pmic_power_module_info *pbi = container_of(psy, + struct pmic_power_module_info, batt); + + /* update pmic_power_module_info members */ + pmic_battery_read_status(pbi); + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = pbi->batt_status; + break; + case POWER_SUPPLY_PROP_HEALTH: + val->intval = pbi->batt_health; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = pbi->batt_is_present; + break; + case POWER_SUPPLY_PROP_CHARGE_NOW: + val->intval = mAStouAh(pbi->batt_charge_now); + break; + case POWER_SUPPLY_PROP_CHARGE_FULL: + val->intval = mAStouAh(pbi->batt_prev_charge_full); + break; + default: + return -EINVAL; + } + + return 0; +} + +/** + * pmic_battery_monitor - monitor battery status + * @work: work structure + * Context: can sleep + * + * PMIC battery status needs to be monitored for any change + * and information needs to be frequently updated. + */ +static void pmic_battery_monitor(struct work_struct *work) +{ + struct pmic_power_module_info *pbi = container_of(work, + struct pmic_power_module_info, monitor_battery.work); + + /* update pmic_power_module_info members */ + pmic_battery_read_status(pbi); + queue_delayed_work(pbi->monitor_wqueue, &pbi->monitor_battery, HZ * 10); +} + +/** + * pmic_battery_set_charger - set battery charger + * @pbi: device info structure + * @chrg: charge mode to set battery charger in + * Context: can sleep + * + * PMIC battery charger needs to be enabled based on the usb charge + * capabilities connected to the platform. + */ +static int pmic_battery_set_charger(struct pmic_power_module_info *pbi, + enum batt_charge_type chrg) +{ + int retval; + + /* set usblmt bits and chrgcntl register bits appropriately */ + switch (chrg) { + case BATT_USBOTG_500MA_CHARGE: + retval = pmic_scu_ipc_set_charger(PMIC_BATT_CHR_IPC_FCHRG_SUBID); + break; + case BATT_USBOTG_TRICKLE_CHARGE: + retval = pmic_scu_ipc_set_charger(PMIC_BATT_CHR_IPC_TCHRG_SUBID); + break; + default: + dev_warn(pbi->dev, "%s(): out of range usb charger " + "charge detected\n", __func__); + return -EINVAL; + } + + if (retval) { + dev_warn(pbi->dev, "%s(): ipc pmic read failed\n", + __func__); + return retval;; + } + + return 0; +} + +/** + * pmic_battery_interrupt_handler - pmic battery interrupt handler + * Context: interrupt context + * + * PMIC battery interrupt handler which will be called with either + * battery full condition occurs or usb otg & battery connect + * condition occurs. + */ +static irqreturn_t pmic_battery_interrupt_handler(int id, void *dev) +{ + struct pmic_power_module_info *pbi = dev; + + schedule_work(&pbi->handler); + + return IRQ_HANDLED; +} + +/** + * pmic_battery_handle_intrpt - pmic battery service interrupt + * @work: work structure + * Context: can sleep + * + * PMIC battery needs to either update the battery status as full + * if it detects battery full condition caused the interrupt or needs + * to enable battery charger if it detects usb and battery detect + * caused the source of interrupt. + */ +static void pmic_battery_handle_intrpt(struct work_struct *work) +{ + struct pmic_power_module_info *pbi = container_of(work, + struct pmic_power_module_info, handler); + enum batt_charge_type chrg; + u8 r8; + + if (intel_scu_ipc_ioread8(PMIC_BATT_CHR_SCHRGINT_ADDR, &r8)) { + dev_warn(pbi->dev, "%s(): ipc pmic read failed\n", + __func__); + return; + } + /* find the cause of the interrupt */ + if (r8 & PMIC_BATT_CHR_SBATDET_MASK) { + pbi->batt_is_present = PMIC_BATT_PRESENT; + } else { + pbi->batt_is_present = PMIC_BATT_NOT_PRESENT; + pbi->batt_health = POWER_SUPPLY_HEALTH_UNKNOWN; + pbi->batt_status = POWER_SUPPLY_STATUS_UNKNOWN; + return; + } + + if (r8 & PMIC_BATT_CHR_EXCPT_MASK) { + pbi->batt_health = POWER_SUPPLY_HEALTH_UNKNOWN; + pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING; + pbi->usb_health = POWER_SUPPLY_HEALTH_UNKNOWN; + pmic_battery_log_event(BATT_EVENT_EXCPT); + return; + } else { + pbi->batt_health = POWER_SUPPLY_HEALTH_GOOD; + pbi->usb_health = POWER_SUPPLY_HEALTH_GOOD; + } + + if (r8 & PMIC_BATT_CHR_SCOMP_MASK) { + u32 ccval; + pbi->batt_status = POWER_SUPPLY_STATUS_FULL; + + if (pmic_scu_ipc_battery_cc_read(&ccval)) { + dev_warn(pbi->dev, "%s(): ipc config cmd " + "failed\n", __func__); + return; + } + pbi->batt_prev_charge_full = ccval & + PMIC_BATT_ADC_ACCCHRGVAL_MASK; + return; + } + + if (r8 & PMIC_BATT_CHR_SUSBDET_MASK) { + pbi->usb_is_present = PMIC_USB_PRESENT; + } else { + pbi->usb_is_present = PMIC_USB_NOT_PRESENT; + pbi->usb_health = POWER_SUPPLY_HEALTH_UNKNOWN; + return; + } + + /* setup battery charging */ + +#if 0 + /* check usb otg power capability and set charger accordingly */ + retval = langwell_udc_maxpower(&power); + if (retval) { + dev_warn(pbi->dev, + "%s(): usb otg power query failed with error code %d\n", + __func__, retval); + return; + } + + if (power >= 500) + chrg = BATT_USBOTG_500MA_CHARGE; + else +#endif + chrg = BATT_USBOTG_TRICKLE_CHARGE; + + /* enable battery charging */ + if (pmic_battery_set_charger(pbi, chrg)) { + dev_warn(pbi->dev, + "%s(): failed to set up battery charging\n", __func__); + return; + } + + dev_dbg(pbi->dev, + "pmic-battery: %s() - setting up battery charger successful\n", + __func__); +} + +/** + * pmic_battery_probe - pmic battery initialize + * @irq: pmic battery device irq + * @dev: pmic battery device structure + * Context: can sleep + * + * PMIC battery initializes its internal data structue and other + * infrastructure components for it to work as expected. + */ +static __devinit int probe(int irq, struct device *dev) +{ + int retval = 0; + struct pmic_power_module_info *pbi; + + dev_dbg(dev, "pmic-battery: found pmic battery device\n"); + + pbi = kzalloc(sizeof(*pbi), GFP_KERNEL); + if (!pbi) { + dev_err(dev, "%s(): memory allocation failed\n", + __func__); + return -ENOMEM; + } + + pbi->dev = dev; + pbi->irq = irq; + dev_set_drvdata(dev, pbi); + + /* initialize all required framework before enabling interrupts */ + INIT_WORK(&pbi->handler, pmic_battery_handle_intrpt); + INIT_DELAYED_WORK(&pbi->monitor_battery, pmic_battery_monitor); + pbi->monitor_wqueue = + create_singlethread_workqueue(dev_name(dev)); + if (!pbi->monitor_wqueue) { + dev_err(dev, "%s(): wqueue init failed\n", __func__); + retval = -ESRCH; + goto wqueue_failed; + } + + /* register interrupt */ + retval = request_irq(pbi->irq, pmic_battery_interrupt_handler, + 0, DRIVER_NAME, pbi); + if (retval) { + dev_err(dev, "%s(): cannot get IRQ\n", __func__); + goto requestirq_failed; + } + + /* register pmic-batt with power supply subsystem */ + pbi->batt.name = "pmic-batt"; + pbi->batt.type = POWER_SUPPLY_TYPE_BATTERY; + pbi->batt.properties = pmic_battery_props; + pbi->batt.num_properties = ARRAY_SIZE(pmic_battery_props); + pbi->batt.get_property = pmic_battery_get_property; + retval = power_supply_register(dev, &pbi->batt); + if (retval) { + dev_err(dev, + "%s(): failed to register pmic battery device with power supply subsystem\n", + __func__); + goto power_reg_failed; + } + + dev_dbg(dev, "pmic-battery: %s() - pmic battery device " + "registration with power supply subsystem successful\n", + __func__); + + queue_delayed_work(pbi->monitor_wqueue, &pbi->monitor_battery, HZ * 1); + + /* register pmic-usb with power supply subsystem */ + pbi->usb.name = "pmic-usb"; + pbi->usb.type = POWER_SUPPLY_TYPE_USB; + pbi->usb.properties = pmic_usb_props; + pbi->usb.num_properties = ARRAY_SIZE(pmic_usb_props); + pbi->usb.get_property = pmic_usb_get_property; + retval = power_supply_register(dev, &pbi->usb); + if (retval) { + dev_err(dev, + "%s(): failed to register pmic usb device with power supply subsystem\n", + __func__); + goto power_reg_failed_1; + } + + if (debug) + printk(KERN_INFO "pmic-battery: %s() - pmic usb device " + "registration with power supply subsystem successful\n", + __func__); + + return retval; + +power_reg_failed_1: + power_supply_unregister(&pbi->batt); +power_reg_failed: + cancel_rearming_delayed_workqueue(pbi->monitor_wqueue, + &pbi->monitor_battery); +requestirq_failed: + destroy_workqueue(pbi->monitor_wqueue); +wqueue_failed: + kfree(pbi); + + return retval; +} + +static int __devinit platform_pmic_battery_probe(struct platform_device *pdev) +{ + return probe(pdev->id, &pdev->dev); +} + +/** + * pmic_battery_remove - pmic battery finalize + * @dev: pmic battery device structure + * Context: can sleep + * + * PMIC battery finalizes its internal data structue and other + * infrastructure components that it initialized in + * pmic_battery_probe. + */ + +static int __devexit platform_pmic_battery_remove(struct platform_device *pdev) +{ + struct pmic_power_module_info *pbi = dev_get_drvdata(&pdev->dev); + + free_irq(pbi->irq, pbi); + cancel_rearming_delayed_workqueue(pbi->monitor_wqueue, + &pbi->monitor_battery); + destroy_workqueue(pbi->monitor_wqueue); + + power_supply_unregister(&pbi->usb); + power_supply_unregister(&pbi->batt); + + flush_scheduled_work(); + kfree(pbi); + return 0; +} + +static struct platform_driver platform_pmic_battery_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = platform_pmic_battery_probe, + .remove = __devexit_p(platform_pmic_battery_remove), +}; + +static int __init platform_pmic_battery_module_init(void) +{ + return platform_driver_register(&platform_pmic_battery_driver); +} + +static void __exit platform_pmic_battery_module_exit(void) +{ + platform_driver_unregister(&platform_pmic_battery_driver); +} + +module_init(platform_pmic_battery_module_init); +module_exit(platform_pmic_battery_module_exit); + +MODULE_AUTHOR("Nithish Mahalingam <nithish.mahalingam@intel.com>"); +MODULE_DESCRIPTION("Intel Moorestown PMIC Battery Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/olpc_battery.c b/drivers/power/olpc_battery.c index 5ab9109b69e..aafc1c506ed 100644 --- a/drivers/power/olpc_battery.c +++ b/drivers/power/olpc_battery.c @@ -1,7 +1,7 @@ /* * Battery driver for One Laptop Per Child board. * - * Copyright © 2006 David Woodhouse <dwmw2@infradead.org> + * Copyright © 2006-2010 David Woodhouse <dwmw2@infradead.org> * * 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 diff --git a/drivers/power/s3c_adc_battery.c b/drivers/power/s3c_adc_battery.c new file mode 100644 index 00000000000..fe16b482e91 --- /dev/null +++ b/drivers/power/s3c_adc_battery.c @@ -0,0 +1,431 @@ +/* + * iPAQ h1930/h1940/rx1950 battery controler driver + * Copyright (c) Vasily Khoruzhick + * Based on h1940_battery.c by Arnaud Patard + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + * + */ + +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/power_supply.h> +#include <linux/leds.h> +#include <linux/gpio.h> +#include <linux/err.h> +#include <linux/timer.h> +#include <linux/jiffies.h> +#include <linux/s3c_adc_battery.h> +#include <linux/errno.h> +#include <linux/init.h> + +#include <plat/adc.h> + +#define BAT_POLL_INTERVAL 10000 /* ms */ +#define JITTER_DELAY 500 /* ms */ + +struct s3c_adc_bat { + struct power_supply psy; + struct s3c_adc_client *client; + struct s3c_adc_bat_pdata *pdata; + int volt_value; + int cur_value; + unsigned int timestamp; + int level; + int status; + int cable_plugged:1; +}; + +static struct delayed_work bat_work; + +static void s3c_adc_bat_ext_power_changed(struct power_supply *psy) +{ + schedule_delayed_work(&bat_work, + msecs_to_jiffies(JITTER_DELAY)); +} + +static enum power_supply_property s3c_adc_backup_bat_props[] = { + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_VOLTAGE_MIN, + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, +}; + +static int s3c_adc_backup_bat_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct s3c_adc_bat *bat = container_of(psy, struct s3c_adc_bat, psy); + + if (!bat) { + dev_err(psy->dev, "%s: no battery infos ?!\n", __func__); + return -EINVAL; + } + + if (bat->volt_value < 0 || + jiffies_to_msecs(jiffies - bat->timestamp) > + BAT_POLL_INTERVAL) { + bat->volt_value = s3c_adc_read(bat->client, + bat->pdata->backup_volt_channel); + bat->volt_value *= bat->pdata->backup_volt_mult; + bat->timestamp = jiffies; + } + + switch (psp) { + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = bat->volt_value; + return 0; + case POWER_SUPPLY_PROP_VOLTAGE_MIN: + val->intval = bat->pdata->backup_volt_min; + return 0; + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + val->intval = bat->pdata->backup_volt_max; + return 0; + default: + return -EINVAL; + } +} + +static struct s3c_adc_bat backup_bat = { + .psy = { + .name = "backup-battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = s3c_adc_backup_bat_props, + .num_properties = ARRAY_SIZE(s3c_adc_backup_bat_props), + .get_property = s3c_adc_backup_bat_get_property, + .use_for_apm = 1, + }, +}; + +static enum power_supply_property s3c_adc_main_bat_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN, + POWER_SUPPLY_PROP_CHARGE_NOW, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, +}; + +static int calc_full_volt(int volt_val, int cur_val, int impedance) +{ + return volt_val + cur_val * impedance / 1000; +} + +static int s3c_adc_bat_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct s3c_adc_bat *bat = container_of(psy, struct s3c_adc_bat, psy); + + int new_level; + int full_volt; + const struct s3c_adc_bat_thresh *lut = bat->pdata->lut_noac; + unsigned int lut_size = bat->pdata->lut_noac_cnt; + + if (!bat) { + dev_err(psy->dev, "no battery infos ?!\n"); + return -EINVAL; + } + + if (bat->volt_value < 0 || bat->cur_value < 0 || + jiffies_to_msecs(jiffies - bat->timestamp) > + BAT_POLL_INTERVAL) { + bat->volt_value = s3c_adc_read(bat->client, + bat->pdata->volt_channel) * bat->pdata->volt_mult; + bat->cur_value = s3c_adc_read(bat->client, + bat->pdata->current_channel) * bat->pdata->current_mult; + bat->timestamp = jiffies; + } + + if (bat->cable_plugged && + ((bat->pdata->gpio_charge_finished < 0) || + !gpio_get_value(bat->pdata->gpio_charge_finished))) { + lut = bat->pdata->lut_acin; + lut_size = bat->pdata->lut_acin_cnt; + } + + new_level = 100000; + full_volt = calc_full_volt((bat->volt_value / 1000), + (bat->cur_value / 1000), bat->pdata->internal_impedance); + + if (full_volt < calc_full_volt(lut->volt, lut->cur, + bat->pdata->internal_impedance)) { + lut_size--; + while (lut_size--) { + int lut_volt1; + int lut_volt2; + + lut_volt1 = calc_full_volt(lut[0].volt, lut[0].cur, + bat->pdata->internal_impedance); + lut_volt2 = calc_full_volt(lut[1].volt, lut[1].cur, + bat->pdata->internal_impedance); + if (full_volt < lut_volt1 && full_volt >= lut_volt2) { + new_level = (lut[1].level + + (lut[0].level - lut[1].level) * + (full_volt - lut_volt2) / + (lut_volt1 - lut_volt2)) * 1000; + break; + } + new_level = lut[1].level * 1000; + lut++; + } + } + + bat->level = new_level; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + if (bat->pdata->gpio_charge_finished < 0) + val->intval = bat->level == 100000 ? + POWER_SUPPLY_STATUS_FULL : bat->status; + else + val->intval = bat->status; + return 0; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + val->intval = 100000; + return 0; + case POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN: + val->intval = 0; + return 0; + case POWER_SUPPLY_PROP_CHARGE_NOW: + val->intval = bat->level; + return 0; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = bat->volt_value; + return 0; + case POWER_SUPPLY_PROP_CURRENT_NOW: + val->intval = bat->cur_value; + return 0; + default: + return -EINVAL; + } +} + +static struct s3c_adc_bat main_bat = { + .psy = { + .name = "main-battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = s3c_adc_main_bat_props, + .num_properties = ARRAY_SIZE(s3c_adc_main_bat_props), + .get_property = s3c_adc_bat_get_property, + .external_power_changed = s3c_adc_bat_ext_power_changed, + .use_for_apm = 1, + }, +}; + +static void s3c_adc_bat_work(struct work_struct *work) +{ + struct s3c_adc_bat *bat = &main_bat; + int is_charged; + int is_plugged; + static int was_plugged; + + is_plugged = power_supply_am_i_supplied(&bat->psy); + bat->cable_plugged = is_plugged; + if (is_plugged != was_plugged) { + was_plugged = is_plugged; + if (is_plugged) { + if (bat->pdata->enable_charger) + bat->pdata->enable_charger(); + bat->status = POWER_SUPPLY_STATUS_CHARGING; + } else { + if (bat->pdata->disable_charger) + bat->pdata->disable_charger(); + bat->status = POWER_SUPPLY_STATUS_DISCHARGING; + } + } else { + if ((bat->pdata->gpio_charge_finished >= 0) && is_plugged) { + is_charged = gpio_get_value( + main_bat.pdata->gpio_charge_finished); + if (is_charged) { + if (bat->pdata->disable_charger) + bat->pdata->disable_charger(); + bat->status = POWER_SUPPLY_STATUS_FULL; + } else { + if (bat->pdata->enable_charger) + bat->pdata->enable_charger(); + bat->status = POWER_SUPPLY_STATUS_CHARGING; + } + } + } + + power_supply_changed(&bat->psy); +} + +static irqreturn_t s3c_adc_bat_charged(int irq, void *dev_id) +{ + schedule_delayed_work(&bat_work, + msecs_to_jiffies(JITTER_DELAY)); + return IRQ_HANDLED; +} + +static int __init s3c_adc_bat_probe(struct platform_device *pdev) +{ + struct s3c_adc_client *client; + struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data; + int ret; + + client = s3c_adc_register(pdev, NULL, NULL, 0); + if (IS_ERR(client)) { + dev_err(&pdev->dev, "cannot register adc\n"); + return PTR_ERR(client); + } + + platform_set_drvdata(pdev, client); + + main_bat.client = client; + main_bat.pdata = pdata; + main_bat.volt_value = -1; + main_bat.cur_value = -1; + main_bat.cable_plugged = 0; + main_bat.status = POWER_SUPPLY_STATUS_DISCHARGING; + + ret = power_supply_register(&pdev->dev, &main_bat.psy); + if (ret) + goto err_reg_main; + if (pdata->backup_volt_mult) { + backup_bat.client = client; + backup_bat.pdata = pdev->dev.platform_data; + backup_bat.volt_value = -1; + ret = power_supply_register(&pdev->dev, &backup_bat.psy); + if (ret) + goto err_reg_backup; + } + + INIT_DELAYED_WORK(&bat_work, s3c_adc_bat_work); + + if (pdata->gpio_charge_finished >= 0) { + ret = gpio_request(pdata->gpio_charge_finished, "charged"); + if (ret) + goto err_gpio; + + ret = request_irq(gpio_to_irq(pdata->gpio_charge_finished), + s3c_adc_bat_charged, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "battery charged", NULL); + if (ret) + goto err_irq; + } + + if (pdata->init) { + ret = pdata->init(); + if (ret) + goto err_platform; + } + + dev_info(&pdev->dev, "successfully loaded\n"); + device_init_wakeup(&pdev->dev, 1); + + /* Schedule timer to check current status */ + schedule_delayed_work(&bat_work, + msecs_to_jiffies(JITTER_DELAY)); + + return 0; + +err_platform: + if (pdata->gpio_charge_finished >= 0) + free_irq(gpio_to_irq(pdata->gpio_charge_finished), NULL); +err_irq: + if (pdata->gpio_charge_finished >= 0) + gpio_free(pdata->gpio_charge_finished); +err_gpio: + if (pdata->backup_volt_mult) + power_supply_unregister(&backup_bat.psy); +err_reg_backup: + power_supply_unregister(&main_bat.psy); +err_reg_main: + return ret; +} + +static int s3c_adc_bat_remove(struct platform_device *pdev) +{ + struct s3c_adc_client *client = platform_get_drvdata(pdev); + struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data; + + power_supply_unregister(&main_bat.psy); + if (pdata->backup_volt_mult) + power_supply_unregister(&backup_bat.psy); + + s3c_adc_release(client); + + if (pdata->gpio_charge_finished >= 0) { + free_irq(gpio_to_irq(pdata->gpio_charge_finished), NULL); + gpio_free(pdata->gpio_charge_finished); + } + + cancel_delayed_work(&bat_work); + + if (pdata->exit) + pdata->exit(); + + return 0; +} + +#ifdef CONFIG_PM +static int s3c_adc_bat_suspend(struct platform_device *pdev, + pm_message_t state) +{ + struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data; + + if (pdata->gpio_charge_finished >= 0) { + if (device_may_wakeup(&pdev->dev)) + enable_irq_wake( + gpio_to_irq(pdata->gpio_charge_finished)); + else { + disable_irq(gpio_to_irq(pdata->gpio_charge_finished)); + main_bat.pdata->disable_charger(); + } + } + + return 0; +} + +static int s3c_adc_bat_resume(struct platform_device *pdev) +{ + struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data; + + if (pdata->gpio_charge_finished >= 0) { + if (device_may_wakeup(&pdev->dev)) + disable_irq_wake( + gpio_to_irq(pdata->gpio_charge_finished)); + else + enable_irq(gpio_to_irq(pdata->gpio_charge_finished)); + } + + /* Schedule timer to check current status */ + schedule_delayed_work(&bat_work, + msecs_to_jiffies(JITTER_DELAY)); + + return 0; +} +#else +#define s3c_adc_battery_suspend NULL +#define s3c_adc_battery_resume NULL +#endif + +static struct platform_driver s3c_adc_bat_driver = { + .driver = { + .name = "s3c-adc-battery", + }, + .probe = s3c_adc_bat_probe, + .remove = s3c_adc_bat_remove, + .suspend = s3c_adc_bat_suspend, + .resume = s3c_adc_bat_resume, +}; + +static int __init s3c_adc_bat_init(void) +{ + return platform_driver_register(&s3c_adc_bat_driver); +} +module_init(s3c_adc_bat_init); + +static void __exit s3c_adc_bat_exit(void) +{ + platform_driver_unregister(&s3c_adc_bat_driver); +} +module_exit(s3c_adc_bat_exit); + +MODULE_AUTHOR("Vasily Khoruzhick <anarsoul@gmail.com>"); +MODULE_DESCRIPTION("iPAQ H1930/H1940/RX1950 battery controler driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/wm97xx_battery.c b/drivers/power/wm97xx_battery.c index 4e8afce0c81..5071d85ec12 100644 --- a/drivers/power/wm97xx_battery.c +++ b/drivers/power/wm97xx_battery.c @@ -29,7 +29,6 @@ static DEFINE_MUTEX(bat_lock); static struct work_struct bat_work; static struct mutex work_lock; static int bat_status = POWER_SUPPLY_STATUS_UNKNOWN; -static struct wm97xx_batt_info *gpdata; static enum power_supply_property *prop; static unsigned long wm97xx_read_bat(struct power_supply *bat_ps) @@ -172,12 +171,6 @@ static int __devinit wm97xx_bat_probe(struct platform_device *dev) struct wm97xx_pdata *wmdata = dev->dev.platform_data; struct wm97xx_batt_pdata *pdata; - if (gpdata) { - dev_err(&dev->dev, "Do not pass platform_data through " - "wm97xx_bat_set_pdata!\n"); - return -EINVAL; - } - if (!wmdata) { dev_err(&dev->dev, "No platform data supplied\n"); return -EINVAL; @@ -308,15 +301,6 @@ static void __exit wm97xx_bat_exit(void) platform_driver_unregister(&wm97xx_bat_driver); } -/* The interface is deprecated, as well as linux/wm97xx_batt.h */ -void wm97xx_bat_set_pdata(struct wm97xx_batt_info *data); - -void wm97xx_bat_set_pdata(struct wm97xx_batt_info *data) -{ - gpdata = data; -} -EXPORT_SYMBOL_GPL(wm97xx_bat_set_pdata); - module_init(wm97xx_bat_init); module_exit(wm97xx_bat_exit); diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 04f2e085116..172951bf23a 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -100,6 +100,14 @@ config REGULATOR_MAX8925 help Say y here to support the voltage regulaltor of Maxim MAX8925 PMIC. +config REGULATOR_MAX8998 + tristate "Maxim 8998 voltage regulator" + depends on MFD_MAX8998 + help + This driver controls a Maxim 8998 voltage output regulator + via I2C bus. The provided regulator is suitable for S3C6410 + and S5PC1XX chips to control VCC_CORE and VCC_USIM voltages. + config REGULATOR_TWL4030 bool "TI TWL4030/TWL5030/TWL6030/TPS695x0 PMIC" depends on TWL4030_CORE @@ -201,5 +209,31 @@ config REGULATOR_88PM8607 help This driver supports 88PM8607 voltage regulator chips. +config REGULATOR_ISL6271A + tristate "Intersil ISL6271A Power regulator" + depends on I2C + help + This driver supports ISL6271A voltage regulator chip. + +config REGULATOR_AD5398 + tristate "Analog Devices AD5398/AD5821 regulators" + depends on I2C + help + This driver supports AD5398 and AD5821 current regulator chips. + If building into module, its name is ad5398.ko. + +config REGULATOR_AB8500 + bool "ST-Ericsson AB8500 Power Regulators" + depends on AB8500_CORE + help + This driver supports the regulators found on the ST-Ericsson mixed + signal AB8500 PMIC + +config REGULATOR_TPS6586X + tristate "TI TPS6586X Power regulators" + depends on MFD_TPS6586X + help + This driver supports TPS6586X voltage regulator chips. + endif diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 4e7feece22d..8285fd832e1 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_REGULATOR_FIXED_VOLTAGE) += fixed.o obj-$(CONFIG_REGULATOR_VIRTUAL_CONSUMER) += virtual.o obj-$(CONFIG_REGULATOR_USERSPACE_CONSUMER) += userspace-consumer.o +obj-$(CONFIG_REGULATOR_AD5398) += ad5398.o obj-$(CONFIG_REGULATOR_BQ24022) += bq24022.o obj-$(CONFIG_REGULATOR_DUMMY) += dummy.o obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o @@ -16,12 +17,14 @@ obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o obj-$(CONFIG_REGULATOR_MAX8649) += max8649.o obj-$(CONFIG_REGULATOR_MAX8660) += max8660.o obj-$(CONFIG_REGULATOR_MAX8925) += max8925-regulator.o +obj-$(CONFIG_REGULATOR_MAX8998) += max8998.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-ldo.o obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o obj-$(CONFIG_REGULATOR_WM8994) += wm8994-regulator.o +obj-$(CONFIG_REGULATOR_TPS6586X) += tps6586x-regulator.o obj-$(CONFIG_REGULATOR_DA903X) += da903x.o obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o @@ -31,5 +34,7 @@ obj-$(CONFIG_REGULATOR_AB3100) += ab3100.o obj-$(CONFIG_REGULATOR_TPS65023) += tps65023-regulator.o obj-$(CONFIG_REGULATOR_TPS6507X) += tps6507x-regulator.o obj-$(CONFIG_REGULATOR_88PM8607) += 88pm8607.o +obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o +obj-$(CONFIG_REGULATOR_AB8500) += ab8500.o ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG diff --git a/drivers/regulator/ab8500.c b/drivers/regulator/ab8500.c new file mode 100644 index 00000000000..dc3f1a49167 --- /dev/null +++ b/drivers/regulator/ab8500.c @@ -0,0 +1,427 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * License Terms: GNU General Public License v2 + * + * Author: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson + * + * AB8500 peripheral regulators + * + * AB8500 supports the following regulators, + * LDOs - VAUDIO, VANAMIC2/2, VDIGMIC, VINTCORE12, VTVOUT, + * VAUX1/2/3, VANA + * + * for DB8500 cut 1.0 and previous versions of the silicon, all accesses + * to registers are through the DB8500 SPI. In cut 1.1 onwards, these + * accesses are through the DB8500 PRCMU I2C + * + */ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/mfd/ab8500.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/ab8500.h> + +/** + * struct ab8500_regulator_info - ab8500 regulator information + * @desc: regulator description + * @ab8500: ab8500 parent + * @regulator_dev: regulator device + * @max_uV: maximum voltage (for variable voltage supplies) + * @min_uV: minimum voltage (for variable voltage supplies) + * @fixed_uV: typical voltage (for fixed voltage supplies) + * @update_reg: register to control on/off + * @mask: mask to enable/disable regulator + * @enable: bits to enable the regulator in normal(high power) mode + * @voltage_reg: register to control regulator voltage + * @voltage_mask: mask to control regulator voltage + * @supported_voltages: supported voltage table + * @voltages_len: number of supported voltages for the regulator + */ +struct ab8500_regulator_info { + struct device *dev; + struct regulator_desc desc; + struct ab8500 *ab8500; + struct regulator_dev *regulator; + int max_uV; + int min_uV; + int fixed_uV; + int update_reg; + int mask; + int enable; + int voltage_reg; + int voltage_mask; + int const *supported_voltages; + int voltages_len; +}; + +/* voltage tables for the vauxn/vintcore supplies */ +static const int ldo_vauxn_voltages[] = { + 1100000, + 1200000, + 1300000, + 1400000, + 1500000, + 1800000, + 1850000, + 1900000, + 2500000, + 2650000, + 2700000, + 2750000, + 2800000, + 2900000, + 3000000, + 3300000, +}; + +static const int ldo_vintcore_voltages[] = { + 1200000, + 1225000, + 1250000, + 1275000, + 1300000, + 1325000, + 1350000, +}; + +static int ab8500_regulator_enable(struct regulator_dev *rdev) +{ + int regulator_id, ret; + struct ab8500_regulator_info *info = rdev_get_drvdata(rdev); + + regulator_id = rdev_get_id(rdev); + if (regulator_id >= AB8500_NUM_REGULATORS) + return -EINVAL; + + ret = ab8500_set_bits(info->ab8500, info->update_reg, + info->mask, info->enable); + if (ret < 0) + dev_err(rdev_get_dev(rdev), + "couldn't set enable bits for regulator\n"); + return ret; +} + +static int ab8500_regulator_disable(struct regulator_dev *rdev) +{ + int regulator_id, ret; + struct ab8500_regulator_info *info = rdev_get_drvdata(rdev); + + regulator_id = rdev_get_id(rdev); + if (regulator_id >= AB8500_NUM_REGULATORS) + return -EINVAL; + + ret = ab8500_set_bits(info->ab8500, info->update_reg, + info->mask, 0x0); + if (ret < 0) + dev_err(rdev_get_dev(rdev), + "couldn't set disable bits for regulator\n"); + return ret; +} + +static int ab8500_regulator_is_enabled(struct regulator_dev *rdev) +{ + int regulator_id, ret; + struct ab8500_regulator_info *info = rdev_get_drvdata(rdev); + + regulator_id = rdev_get_id(rdev); + if (regulator_id >= AB8500_NUM_REGULATORS) + return -EINVAL; + + ret = ab8500_read(info->ab8500, info->update_reg); + if (ret < 0) { + dev_err(rdev_get_dev(rdev), + "couldn't read 0x%x register\n", info->update_reg); + return ret; + } + + if (ret & info->mask) + return true; + else + return false; +} + +static int ab8500_list_voltage(struct regulator_dev *rdev, unsigned selector) +{ + int regulator_id; + struct ab8500_regulator_info *info = rdev_get_drvdata(rdev); + + regulator_id = rdev_get_id(rdev); + if (regulator_id >= AB8500_NUM_REGULATORS) + return -EINVAL; + + /* return the uV for the fixed regulators */ + if (info->fixed_uV) + return info->fixed_uV; + + if (selector > info->voltages_len) + return -EINVAL; + + return info->supported_voltages[selector]; +} + +static int ab8500_regulator_get_voltage(struct regulator_dev *rdev) +{ + int regulator_id, ret, val; + struct ab8500_regulator_info *info = rdev_get_drvdata(rdev); + + regulator_id = rdev_get_id(rdev); + if (regulator_id >= AB8500_NUM_REGULATORS) + return -EINVAL; + + ret = ab8500_read(info->ab8500, info->voltage_reg); + if (ret < 0) { + dev_err(rdev_get_dev(rdev), + "couldn't read voltage reg for regulator\n"); + return ret; + } + + /* vintcore has a different layout */ + val = ret & info->voltage_mask; + if (regulator_id == AB8500_LDO_INTCORE) + ret = info->supported_voltages[val >> 0x3]; + else + ret = info->supported_voltages[val]; + + return ret; +} + +static int ab8500_get_best_voltage_index(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + struct ab8500_regulator_info *info = rdev_get_drvdata(rdev); + int i; + + /* check the supported voltage */ + for (i = 0; i < info->voltages_len; i++) { + if ((info->supported_voltages[i] >= min_uV) && + (info->supported_voltages[i] <= max_uV)) + return i; + } + + return -EINVAL; +} + +static int ab8500_regulator_set_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + int regulator_id, ret; + struct ab8500_regulator_info *info = rdev_get_drvdata(rdev); + + regulator_id = rdev_get_id(rdev); + if (regulator_id >= AB8500_NUM_REGULATORS) + return -EINVAL; + + /* get the appropriate voltages within the range */ + ret = ab8500_get_best_voltage_index(rdev, min_uV, max_uV); + if (ret < 0) { + dev_err(rdev_get_dev(rdev), + "couldn't get best voltage for regulator\n"); + return ret; + } + + /* set the registers for the request */ + ret = ab8500_set_bits(info->ab8500, info->voltage_reg, + info->voltage_mask, ret); + if (ret < 0) + dev_err(rdev_get_dev(rdev), + "couldn't set voltage reg for regulator\n"); + + return ret; +} + +static struct regulator_ops ab8500_regulator_ops = { + .enable = ab8500_regulator_enable, + .disable = ab8500_regulator_disable, + .is_enabled = ab8500_regulator_is_enabled, + .get_voltage = ab8500_regulator_get_voltage, + .set_voltage = ab8500_regulator_set_voltage, + .list_voltage = ab8500_list_voltage, +}; + +static int ab8500_fixed_get_voltage(struct regulator_dev *rdev) +{ + int regulator_id; + struct ab8500_regulator_info *info = rdev_get_drvdata(rdev); + + regulator_id = rdev_get_id(rdev); + if (regulator_id >= AB8500_NUM_REGULATORS) + return -EINVAL; + + return info->fixed_uV; +} + +static struct regulator_ops ab8500_ldo_fixed_ops = { + .enable = ab8500_regulator_enable, + .disable = ab8500_regulator_disable, + .is_enabled = ab8500_regulator_is_enabled, + .get_voltage = ab8500_fixed_get_voltage, + .list_voltage = ab8500_list_voltage, +}; + +#define AB8500_LDO(_id, min, max, reg, reg_mask, reg_enable, \ + volt_reg, volt_mask, voltages, \ + len_volts) \ +{ \ + .desc = { \ + .name = "LDO-" #_id, \ + .ops = &ab8500_regulator_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = AB8500_LDO_##_id, \ + .owner = THIS_MODULE, \ + }, \ + .min_uV = (min) * 1000, \ + .max_uV = (max) * 1000, \ + .update_reg = reg, \ + .mask = reg_mask, \ + .enable = reg_enable, \ + .voltage_reg = volt_reg, \ + .voltage_mask = volt_mask, \ + .supported_voltages = voltages, \ + .voltages_len = len_volts, \ + .fixed_uV = 0, \ +} + +#define AB8500_FIXED_LDO(_id, fixed, reg, reg_mask, \ + reg_enable) \ +{ \ + .desc = { \ + .name = "LDO-" #_id, \ + .ops = &ab8500_ldo_fixed_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = AB8500_LDO_##_id, \ + .owner = THIS_MODULE, \ + }, \ + .fixed_uV = fixed * 1000, \ + .update_reg = reg, \ + .mask = reg_mask, \ + .enable = reg_enable, \ +} + +static struct ab8500_regulator_info ab8500_regulator_info[] = { + /* + * Variable Voltage LDOs + * name, min uV, max uV, ctrl reg, reg mask, enable mask, + * volt ctrl reg, volt ctrl mask, volt table, num supported volts + */ + AB8500_LDO(AUX1, 1100, 3300, 0x0409, 0x3, 0x1, 0x041f, 0xf, + ldo_vauxn_voltages, ARRAY_SIZE(ldo_vauxn_voltages)), + AB8500_LDO(AUX2, 1100, 3300, 0x0409, 0xc, 0x4, 0x0420, 0xf, + ldo_vauxn_voltages, ARRAY_SIZE(ldo_vauxn_voltages)), + AB8500_LDO(AUX3, 1100, 3300, 0x040a, 0x3, 0x1, 0x0421, 0xf, + ldo_vauxn_voltages, ARRAY_SIZE(ldo_vauxn_voltages)), + AB8500_LDO(INTCORE, 1100, 3300, 0x0380, 0x4, 0x4, 0x0380, 0x38, + ldo_vintcore_voltages, ARRAY_SIZE(ldo_vintcore_voltages)), + + /* + * Fixed Voltage LDOs + * name, o/p uV, ctrl reg, enable, disable + */ + AB8500_FIXED_LDO(TVOUT, 2000, 0x0380, 0x2, 0x2), + AB8500_FIXED_LDO(AUDIO, 2000, 0x0383, 0x2, 0x2), + AB8500_FIXED_LDO(ANAMIC1, 2050, 0x0383, 0x4, 0x4), + AB8500_FIXED_LDO(ANAMIC2, 2050, 0x0383, 0x8, 0x8), + AB8500_FIXED_LDO(DMIC, 1800, 0x0383, 0x10, 0x10), + AB8500_FIXED_LDO(ANA, 1200, 0x0383, 0xc, 0x4), +}; + +static inline struct ab8500_regulator_info *find_regulator_info(int id) +{ + struct ab8500_regulator_info *info; + int i; + + for (i = 0; i < ARRAY_SIZE(ab8500_regulator_info); i++) { + info = &ab8500_regulator_info[i]; + if (info->desc.id == id) + return info; + } + return NULL; +} + +static __devinit int ab8500_regulator_probe(struct platform_device *pdev) +{ + struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent); + struct ab8500_platform_data *pdata = dev_get_platdata(ab8500->dev); + int i, err; + + if (!ab8500) { + dev_err(&pdev->dev, "null mfd parent\n"); + return -EINVAL; + } + + /* register all regulators */ + for (i = 0; i < ARRAY_SIZE(ab8500_regulator_info); i++) { + struct ab8500_regulator_info *info = NULL; + + /* assign per-regulator data */ + info = &ab8500_regulator_info[i]; + info->dev = &pdev->dev; + info->ab8500 = ab8500; + + info->regulator = regulator_register(&info->desc, &pdev->dev, + pdata->regulator[i], info); + if (IS_ERR(info->regulator)) { + err = PTR_ERR(info->regulator); + dev_err(&pdev->dev, "failed to register regulator %s\n", + info->desc.name); + /* when we fail, un-register all earlier regulators */ + i--; + while (i > 0) { + info = &ab8500_regulator_info[i]; + regulator_unregister(info->regulator); + i--; + } + return err; + } + } + + return 0; +} + +static __devexit int ab8500_regulator_remove(struct platform_device *pdev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ab8500_regulator_info); i++) { + struct ab8500_regulator_info *info = NULL; + info = &ab8500_regulator_info[i]; + regulator_unregister(info->regulator); + } + + return 0; +} + +static struct platform_driver ab8500_regulator_driver = { + .probe = ab8500_regulator_probe, + .remove = __devexit_p(ab8500_regulator_remove), + .driver = { + .name = "ab8500-regulator", + .owner = THIS_MODULE, + }, +}; + +static int __init ab8500_regulator_init(void) +{ + int ret; + + ret = platform_driver_register(&ab8500_regulator_driver); + if (ret != 0) + pr_err("Failed to register ab8500 regulator: %d\n", ret); + + return ret; +} +subsys_initcall(ab8500_regulator_init); + +static void __exit ab8500_regulator_exit(void) +{ + platform_driver_unregister(&ab8500_regulator_driver); +} +module_exit(ab8500_regulator_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Sundar Iyer <sundar.iyer@stericsson.com>"); +MODULE_DESCRIPTION("Regulator Driver for ST-Ericsson AB8500 Mixed-Sig PMIC"); +MODULE_ALIAS("platform:ab8500-regulator"); diff --git a/drivers/regulator/ad5398.c b/drivers/regulator/ad5398.c new file mode 100644 index 00000000000..d59d2f2314a --- /dev/null +++ b/drivers/regulator/ad5398.c @@ -0,0 +1,288 @@ +/* + * Voltage and current regulation for AD5398 and AD5821 + * + * Copyright 2010 Analog Devices Inc. + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/module.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> + +#define AD5398_CURRENT_EN_MASK 0x8000 + +struct ad5398_chip_info { + struct i2c_client *client; + int min_uA; + int max_uA; + unsigned int current_level; + unsigned int current_mask; + unsigned int current_offset; + struct regulator_dev rdev; +}; + +static int ad5398_calc_current(struct ad5398_chip_info *chip, + unsigned selector) +{ + unsigned range_uA = chip->max_uA - chip->min_uA; + + return chip->min_uA + (selector * range_uA / chip->current_level); +} + +static int ad5398_read_reg(struct i2c_client *client, unsigned short *data) +{ + unsigned short val; + int ret; + + ret = i2c_master_recv(client, (char *)&val, 2); + if (ret < 0) { + dev_err(&client->dev, "I2C read error\n"); + return ret; + } + *data = be16_to_cpu(val); + + return ret; +} + +static int ad5398_write_reg(struct i2c_client *client, const unsigned short data) +{ + unsigned short val; + int ret; + + val = cpu_to_be16(data); + ret = i2c_master_send(client, (char *)&val, 2); + if (ret < 0) + dev_err(&client->dev, "I2C write error\n"); + + return ret; +} + +static int ad5398_get_current_limit(struct regulator_dev *rdev) +{ + struct ad5398_chip_info *chip = rdev_get_drvdata(rdev); + struct i2c_client *client = chip->client; + unsigned short data; + int ret; + + ret = ad5398_read_reg(client, &data); + if (ret < 0) + return ret; + + ret = (data & chip->current_mask) >> chip->current_offset; + + return ad5398_calc_current(chip, ret); +} + +static int ad5398_set_current_limit(struct regulator_dev *rdev, int min_uA, int max_uA) +{ + struct ad5398_chip_info *chip = rdev_get_drvdata(rdev); + struct i2c_client *client = chip->client; + unsigned range_uA = chip->max_uA - chip->min_uA; + unsigned selector; + unsigned short data; + int ret; + + if (min_uA > chip->max_uA || min_uA < chip->min_uA) + return -EINVAL; + if (max_uA > chip->max_uA || max_uA < chip->min_uA) + return -EINVAL; + + selector = ((min_uA - chip->min_uA) * chip->current_level + + range_uA - 1) / range_uA; + if (ad5398_calc_current(chip, selector) > max_uA) + return -EINVAL; + + dev_dbg(&client->dev, "changing current %dmA\n", + ad5398_calc_current(chip, selector) / 1000); + + /* read chip enable bit */ + ret = ad5398_read_reg(client, &data); + if (ret < 0) + return ret; + + /* prepare register data */ + selector = (selector << chip->current_offset) & chip->current_mask; + data = (unsigned short)selector | (data & AD5398_CURRENT_EN_MASK); + + /* write the new current value back as well as enable bit */ + ret = ad5398_write_reg(client, data); + + return ret; +} + +static int ad5398_is_enabled(struct regulator_dev *rdev) +{ + struct ad5398_chip_info *chip = rdev_get_drvdata(rdev); + struct i2c_client *client = chip->client; + unsigned short data; + int ret; + + ret = ad5398_read_reg(client, &data); + if (ret < 0) + return ret; + + if (data & AD5398_CURRENT_EN_MASK) + return 1; + else + return 0; +} + +static int ad5398_enable(struct regulator_dev *rdev) +{ + struct ad5398_chip_info *chip = rdev_get_drvdata(rdev); + struct i2c_client *client = chip->client; + unsigned short data; + int ret; + + ret = ad5398_read_reg(client, &data); + if (ret < 0) + return ret; + + if (data & AD5398_CURRENT_EN_MASK) + return 0; + + data |= AD5398_CURRENT_EN_MASK; + + ret = ad5398_write_reg(client, data); + + return ret; +} + +static int ad5398_disable(struct regulator_dev *rdev) +{ + struct ad5398_chip_info *chip = rdev_get_drvdata(rdev); + struct i2c_client *client = chip->client; + unsigned short data; + int ret; + + ret = ad5398_read_reg(client, &data); + if (ret < 0) + return ret; + + if (!(data & AD5398_CURRENT_EN_MASK)) + return 0; + + data &= ~AD5398_CURRENT_EN_MASK; + + ret = ad5398_write_reg(client, data); + + return ret; +} + +static struct regulator_ops ad5398_ops = { + .get_current_limit = ad5398_get_current_limit, + .set_current_limit = ad5398_set_current_limit, + .enable = ad5398_enable, + .disable = ad5398_disable, + .is_enabled = ad5398_is_enabled, +}; + +static struct regulator_desc ad5398_reg = { + .name = "isink", + .id = 0, + .ops = &ad5398_ops, + .type = REGULATOR_CURRENT, + .owner = THIS_MODULE, +}; + +struct ad5398_current_data_format { + int current_bits; + int current_offset; + int min_uA; + int max_uA; +}; + +static const struct ad5398_current_data_format df_10_4_120 = {10, 4, 0, 120000}; + +static const struct i2c_device_id ad5398_id[] = { + { "ad5398", (kernel_ulong_t)&df_10_4_120 }, + { "ad5821", (kernel_ulong_t)&df_10_4_120 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ad5398_id); + +static int __devinit ad5398_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct regulator_dev *rdev; + struct regulator_init_data *init_data = client->dev.platform_data; + struct ad5398_chip_info *chip; + const struct ad5398_current_data_format *df = + (struct ad5398_current_data_format *)id->driver_data; + int ret; + + if (!init_data) + return -EINVAL; + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->client = client; + + chip->min_uA = df->min_uA; + chip->max_uA = df->max_uA; + chip->current_level = 1 << df->current_bits; + chip->current_offset = df->current_offset; + chip->current_mask = (chip->current_level - 1) << chip->current_offset; + + rdev = regulator_register(&ad5398_reg, &client->dev, init_data, chip); + if (IS_ERR(rdev)) { + ret = PTR_ERR(rdev); + dev_err(&client->dev, "failed to register %s %s\n", + id->name, ad5398_reg.name); + goto err; + } + + i2c_set_clientdata(client, chip); + dev_dbg(&client->dev, "%s regulator driver is registered.\n", id->name); + return 0; + +err: + kfree(chip); + return ret; +} + +static int __devexit ad5398_remove(struct i2c_client *client) +{ + struct ad5398_chip_info *chip = i2c_get_clientdata(client); + + regulator_unregister(&chip->rdev); + kfree(chip); + i2c_set_clientdata(client, NULL); + + return 0; +} + +static struct i2c_driver ad5398_driver = { + .probe = ad5398_probe, + .remove = __devexit_p(ad5398_remove), + .driver = { + .name = "ad5398", + }, + .id_table = ad5398_id, +}; + +static int __init ad5398_init(void) +{ + return i2c_add_driver(&ad5398_driver); +} +subsys_initcall(ad5398_init); + +static void __exit ad5398_exit(void) +{ + i2c_del_driver(&ad5398_driver); +} +module_exit(ad5398_exit); + +MODULE_DESCRIPTION("AD5398 and AD5821 current regulator driver"); +MODULE_AUTHOR("Sonic Zhang"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("i2c:ad5398-regulator"); diff --git a/drivers/regulator/isl6271a-regulator.c b/drivers/regulator/isl6271a-regulator.c new file mode 100644 index 00000000000..e49d2bd393f --- /dev/null +++ b/drivers/regulator/isl6271a-regulator.c @@ -0,0 +1,236 @@ +/* + * isl6271a-regulator.c + * + * Support for Intersil ISL6271A voltage regulator + * + * Copyright (C) 2010 Marek Vasut <marek.vasut@gmail.com> + * + * 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. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, + * whether express or implied; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/i2c.h> +#include <linux/delay.h> +#include <linux/slab.h> + +#define ISL6271A_VOLTAGE_MIN 850000 +#define ISL6271A_VOLTAGE_MAX 1600000 +#define ISL6271A_VOLTAGE_STEP 50000 + +/* PMIC details */ +struct isl_pmic { + struct i2c_client *client; + struct regulator_dev *rdev[3]; + struct mutex mtx; +}; + +static int isl6271a_get_voltage(struct regulator_dev *dev) +{ + struct isl_pmic *pmic = rdev_get_drvdata(dev); + int idx, data; + + mutex_lock(&pmic->mtx); + + idx = i2c_smbus_read_byte(pmic->client); + if (idx < 0) { + dev_err(&pmic->client->dev, "Error getting voltage\n"); + data = idx; + goto out; + } + + /* Convert the data from chip to microvolts */ + data = ISL6271A_VOLTAGE_MIN + (ISL6271A_VOLTAGE_STEP * (idx & 0xf)); + +out: + mutex_unlock(&pmic->mtx); + return data; +} + +static int isl6271a_set_voltage(struct regulator_dev *dev, int minuV, int maxuV) +{ + struct isl_pmic *pmic = rdev_get_drvdata(dev); + int vsel, err, data; + + if (minuV < ISL6271A_VOLTAGE_MIN || minuV > ISL6271A_VOLTAGE_MAX) + return -EINVAL; + if (maxuV < ISL6271A_VOLTAGE_MIN || maxuV > ISL6271A_VOLTAGE_MAX) + return -EINVAL; + + /* Align to 50000 mV */ + vsel = minuV - (minuV % ISL6271A_VOLTAGE_STEP); + + /* If the result fell out of [minuV,maxuV] range, put it back */ + if (vsel < minuV) + vsel += ISL6271A_VOLTAGE_STEP; + + /* Convert the microvolts to data for the chip */ + data = (vsel - ISL6271A_VOLTAGE_MIN) / ISL6271A_VOLTAGE_STEP; + + mutex_lock(&pmic->mtx); + + err = i2c_smbus_write_byte(pmic->client, data); + if (err < 0) + dev_err(&pmic->client->dev, "Error setting voltage\n"); + + mutex_unlock(&pmic->mtx); + return err; +} + +static int isl6271a_list_voltage(struct regulator_dev *dev, unsigned selector) +{ + return ISL6271A_VOLTAGE_MIN + (ISL6271A_VOLTAGE_STEP * selector); +} + +static struct regulator_ops isl_core_ops = { + .get_voltage = isl6271a_get_voltage, + .set_voltage = isl6271a_set_voltage, + .list_voltage = isl6271a_list_voltage, +}; + +static int isl6271a_get_fixed_voltage(struct regulator_dev *dev) +{ + int id = rdev_get_id(dev); + return (id == 1) ? 1100000 : 1300000; +} + +static int isl6271a_list_fixed_voltage(struct regulator_dev *dev, unsigned selector) +{ + int id = rdev_get_id(dev); + return (id == 1) ? 1100000 : 1300000; +} + +static struct regulator_ops isl_fixed_ops = { + .get_voltage = isl6271a_get_fixed_voltage, + .list_voltage = isl6271a_list_fixed_voltage, +}; + +static struct regulator_desc isl_rd[] = { + { + .name = "Core Buck", + .id = 0, + .n_voltages = 16, + .ops = &isl_core_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO1", + .id = 1, + .n_voltages = 1, + .ops = &isl_fixed_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO2", + .id = 2, + .n_voltages = 1, + .ops = &isl_fixed_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, +}; + +static int __devinit isl6271a_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct regulator_init_data *init_data = i2c->dev.platform_data; + struct isl_pmic *pmic; + int err, i; + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + if (!init_data) { + dev_err(&i2c->dev, "no platform data supplied\n"); + return -EIO; + } + + pmic = kzalloc(sizeof(struct isl_pmic), GFP_KERNEL); + if (!pmic) + return -ENOMEM; + + pmic->client = i2c; + + mutex_init(&pmic->mtx); + + for (i = 0; i < 3; i++) { + pmic->rdev[i] = regulator_register(&isl_rd[0], &i2c->dev, + init_data, pmic); + if (IS_ERR(pmic->rdev[i])) { + dev_err(&i2c->dev, "failed to register %s\n", id->name); + err = PTR_ERR(pmic->rdev); + goto error; + } + } + + i2c_set_clientdata(i2c, pmic); + + return 0; + +error: + while (--i >= 0) + regulator_unregister(pmic->rdev[i]); + + kfree(pmic); + return err; +} + +static int __devexit isl6271a_remove(struct i2c_client *i2c) +{ + struct isl_pmic *pmic = i2c_get_clientdata(i2c); + int i; + + i2c_set_clientdata(i2c, NULL); + + for (i = 0; i < 3; i++) + regulator_unregister(pmic->rdev[i]); + + kfree(pmic); + + return 0; +} + +static const struct i2c_device_id isl6271a_id[] = { + {.name = "isl6271a", 0 }, + { }, +}; + +MODULE_DEVICE_TABLE(i2c, isl6271a_id); + +static struct i2c_driver isl6271a_i2c_driver = { + .driver = { + .name = "isl6271a", + .owner = THIS_MODULE, + }, + .probe = isl6271a_probe, + .remove = __devexit_p(isl6271a_remove), + .id_table = isl6271a_id, +}; + +static int __init isl6271a_init(void) +{ + return i2c_add_driver(&isl6271a_i2c_driver); +} + +static void __exit isl6271a_cleanup(void) +{ + i2c_del_driver(&isl6271a_i2c_driver); +} + +subsys_initcall(isl6271a_init); +module_exit(isl6271a_cleanup); + +MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); +MODULE_DESCRIPTION("Intersil ISL6271A voltage regulator driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/lp3971.c b/drivers/regulator/lp3971.c index 8ae3732eb24..3bb82b624e1 100644 --- a/drivers/regulator/lp3971.c +++ b/drivers/regulator/lp3971.c @@ -377,7 +377,7 @@ static int lp3971_i2c_read(struct i2c_client *i2c, char reg, int count, if (count != 1) return -EIO; ret = i2c_smbus_read_byte_data(i2c, reg); - if (ret < 0 || count != 1) + if (ret < 0) return -EIO; *dest = ret; @@ -387,15 +387,9 @@ static int lp3971_i2c_read(struct i2c_client *i2c, char reg, int count, static int lp3971_i2c_write(struct i2c_client *i2c, char reg, int count, const u16 *src) { - int ret; - if (count != 1) return -EIO; - ret = i2c_smbus_write_byte_data(i2c, reg, *src); - if (ret >= 0) - return 0; - - return ret; + return i2c_smbus_write_byte_data(i2c, reg, *src); } static u8 lp3971_reg_read(struct lp3971 *lp3971, u8 reg) diff --git a/drivers/regulator/max1586.c b/drivers/regulator/max1586.c index 2b54d9d75f1..8867c2710a6 100644 --- a/drivers/regulator/max1586.c +++ b/drivers/regulator/max1586.c @@ -223,7 +223,7 @@ static int __devinit max1586_pmic_probe(struct i2c_client *client, } } - i2c_set_clientdata(client, rdev); + i2c_set_clientdata(client, max1586); dev_info(&client->dev, "Maxim 1586 regulator driver loaded\n"); return 0; @@ -238,13 +238,13 @@ out: static int __devexit max1586_pmic_remove(struct i2c_client *client) { - struct regulator_dev **rdev = i2c_get_clientdata(client); + struct max1586_data *max1586 = i2c_get_clientdata(client); int i; for (i = 0; i <= MAX1586_V6; i++) - if (rdev[i]) - regulator_unregister(rdev[i]); - kfree(rdev); + if (max1586->rdev[i]) + regulator_unregister(max1586->rdev[i]); + kfree(max1586); return 0; } diff --git a/drivers/regulator/max8660.c b/drivers/regulator/max8660.c index d97220efae5..c570e6eb0db 100644 --- a/drivers/regulator/max8660.c +++ b/drivers/regulator/max8660.c @@ -450,7 +450,7 @@ static int __devinit max8660_probe(struct i2c_client *client, } } - i2c_set_clientdata(client, rdev); + i2c_set_clientdata(client, max8660); dev_info(&client->dev, "Maxim 8660/8661 regulator driver loaded\n"); return 0; @@ -465,13 +465,13 @@ out: static int __devexit max8660_remove(struct i2c_client *client) { - struct regulator_dev **rdev = i2c_get_clientdata(client); + struct max8660 *max8660 = i2c_get_clientdata(client); int i; for (i = 0; i < MAX8660_V_END; i++) - if (rdev[i]) - regulator_unregister(rdev[i]); - kfree(rdev); + if (max8660->rdev[i]) + regulator_unregister(max8660->rdev[i]); + kfree(max8660); return 0; } diff --git a/drivers/regulator/max8998.c b/drivers/regulator/max8998.c new file mode 100644 index 00000000000..ab67298799f --- /dev/null +++ b/drivers/regulator/max8998.c @@ -0,0 +1,635 @@ +/* + * max8998.c - Voltage regulator driver for the Maxim 8998 + * + * Copyright (C) 2009-2010 Samsung Electronics + * Kyungmin Park <kyungmin.park@samsung.com> + * Marek Szyprowski <m.szyprowski@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/gpio.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/mutex.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/mfd/max8998.h> +#include <linux/mfd/max8998-private.h> + +struct max8998_data { + struct device *dev; + struct max8998_dev *iodev; + int num_regulators; + struct regulator_dev **rdev; +}; + +struct voltage_map_desc { + int min; + int max; + int step; +}; + +/* Voltage maps */ +static const struct voltage_map_desc ldo23_voltage_map_desc = { + .min = 800, .step = 50, .max = 1300, +}; +static const struct voltage_map_desc ldo456711_voltage_map_desc = { + .min = 1600, .step = 100, .max = 3600, +}; +static const struct voltage_map_desc ldo8_voltage_map_desc = { + .min = 3000, .step = 100, .max = 3600, +}; +static const struct voltage_map_desc ldo9_voltage_map_desc = { + .min = 2800, .step = 100, .max = 3100, +}; +static const struct voltage_map_desc ldo10_voltage_map_desc = { + .min = 950, .step = 50, .max = 1300, +}; +static const struct voltage_map_desc ldo1213_voltage_map_desc = { + .min = 800, .step = 100, .max = 3300, +}; +static const struct voltage_map_desc ldo1415_voltage_map_desc = { + .min = 1200, .step = 100, .max = 3300, +}; +static const struct voltage_map_desc ldo1617_voltage_map_desc = { + .min = 1600, .step = 100, .max = 3600, +}; +static const struct voltage_map_desc buck12_voltage_map_desc = { + .min = 750, .step = 25, .max = 1525, +}; +static const struct voltage_map_desc buck3_voltage_map_desc = { + .min = 1600, .step = 100, .max = 3600, +}; +static const struct voltage_map_desc buck4_voltage_map_desc = { + .min = 800, .step = 100, .max = 2300, +}; + +static const struct voltage_map_desc *ldo_voltage_map[] = { + NULL, + NULL, + &ldo23_voltage_map_desc, /* LDO2 */ + &ldo23_voltage_map_desc, /* LDO3 */ + &ldo456711_voltage_map_desc, /* LDO4 */ + &ldo456711_voltage_map_desc, /* LDO5 */ + &ldo456711_voltage_map_desc, /* LDO6 */ + &ldo456711_voltage_map_desc, /* LDO7 */ + &ldo8_voltage_map_desc, /* LDO8 */ + &ldo9_voltage_map_desc, /* LDO9 */ + &ldo10_voltage_map_desc, /* LDO10 */ + &ldo456711_voltage_map_desc, /* LDO11 */ + &ldo1213_voltage_map_desc, /* LDO12 */ + &ldo1213_voltage_map_desc, /* LDO13 */ + &ldo1415_voltage_map_desc, /* LDO14 */ + &ldo1415_voltage_map_desc, /* LDO15 */ + &ldo1617_voltage_map_desc, /* LDO16 */ + &ldo1617_voltage_map_desc, /* LDO17 */ + &buck12_voltage_map_desc, /* BUCK1 */ + &buck12_voltage_map_desc, /* BUCK2 */ + &buck3_voltage_map_desc, /* BUCK3 */ + &buck4_voltage_map_desc, /* BUCK4 */ +}; + +static inline int max8998_get_ldo(struct regulator_dev *rdev) +{ + return rdev_get_id(rdev); +} + +static int max8998_list_voltage(struct regulator_dev *rdev, + unsigned int selector) +{ + const struct voltage_map_desc *desc; + int ldo = max8998_get_ldo(rdev); + int val; + + if (ldo >= ARRAY_SIZE(ldo_voltage_map)) + return -EINVAL; + + desc = ldo_voltage_map[ldo]; + if (desc == NULL) + return -EINVAL; + + val = desc->min + desc->step * selector; + if (val > desc->max) + return -EINVAL; + + return val * 1000; +} + +static int max8998_get_enable_register(struct regulator_dev *rdev, + int *reg, int *shift) +{ + int ldo = max8998_get_ldo(rdev); + + switch (ldo) { + case MAX8998_LDO2 ... MAX8998_LDO5: + *reg = MAX8998_REG_ONOFF1; + *shift = 3 - (ldo - MAX8998_LDO2); + break; + case MAX8998_LDO6 ... MAX8998_LDO13: + *reg = MAX8998_REG_ONOFF2; + *shift = 7 - (ldo - MAX8998_LDO6); + break; + case MAX8998_LDO14 ... MAX8998_LDO17: + *reg = MAX8998_REG_ONOFF3; + *shift = 7 - (ldo - MAX8998_LDO14); + break; + case MAX8998_BUCK1 ... MAX8998_BUCK4: + *reg = MAX8998_REG_ONOFF1; + *shift = 7 - (ldo - MAX8998_BUCK1); + break; + case MAX8998_EN32KHZ_AP ... MAX8998_ENVICHG: + *reg = MAX8998_REG_ONOFF4; + *shift = 7 - (ldo - MAX8998_EN32KHZ_AP); + break; + case MAX8998_ESAFEOUT1 ... MAX8998_ESAFEOUT2: + *reg = MAX8998_REG_CHGR2; + *shift = 7 - (ldo - MAX8998_ESAFEOUT1); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int max8998_ldo_is_enabled(struct regulator_dev *rdev) +{ + struct max8998_data *max8998 = rdev_get_drvdata(rdev); + int ret, reg, shift = 8; + u8 val; + + ret = max8998_get_enable_register(rdev, ®, &shift); + if (ret) + return ret; + + ret = max8998_read_reg(max8998->iodev, reg, &val); + if (ret) + return ret; + + return val & (1 << shift); +} + +static int max8998_ldo_enable(struct regulator_dev *rdev) +{ + struct max8998_data *max8998 = rdev_get_drvdata(rdev); + int reg, shift = 8, ret; + + ret = max8998_get_enable_register(rdev, ®, &shift); + if (ret) + return ret; + + return max8998_update_reg(max8998->iodev, reg, 1<<shift, 1<<shift); +} + +static int max8998_ldo_disable(struct regulator_dev *rdev) +{ + struct max8998_data *max8998 = rdev_get_drvdata(rdev); + int reg, shift = 8, ret; + + ret = max8998_get_enable_register(rdev, ®, &shift); + if (ret) + return ret; + + return max8998_update_reg(max8998->iodev, reg, 0, 1<<shift); +} + +static int max8998_get_voltage_register(struct regulator_dev *rdev, + int *_reg, int *_shift, int *_mask) +{ + int ldo = max8998_get_ldo(rdev); + int reg, shift = 0, mask = 0xff; + + switch (ldo) { + case MAX8998_LDO2 ... MAX8998_LDO3: + reg = MAX8998_REG_LDO2_LDO3; + mask = 0xf; + if (ldo == MAX8998_LDO2) + shift = 4; + else + shift = 0; + break; + case MAX8998_LDO4 ... MAX8998_LDO7: + reg = MAX8998_REG_LDO4 + (ldo - MAX8998_LDO4); + break; + case MAX8998_LDO8 ... MAX8998_LDO9: + reg = MAX8998_REG_LDO8_LDO9; + mask = 0xf; + if (ldo == MAX8998_LDO8) + shift = 4; + else + shift = 0; + break; + case MAX8998_LDO10 ... MAX8998_LDO11: + reg = MAX8998_REG_LDO10_LDO11; + if (ldo == MAX8998_LDO10) { + shift = 5; + mask = 0x7; + } else { + shift = 0; + mask = 0x1f; + } + break; + case MAX8998_LDO12 ... MAX8998_LDO17: + reg = MAX8998_REG_LDO12 + (ldo - MAX8998_LDO12); + break; + case MAX8998_BUCK1: + reg = MAX8998_REG_BUCK1_DVSARM1; + break; + case MAX8998_BUCK2: + reg = MAX8998_REG_BUCK2_DVSINT1; + break; + case MAX8998_BUCK3: + reg = MAX8998_REG_BUCK3; + break; + case MAX8998_BUCK4: + reg = MAX8998_REG_BUCK4; + break; + default: + return -EINVAL; + } + + *_reg = reg; + *_shift = shift; + *_mask = mask; + + return 0; +} + +static int max8998_get_voltage(struct regulator_dev *rdev) +{ + struct max8998_data *max8998 = rdev_get_drvdata(rdev); + int reg, shift = 0, mask, ret; + u8 val; + + ret = max8998_get_voltage_register(rdev, ®, &shift, &mask); + if (ret) + return ret; + + ret = max8998_read_reg(max8998->iodev, reg, &val); + if (ret) + return ret; + + val >>= shift; + val &= mask; + + return max8998_list_voltage(rdev, val); +} + +static int max8998_set_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + struct max8998_data *max8998 = rdev_get_drvdata(rdev); + int min_vol = min_uV / 1000, max_vol = max_uV / 1000; + int previous_vol = 0; + const struct voltage_map_desc *desc; + int ldo = max8998_get_ldo(rdev); + int reg, shift = 0, mask, ret; + int i = 0; + u8 val; + bool en_ramp = false; + + if (ldo >= ARRAY_SIZE(ldo_voltage_map)) + return -EINVAL; + + desc = ldo_voltage_map[ldo]; + if (desc == NULL) + return -EINVAL; + + if (max_vol < desc->min || min_vol > desc->max) + return -EINVAL; + + while (desc->min + desc->step*i < min_vol && + desc->min + desc->step*i < desc->max) + i++; + + if (desc->min + desc->step*i > max_vol) + return -EINVAL; + + ret = max8998_get_voltage_register(rdev, ®, &shift, &mask); + if (ret) + return ret; + + /* wait for RAMP_UP_DELAY if rdev is BUCK1/2 and + * ENRAMP is ON */ + if (ldo == MAX8998_BUCK1 || ldo == MAX8998_BUCK2) { + max8998_read_reg(max8998->iodev, MAX8998_REG_ONOFF4, &val); + if (val & (1 << 4)) { + en_ramp = true; + previous_vol = max8998_get_voltage(rdev); + } + } + + ret = max8998_update_reg(max8998->iodev, reg, i<<shift, mask<<shift); + + if (en_ramp == true) { + int difference = desc->min + desc->step*i - previous_vol/1000; + if (difference > 0) + udelay(difference / ((val & 0x0f) + 1)); + } + + return ret; +} + +static struct regulator_ops max8998_ldo_ops = { + .list_voltage = max8998_list_voltage, + .is_enabled = max8998_ldo_is_enabled, + .enable = max8998_ldo_enable, + .disable = max8998_ldo_disable, + .get_voltage = max8998_get_voltage, + .set_voltage = max8998_set_voltage, + .set_suspend_enable = max8998_ldo_enable, + .set_suspend_disable = max8998_ldo_disable, +}; + +static struct regulator_ops max8998_buck_ops = { + .list_voltage = max8998_list_voltage, + .is_enabled = max8998_ldo_is_enabled, + .enable = max8998_ldo_enable, + .disable = max8998_ldo_disable, + .get_voltage = max8998_get_voltage, + .set_voltage = max8998_set_voltage, + .set_suspend_enable = max8998_ldo_enable, + .set_suspend_disable = max8998_ldo_disable, +}; + +static struct regulator_ops max8998_others_ops = { + .is_enabled = max8998_ldo_is_enabled, + .enable = max8998_ldo_enable, + .disable = max8998_ldo_disable, + .set_suspend_enable = max8998_ldo_enable, + .set_suspend_disable = max8998_ldo_disable, +}; + +static struct regulator_desc regulators[] = { + { + .name = "LDO2", + .id = MAX8998_LDO2, + .ops = &max8998_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO3", + .id = MAX8998_LDO3, + .ops = &max8998_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO4", + .id = MAX8998_LDO4, + .ops = &max8998_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO5", + .id = MAX8998_LDO5, + .ops = &max8998_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO6", + .id = MAX8998_LDO6, + .ops = &max8998_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO7", + .id = MAX8998_LDO7, + .ops = &max8998_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO8", + .id = MAX8998_LDO8, + .ops = &max8998_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO9", + .id = MAX8998_LDO9, + .ops = &max8998_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO10", + .id = MAX8998_LDO10, + .ops = &max8998_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO11", + .id = MAX8998_LDO11, + .ops = &max8998_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO12", + .id = MAX8998_LDO12, + .ops = &max8998_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO13", + .id = MAX8998_LDO13, + .ops = &max8998_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO14", + .id = MAX8998_LDO14, + .ops = &max8998_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO15", + .id = MAX8998_LDO15, + .ops = &max8998_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO16", + .id = MAX8998_LDO16, + .ops = &max8998_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO17", + .id = MAX8998_LDO17, + .ops = &max8998_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "BUCK1", + .id = MAX8998_BUCK1, + .ops = &max8998_buck_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "BUCK2", + .id = MAX8998_BUCK2, + .ops = &max8998_buck_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "BUCK3", + .id = MAX8998_BUCK3, + .ops = &max8998_buck_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "BUCK4", + .id = MAX8998_BUCK4, + .ops = &max8998_buck_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "EN32KHz AP", + .id = MAX8998_EN32KHZ_AP, + .ops = &max8998_others_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "EN32KHz CP", + .id = MAX8998_EN32KHZ_CP, + .ops = &max8998_others_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "ENVICHG", + .id = MAX8998_ENVICHG, + .ops = &max8998_others_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "ESAFEOUT1", + .id = MAX8998_ESAFEOUT1, + .ops = &max8998_others_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "ESAFEOUT2", + .id = MAX8998_ESAFEOUT2, + .ops = &max8998_others_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + } +}; + +static __devinit int max8998_pmic_probe(struct platform_device *pdev) +{ + struct max8998_dev *iodev = dev_get_drvdata(pdev->dev.parent); + struct max8998_platform_data *pdata = dev_get_platdata(iodev->dev); + struct regulator_dev **rdev; + struct max8998_data *max8998; + int i, ret, size; + + if (!pdata) { + dev_err(pdev->dev.parent, "No platform init data supplied\n"); + return -ENODEV; + } + + max8998 = kzalloc(sizeof(struct max8998_data), GFP_KERNEL); + if (!max8998) + return -ENOMEM; + + size = sizeof(struct regulator_dev *) * (pdata->num_regulators + 1); + max8998->rdev = kzalloc(size, GFP_KERNEL); + if (!max8998->rdev) { + kfree(max8998); + return -ENOMEM; + } + + rdev = max8998->rdev; + max8998->iodev = iodev; + platform_set_drvdata(pdev, max8998); + + for (i = 0; i < pdata->num_regulators; i++) { + const struct voltage_map_desc *desc; + int id = pdata->regulators[i].id; + int index = id - MAX8998_LDO2; + + desc = ldo_voltage_map[id]; + if (desc && regulators[index].ops != &max8998_others_ops) { + int count = (desc->max - desc->min) / desc->step + 1; + regulators[index].n_voltages = count; + } + rdev[i] = regulator_register(®ulators[index], max8998->dev, + pdata->regulators[i].initdata, max8998); + if (IS_ERR(rdev[i])) { + ret = PTR_ERR(rdev[i]); + dev_err(max8998->dev, "regulator init failed\n"); + rdev[i] = NULL; + goto err; + } + } + + + return 0; +err: + for (i = 0; i <= max8998->num_regulators; i++) + if (rdev[i]) + regulator_unregister(rdev[i]); + + kfree(max8998->rdev); + kfree(max8998); + + return ret; +} + +static int __devexit max8998_pmic_remove(struct platform_device *pdev) +{ + struct max8998_data *max8998 = platform_get_drvdata(pdev); + struct regulator_dev **rdev = max8998->rdev; + int i; + + for (i = 0; i <= max8998->num_regulators; i++) + if (rdev[i]) + regulator_unregister(rdev[i]); + + kfree(max8998->rdev); + kfree(max8998); + + return 0; +} + +static struct platform_driver max8998_pmic_driver = { + .driver = { + .name = "max8998-pmic", + .owner = THIS_MODULE, + }, + .probe = max8998_pmic_probe, + .remove = __devexit_p(max8998_pmic_remove), +}; + +static int __init max8998_pmic_init(void) +{ + return platform_driver_register(&max8998_pmic_driver); +} +subsys_initcall(max8998_pmic_init); + +static void __exit max8998_pmic_cleanup(void) +{ + platform_driver_unregister(&max8998_pmic_driver); +} +module_exit(max8998_pmic_cleanup); + +MODULE_DESCRIPTION("MAXIM 8998 voltage regulator driver"); +MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c index f50afc9f287..cd6d4fc9d74 100644 --- a/drivers/regulator/tps65023-regulator.c +++ b/drivers/regulator/tps65023-regulator.c @@ -585,6 +585,8 @@ static const struct tps_info tps65023_regs[] = { static const struct i2c_device_id tps_65023_id[] = { {.name = "tps65023", .driver_data = (unsigned long) tps65023_regs,}, + {.name = "tps65021", + .driver_data = (unsigned long) tps65023_regs,}, { }, }; diff --git a/drivers/regulator/tps6507x-regulator.c b/drivers/regulator/tps6507x-regulator.c index 8152d65220f..c239f42aa4a 100644 --- a/drivers/regulator/tps6507x-regulator.c +++ b/drivers/regulator/tps6507x-regulator.c @@ -614,6 +614,7 @@ int tps6507x_pmic_probe(struct platform_device *pdev) } tps6507x_dev->pmic = tps; + platform_set_drvdata(pdev, tps6507x_dev); return 0; diff --git a/drivers/regulator/tps6586x-regulator.c b/drivers/regulator/tps6586x-regulator.c new file mode 100644 index 00000000000..8cff1413a14 --- /dev/null +++ b/drivers/regulator/tps6586x-regulator.c @@ -0,0 +1,396 @@ +/* + * Regulator driver for TI TPS6586x + * + * Copyright (C) 2010 Compulab Ltd. + * Author: Mike Rapoport <mike@compulab.co.il> + * + * Based on da903x + * Copyright (C) 2006-2008 Marvell International Ltd. + * Copyright (C) 2008 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/kernel.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/mfd/tps6586x.h> + +/* supply control and voltage setting */ +#define TPS6586X_SUPPLYENA 0x10 +#define TPS6586X_SUPPLYENB 0x11 +#define TPS6586X_SUPPLYENC 0x12 +#define TPS6586X_SUPPLYEND 0x13 +#define TPS6586X_SUPPLYENE 0x14 +#define TPS6586X_VCC1 0x20 +#define TPS6586X_VCC2 0x21 +#define TPS6586X_SM1V1 0x23 +#define TPS6586X_SM1V2 0x24 +#define TPS6586X_SM1SL 0x25 +#define TPS6586X_SM0V1 0x26 +#define TPS6586X_SM0V2 0x27 +#define TPS6586X_SM0SL 0x28 +#define TPS6586X_LDO2AV1 0x29 +#define TPS6586X_LDO2AV2 0x2A +#define TPS6586X_LDO2BV1 0x2F +#define TPS6586X_LDO2BV2 0x30 +#define TPS6586X_LDO4V1 0x32 +#define TPS6586X_LDO4V2 0x33 + +/* converter settings */ +#define TPS6586X_SUPPLYV1 0x41 +#define TPS6586X_SUPPLYV2 0x42 +#define TPS6586X_SUPPLYV3 0x43 +#define TPS6586X_SUPPLYV4 0x44 +#define TPS6586X_SUPPLYV5 0x45 +#define TPS6586X_SUPPLYV6 0x46 +#define TPS6586X_SMODE1 0x47 +#define TPS6586X_SMODE2 0x48 + +struct tps6586x_regulator { + struct regulator_desc desc; + + int volt_reg; + int volt_shift; + int volt_nbits; + int enable_bit[2]; + int enable_reg[2]; + + int *voltages; + + /* for DVM regulators */ + int go_reg; + int go_bit; +}; + +static inline struct device *to_tps6586x_dev(struct regulator_dev *rdev) +{ + return rdev_get_dev(rdev)->parent->parent; +} + +static int tps6586x_ldo_list_voltage(struct regulator_dev *rdev, + unsigned selector) +{ + struct tps6586x_regulator *info = rdev_get_drvdata(rdev); + + return info->voltages[selector] * 1000; +} + + +static int __tps6586x_ldo_set_voltage(struct device *parent, + struct tps6586x_regulator *ri, + int min_uV, int max_uV) +{ + int val, uV; + uint8_t mask; + + for (val = 0; val < ri->desc.n_voltages; val++) { + uV = ri->voltages[val] * 1000; + + /* LDO0 has minimal voltage 1.2 rather than 1.25 */ + if (ri->desc.id == TPS6586X_ID_LDO_0 && val == 0) + uV -= 50 * 1000; + + /* use the first in-range value */ + if (min_uV <= uV && uV <= max_uV) { + + val <<= ri->volt_shift; + mask = ((1 << ri->volt_nbits) - 1) << ri->volt_shift; + + return tps6586x_update(parent, ri->volt_reg, val, mask); + } + } + + return -EINVAL; +} + +static int tps6586x_ldo_set_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + struct tps6586x_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_tps6586x_dev(rdev); + + return __tps6586x_ldo_set_voltage(parent, ri, min_uV, max_uV); +} + +static int tps6586x_ldo_get_voltage(struct regulator_dev *rdev) +{ + struct tps6586x_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_tps6586x_dev(rdev); + uint8_t val, mask; + int ret; + + ret = tps6586x_read(parent, ri->volt_reg, &val); + if (ret) + return ret; + + mask = ((1 << ri->volt_nbits) - 1) << ri->volt_shift; + val = (val & mask) >> ri->volt_shift; + + if (val > ri->desc.n_voltages) + BUG(); + + return ri->voltages[val] * 1000; +} + +static int tps6586x_dvm_set_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + struct tps6586x_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_tps6586x_dev(rdev); + int ret; + + ret = __tps6586x_ldo_set_voltage(parent, ri, min_uV, max_uV); + if (ret) + return ret; + + return tps6586x_set_bits(parent, ri->go_reg, ri->go_bit); +} + +static int tps6586x_regulator_enable(struct regulator_dev *rdev) +{ + struct tps6586x_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_tps6586x_dev(rdev); + + return tps6586x_set_bits(parent, ri->enable_reg[0], + 1 << ri->enable_bit[0]); +} + +static int tps6586x_regulator_disable(struct regulator_dev *rdev) +{ + struct tps6586x_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_tps6586x_dev(rdev); + + return tps6586x_clr_bits(parent, ri->enable_reg[0], + 1 << ri->enable_bit[0]); +} + +static int tps6586x_regulator_is_enabled(struct regulator_dev *rdev) +{ + struct tps6586x_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_tps6586x_dev(rdev); + uint8_t reg_val; + int ret; + + ret = tps6586x_read(parent, ri->enable_reg[0], ®_val); + if (ret) + return ret; + + return !!(reg_val & (1 << ri->enable_bit[0])); +} + +static struct regulator_ops tps6586x_regulator_ldo_ops = { + .list_voltage = tps6586x_ldo_list_voltage, + .get_voltage = tps6586x_ldo_get_voltage, + .set_voltage = tps6586x_ldo_set_voltage, + + .is_enabled = tps6586x_regulator_is_enabled, + .enable = tps6586x_regulator_enable, + .disable = tps6586x_regulator_disable, +}; + +static struct regulator_ops tps6586x_regulator_dvm_ops = { + .list_voltage = tps6586x_ldo_list_voltage, + .get_voltage = tps6586x_ldo_get_voltage, + .set_voltage = tps6586x_dvm_set_voltage, + + .is_enabled = tps6586x_regulator_is_enabled, + .enable = tps6586x_regulator_enable, + .disable = tps6586x_regulator_disable, +}; + +static int tps6586x_ldo_voltages[] = { + 1250, 1500, 1800, 2500, 2700, 2850, 3100, 3300, +}; + +static int tps6586x_ldo4_voltages[] = { + 1700, 1725, 1750, 1775, 1800, 1825, 1850, 1875, + 1900, 1925, 1950, 1975, 2000, 2025, 2050, 2075, + 2100, 2125, 2150, 2175, 2200, 2225, 2250, 2275, + 2300, 2325, 2350, 2375, 2400, 2425, 2450, 2475, +}; + +static int tps6586x_sm2_voltages[] = { + 3000, 3050, 3100, 3150, 3200, 3250, 3300, 3350, + 3400, 3450, 3500, 3550, 3600, 3650, 3700, 3750, + 3800, 3850, 3900, 3950, 4000, 4050, 4100, 4150, + 4200, 4250, 4300, 4350, 4400, 4450, 4500, 4550, +}; + +static int tps6586x_dvm_voltages[] = { + 725, 750, 775, 800, 825, 850, 875, 900, + 925, 950, 975, 1000, 1025, 1050, 1075, 1100, + 1125, 1150, 1175, 1200, 1225, 1250, 1275, 1300, + 1325, 1350, 1375, 1400, 1425, 1450, 1475, 1500, +}; + +#define TPS6586X_REGULATOR(_id, vdata, _ops, vreg, shift, nbits, \ + ereg0, ebit0, ereg1, ebit1, goreg, gobit) \ +{ \ + .desc = { \ + .name = "REG-" #_id, \ + .ops = &tps6586x_regulator_##_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = TPS6586X_ID_##_id, \ + .n_voltages = ARRAY_SIZE(tps6586x_##vdata##_voltages), \ + .owner = THIS_MODULE, \ + }, \ + .volt_reg = TPS6586X_##vreg, \ + .volt_shift = (shift), \ + .volt_nbits = (nbits), \ + .enable_reg[0] = TPS6586X_SUPPLY##ereg0, \ + .enable_bit[0] = (ebit0), \ + .enable_reg[1] = TPS6586X_SUPPLY##ereg1, \ + .enable_bit[1] = (ebit1), \ + .voltages = tps6586x_##vdata##_voltages, \ +} + +#define TPS6586X_LDO(_id, vdata, vreg, shift, nbits, \ + ereg0, ebit0, ereg1, ebit1) \ + TPS6586X_REGULATOR(_id, vdata, ldo_ops, vreg, shift, nbits, \ + ereg0, ebit0, ereg1, ebit1, 0, 0) + +#define TPS6586X_DVM(_id, vdata, vreg, shift, nbits, \ + ereg0, ebit0, ereg1, ebit1, goreg, gobit) \ + TPS6586X_REGULATOR(_id, vdata, dvm_ops, vreg, shift, nbits, \ + ereg0, ebit0, ereg1, ebit1, goreg, gobit) + +static struct tps6586x_regulator tps6586x_regulator[] = { + TPS6586X_LDO(LDO_0, ldo, SUPPLYV1, 5, 3, ENC, 0, END, 0), + TPS6586X_LDO(LDO_3, ldo, SUPPLYV4, 0, 3, ENC, 2, END, 2), + TPS6586X_LDO(LDO_5, ldo, SUPPLYV6, 0, 3, ENE, 6, ENE, 6), + TPS6586X_LDO(LDO_6, ldo, SUPPLYV3, 0, 3, ENC, 4, END, 4), + TPS6586X_LDO(LDO_7, ldo, SUPPLYV3, 3, 3, ENC, 5, END, 5), + TPS6586X_LDO(LDO_8, ldo, SUPPLYV1, 5, 3, ENC, 6, END, 6), + TPS6586X_LDO(LDO_9, ldo, SUPPLYV6, 3, 3, ENE, 7, ENE, 7), + TPS6586X_LDO(LDO_RTC, ldo, SUPPLYV4, 3, 3, ENE, 7, ENE, 7), + TPS6586X_LDO(LDO_1, dvm, SUPPLYV1, 0, 5, ENC, 1, END, 1), + TPS6586X_LDO(SM_2, sm2, SUPPLYV2, 0, 5, ENC, 1, END, 1), + + TPS6586X_DVM(LDO_2, dvm, LDO2BV1, 0, 5, ENA, 3, ENB, 3, VCC2, 6), + TPS6586X_DVM(LDO_4, ldo4, LDO4V1, 0, 5, ENC, 3, END, 3, VCC1, 6), + TPS6586X_DVM(SM_0, dvm, SM0V1, 0, 5, ENA, 1, ENB, 1, VCC1, 2), + TPS6586X_DVM(SM_1, dvm, SM1V1, 0, 5, ENA, 0, ENB, 0, VCC1, 0), +}; + +/* + * TPS6586X has 2 enable bits that are OR'ed to determine the actual + * regulator state. Clearing one of this bits allows switching + * regulator on and of with single register write. + */ +static inline int tps6586x_regulator_preinit(struct device *parent, + struct tps6586x_regulator *ri) +{ + uint8_t val1, val2; + int ret; + + ret = tps6586x_read(parent, ri->enable_reg[0], &val1); + if (ret) + return ret; + + ret = tps6586x_read(parent, ri->enable_reg[1], &val2); + if (ret) + return ret; + + if (!(val2 & ri->enable_bit[1])) + return 0; + + /* + * The regulator is on, but it's enabled with the bit we don't + * want to use, so we switch the enable bits + */ + if (!(val1 & ri->enable_bit[0])) { + ret = tps6586x_set_bits(parent, ri->enable_reg[0], + 1 << ri->enable_bit[0]); + if (ret) + return ret; + } + + return tps6586x_clr_bits(parent, ri->enable_reg[1], + 1 << ri->enable_bit[1]); +} + +static inline struct tps6586x_regulator *find_regulator_info(int id) +{ + struct tps6586x_regulator *ri; + int i; + + for (i = 0; i < ARRAY_SIZE(tps6586x_regulator); i++) { + ri = &tps6586x_regulator[i]; + if (ri->desc.id == id) + return ri; + } + return NULL; +} + +static int __devinit tps6586x_regulator_probe(struct platform_device *pdev) +{ + struct tps6586x_regulator *ri = NULL; + struct regulator_dev *rdev; + int id = pdev->id; + int err; + + dev_dbg(&pdev->dev, "Probing reulator %d\n", id); + + ri = find_regulator_info(id); + if (ri == NULL) { + dev_err(&pdev->dev, "invalid regulator ID specified\n"); + return -EINVAL; + } + + err = tps6586x_regulator_preinit(pdev->dev.parent, ri); + if (err) + return err; + + rdev = regulator_register(&ri->desc, &pdev->dev, + pdev->dev.platform_data, ri); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, "failed to register regulator %s\n", + ri->desc.name); + return PTR_ERR(rdev); + } + + platform_set_drvdata(pdev, rdev); + + return 0; +} + +static int __devexit tps6586x_regulator_remove(struct platform_device *pdev) +{ + struct regulator_dev *rdev = platform_get_drvdata(pdev); + + regulator_unregister(rdev); + return 0; +} + +static struct platform_driver tps6586x_regulator_driver = { + .driver = { + .name = "tps6586x-regulator", + .owner = THIS_MODULE, + }, + .probe = tps6586x_regulator_probe, + .remove = __devexit_p(tps6586x_regulator_remove), +}; + +static int __init tps6586x_regulator_init(void) +{ + return platform_driver_register(&tps6586x_regulator_driver); +} +subsys_initcall(tps6586x_regulator_init); + +static void __exit tps6586x_regulator_exit(void) +{ + platform_driver_unregister(&tps6586x_regulator_driver); +} +module_exit(tps6586x_regulator_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>"); +MODULE_DESCRIPTION("Regulator Driver for TI TPS6586X PMIC"); +MODULE_ALIAS("platform:tps6586x-regulator"); diff --git a/drivers/regulator/wm8994-regulator.c b/drivers/regulator/wm8994-regulator.c index 5a1dc8a24d3..03713bc66e4 100644 --- a/drivers/regulator/wm8994-regulator.c +++ b/drivers/regulator/wm8994-regulator.c @@ -219,8 +219,6 @@ static __devinit int wm8994_ldo_probe(struct platform_device *pdev) ldo->wm8994 = wm8994; - ldo->is_enabled = true; - if (pdata->ldo[id].enable && gpio_is_valid(pdata->ldo[id].enable)) { ldo->enable = pdata->ldo[id].enable; @@ -237,7 +235,8 @@ static __devinit int wm8994_ldo_probe(struct platform_device *pdev) ret); goto err_gpio; } - } + } else + ldo->is_enabled = true; ldo->regulator = regulator_register(&wm8994_ldo_desc[id], &pdev->dev, pdata->ldo[id].init_data, ldo); diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 4301a6c7ed3..48ca7132cc0 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -166,6 +166,16 @@ config RTC_DRV_DS1672 This driver can also be built as a module. If so, the module will be called rtc-ds1672. +config RTC_DRV_DS3232 + tristate "Dallas/Maxim DS3232" + depends on RTC_CLASS && I2C + help + If you say yes here you get support for Dallas Semiconductor + DS3232 real-time clock chips. + + This driver can also be built as a module. If so, the module + will be called rtc-ds3232. + config RTC_DRV_MAX6900 tristate "Maxim MAX6900" help @@ -203,6 +213,15 @@ config RTC_DRV_ISL1208 This driver can also be built as a module. If so, the module will be called rtc-isl1208. +config RTC_DRV_ISL12022 + tristate "Intersil ISL12022" + help + If you say yes here you get support for the + Intersil ISL12022 RTC chip. + + This driver can also be built as a module. If so, the module + will be called rtc-isl12022. + config RTC_DRV_X1205 tristate "Xicor/Intersil X1205" help @@ -537,6 +556,16 @@ config RTC_DRV_MSM6242 This driver can also be built as a module. If so, the module will be called rtc-msm6242. +config RTC_DRV_IMXDI + tristate "Freescale IMX DryIce Real Time Clock" + depends on ARCH_MX25 + depends on RTC_CLASS + help + Support for Freescale IMX DryIce RTC + + This driver can also be built as a module, if so, the module + will be called "rtc-imxdi". + config RTC_MXC tristate "Freescale MXC Real Time Clock" depends on ARCH_MXC @@ -645,9 +674,16 @@ config RTC_DRV_OMAP DA8xx/OMAP-L13x chips. This driver can also be built as a module called rtc-omap. +config HAVE_S3C_RTC + bool + help + This will include RTC support for Samsung SoCs. If + you want to include RTC support for any machine, kindly + select this in the respective mach-XXXX/Kconfig file. + config RTC_DRV_S3C tristate "Samsung S3C series SoC RTC" - depends on ARCH_S3C2410 || ARCH_S3C64XX + depends on ARCH_S3C2410 || ARCH_S3C64XX || HAVE_S3C_RTC help RTC (Realtime Clock) driver for the clock inbuilt into the Samsung S3C24XX series of SoCs. This can provide periodic diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index fedf9bb3659..0f207b3b583 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -41,12 +41,15 @@ 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 +obj-$(CONFIG_RTC_DRV_DS3232) += rtc-ds3232.o obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds3234.o obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o obj-$(CONFIG_RTC_DRV_GENERIC) += rtc-generic.o +obj-$(CONFIG_RTC_DRV_IMXDI) += rtc-imxdi.o obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o +obj-$(CONFIG_RTC_DRV_ISL12022) += rtc-isl12022.o obj-$(CONFIG_RTC_DRV_JZ4740) += rtc-jz4740.o obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index 11b8ea29d2b..5856167a0c9 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -970,7 +970,6 @@ static inline int cmos_poweroff(struct device *dev) #include <linux/acpi.h> -#ifdef CONFIG_PM static u32 rtc_handler(void *context) { acpi_clear_event(ACPI_EVENT_RTC); @@ -999,11 +998,6 @@ static void rtc_wake_off(struct device *dev) { acpi_disable_event(ACPI_EVENT_RTC, 0); } -#else -#define rtc_wake_setup() do{}while(0) -#define rtc_wake_on NULL -#define rtc_wake_off NULL -#endif /* Every ACPI platform has a mc146818 compatible "cmos rtc". Here we find * its device node and pass extra config data. This helps its driver use diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c new file mode 100644 index 00000000000..9daed8db83d --- /dev/null +++ b/drivers/rtc/rtc-ds3232.c @@ -0,0 +1,326 @@ +/* + * RTC client/driver for the Maxim/Dallas DS3232 Real-Time Clock over I2C + * + * Copyright (C) 2009-2010 Freescale Semiconductor. + * + * 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. + */ +/* + * It would be more efficient to use i2c msgs/i2c_transfer directly but, as + * recommened in .../Documentation/i2c/writing-clients section + * "Sending and receiving", using SMBus level communication is preferred. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/rtc.h> +#include <linux/bcd.h> +#include <linux/workqueue.h> +#include <linux/slab.h> + +#define DS3232_REG_SECONDS 0x00 +#define DS3232_REG_MINUTES 0x01 +#define DS3232_REG_HOURS 0x02 +#define DS3232_REG_AMPM 0x02 +#define DS3232_REG_DAY 0x03 +#define DS3232_REG_DATE 0x04 +#define DS3232_REG_MONTH 0x05 +#define DS3232_REG_CENTURY 0x05 +#define DS3232_REG_YEAR 0x06 +#define DS3232_REG_ALARM1 0x07 /* Alarm 1 BASE */ +#define DS3232_REG_ALARM2 0x0B /* Alarm 2 BASE */ +#define DS3232_REG_CR 0x0E /* Control register */ +# define DS3232_REG_CR_nEOSC 0x80 +# define DS3232_REG_CR_INTCN 0x04 +# define DS3232_REG_CR_A2IE 0x02 +# define DS3232_REG_CR_A1IE 0x01 + +#define DS3232_REG_SR 0x0F /* control/status register */ +# define DS3232_REG_SR_OSF 0x80 +# define DS3232_REG_SR_BSY 0x04 +# define DS3232_REG_SR_A2F 0x02 +# define DS3232_REG_SR_A1F 0x01 + +struct ds3232 { + struct i2c_client *client; + struct rtc_device *rtc; + struct work_struct work; + + /* The mutex protects alarm operations, and prevents a race + * between the enable_irq() in the workqueue and the free_irq() + * in the remove function. + */ + struct mutex mutex; + int exiting; +}; + +static struct i2c_driver ds3232_driver; + +static int ds3232_check_rtc_status(struct i2c_client *client) +{ + int ret = 0; + int control, stat; + + stat = i2c_smbus_read_byte_data(client, DS3232_REG_SR); + if (stat < 0) + return stat; + + if (stat & DS3232_REG_SR_OSF) + dev_warn(&client->dev, + "oscillator discontinuity flagged, " + "time unreliable\n"); + + stat &= ~(DS3232_REG_SR_OSF | DS3232_REG_SR_A1F | DS3232_REG_SR_A2F); + + ret = i2c_smbus_write_byte_data(client, DS3232_REG_SR, stat); + if (ret < 0) + return ret; + + /* If the alarm is pending, clear it before requesting + * the interrupt, so an interrupt event isn't reported + * before everything is initialized. + */ + + control = i2c_smbus_read_byte_data(client, DS3232_REG_CR); + if (control < 0) + return control; + + control &= ~(DS3232_REG_CR_A1IE | DS3232_REG_CR_A2IE); + control |= DS3232_REG_CR_INTCN; + + return i2c_smbus_write_byte_data(client, DS3232_REG_CR, control); +} + +static int ds3232_read_time(struct device *dev, struct rtc_time *time) +{ + struct i2c_client *client = to_i2c_client(dev); + int ret; + u8 buf[7]; + unsigned int year, month, day, hour, minute, second; + unsigned int week, twelve_hr, am_pm; + unsigned int century, add_century = 0; + + ret = i2c_smbus_read_i2c_block_data(client, DS3232_REG_SECONDS, 7, buf); + + if (ret < 0) + return ret; + if (ret < 7) + return -EIO; + + second = buf[0]; + minute = buf[1]; + hour = buf[2]; + week = buf[3]; + day = buf[4]; + month = buf[5]; + year = buf[6]; + + /* Extract additional information for AM/PM and century */ + + twelve_hr = hour & 0x40; + am_pm = hour & 0x20; + century = month & 0x80; + + /* Write to rtc_time structure */ + + time->tm_sec = bcd2bin(second); + time->tm_min = bcd2bin(minute); + if (twelve_hr) { + /* Convert to 24 hr */ + if (am_pm) + time->tm_hour = bcd2bin(hour & 0x1F) + 12; + else + time->tm_hour = bcd2bin(hour & 0x1F); + } else { + time->tm_hour = bcd2bin(hour); + } + + time->tm_wday = bcd2bin(week); + time->tm_mday = bcd2bin(day); + time->tm_mon = bcd2bin(month & 0x7F); + if (century) + add_century = 100; + + time->tm_year = bcd2bin(year) + add_century; + + return rtc_valid_tm(time); +} + +static int ds3232_set_time(struct device *dev, struct rtc_time *time) +{ + struct i2c_client *client = to_i2c_client(dev); + u8 buf[7]; + + /* Extract time from rtc_time and load into ds3232*/ + + buf[0] = bin2bcd(time->tm_sec); + buf[1] = bin2bcd(time->tm_min); + buf[2] = bin2bcd(time->tm_hour); + buf[3] = bin2bcd(time->tm_wday); /* Day of the week */ + buf[4] = bin2bcd(time->tm_mday); /* Date */ + buf[5] = bin2bcd(time->tm_mon); + if (time->tm_year >= 100) { + buf[5] |= 0x80; + buf[6] = bin2bcd(time->tm_year - 100); + } else { + buf[6] = bin2bcd(time->tm_year); + } + + return i2c_smbus_write_i2c_block_data(client, + DS3232_REG_SECONDS, 7, buf); +} + +static irqreturn_t ds3232_irq(int irq, void *dev_id) +{ + struct i2c_client *client = dev_id; + struct ds3232 *ds3232 = i2c_get_clientdata(client); + + disable_irq_nosync(irq); + schedule_work(&ds3232->work); + return IRQ_HANDLED; +} + +static void ds3232_work(struct work_struct *work) +{ + struct ds3232 *ds3232 = container_of(work, struct ds3232, work); + struct i2c_client *client = ds3232->client; + int stat, control; + + mutex_lock(&ds3232->mutex); + + stat = i2c_smbus_read_byte_data(client, DS3232_REG_SR); + if (stat < 0) + goto unlock; + + if (stat & DS3232_REG_SR_A1F) { + control = i2c_smbus_read_byte_data(client, DS3232_REG_CR); + if (control < 0) + goto out; + /* disable alarm1 interrupt */ + control &= ~(DS3232_REG_CR_A1IE); + i2c_smbus_write_byte_data(client, DS3232_REG_CR, control); + + /* clear the alarm pend flag */ + stat &= ~DS3232_REG_SR_A1F; + i2c_smbus_write_byte_data(client, DS3232_REG_SR, stat); + + rtc_update_irq(ds3232->rtc, 1, RTC_AF | RTC_IRQF); + } + +out: + if (!ds3232->exiting) + enable_irq(client->irq); +unlock: + mutex_unlock(&ds3232->mutex); +} + +static const struct rtc_class_ops ds3232_rtc_ops = { + .read_time = ds3232_read_time, + .set_time = ds3232_set_time, +}; + +static int __devinit ds3232_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ds3232 *ds3232; + int ret; + + ds3232 = kzalloc(sizeof(struct ds3232), GFP_KERNEL); + if (!ds3232) + return -ENOMEM; + + ds3232->client = client; + i2c_set_clientdata(client, ds3232); + + INIT_WORK(&ds3232->work, ds3232_work); + mutex_init(&ds3232->mutex); + + ret = ds3232_check_rtc_status(client); + if (ret) + goto out_free; + + ds3232->rtc = rtc_device_register(client->name, &client->dev, + &ds3232_rtc_ops, THIS_MODULE); + if (IS_ERR(ds3232->rtc)) { + ret = PTR_ERR(ds3232->rtc); + dev_err(&client->dev, "unable to register the class device\n"); + goto out_irq; + } + + if (client->irq >= 0) { + ret = request_irq(client->irq, ds3232_irq, 0, + "ds3232", client); + if (ret) { + dev_err(&client->dev, "unable to request IRQ\n"); + goto out_free; + } + } + + return 0; + +out_irq: + if (client->irq >= 0) + free_irq(client->irq, client); + +out_free: + i2c_set_clientdata(client, NULL); + kfree(ds3232); + return ret; +} + +static int __devexit ds3232_remove(struct i2c_client *client) +{ + struct ds3232 *ds3232 = i2c_get_clientdata(client); + + if (client->irq >= 0) { + mutex_lock(&ds3232->mutex); + ds3232->exiting = 1; + mutex_unlock(&ds3232->mutex); + + free_irq(client->irq, client); + flush_scheduled_work(); + } + + rtc_device_unregister(ds3232->rtc); + i2c_set_clientdata(client, NULL); + kfree(ds3232); + return 0; +} + +static const struct i2c_device_id ds3232_id[] = { + { "ds3232", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ds3232_id); + +static struct i2c_driver ds3232_driver = { + .driver = { + .name = "rtc-ds3232", + .owner = THIS_MODULE, + }, + .probe = ds3232_probe, + .remove = __devexit_p(ds3232_remove), + .id_table = ds3232_id, +}; + +static int __init ds3232_init(void) +{ + return i2c_add_driver(&ds3232_driver); +} + +static void __exit ds3232_exit(void) +{ + i2c_del_driver(&ds3232_driver); +} + +module_init(ds3232_init); +module_exit(ds3232_exit); + +MODULE_AUTHOR("Srikanth Srinivasan <srikanth.srinivasan@freescale.com>"); +MODULE_DESCRIPTION("Maxim/Dallas DS3232 RTC Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-fm3130.c b/drivers/rtc/rtc-fm3130.c index e4de8f37ae4..4cf2e70c507 100644 --- a/drivers/rtc/rtc-fm3130.c +++ b/drivers/rtc/rtc-fm3130.c @@ -52,8 +52,8 @@ struct fm3130 { struct i2c_msg msg[4]; struct i2c_client *client; struct rtc_device *rtc; + int alarm_valid; int data_valid; - int alarm; }; static const struct i2c_device_id fm3130_id[] = { { "fm3130", 0 }, @@ -87,11 +87,7 @@ static void fm3130_rtc_mode(struct device *dev, int mode) dev_dbg(dev, "invalid mode %d\n", mode); break; } - /* Checking for alarm */ - if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_AF) { - fm3130->alarm = 1; - fm3130->regs[FM3130_RTC_CONTROL] &= ~FM3130_RTC_CONTROL_BIT_AF; - } + i2c_smbus_write_byte_data(fm3130->client, FM3130_RTC_CONTROL, fm3130->regs[FM3130_RTC_CONTROL]); } @@ -208,6 +204,17 @@ static int fm3130_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) struct fm3130 *fm3130 = dev_get_drvdata(dev); int tmp; struct rtc_time *tm = &alrm->time; + + if (!fm3130->alarm_valid) { + /* + * We have invalid alarm in RTC, probably due to battery faults + * or other problems. Return EIO for now, it will allow us to + * set alarm value later instead of error during probing which + * disables device + */ + return -EIO; + } + /* read the RTC alarm registers all at once */ tmp = i2c_transfer(to_i2c_adapter(fm3130->client->dev.parent), &fm3130->msg[2], 2); @@ -222,20 +229,31 @@ static int fm3130_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) fm3130->regs[FM3130_ALARM_DATE], fm3130->regs[FM3130_ALARM_MONTHS]); - tm->tm_sec = bcd2bin(fm3130->regs[FM3130_ALARM_SECONDS] & 0x7F); tm->tm_min = bcd2bin(fm3130->regs[FM3130_ALARM_MINUTES] & 0x7F); tm->tm_hour = bcd2bin(fm3130->regs[FM3130_ALARM_HOURS] & 0x3F); tm->tm_mday = bcd2bin(fm3130->regs[FM3130_ALARM_DATE] & 0x3F); tm->tm_mon = bcd2bin(fm3130->regs[FM3130_ALARM_MONTHS] & 0x1F); + if (tm->tm_mon > 0) tm->tm_mon -= 1; /* RTC is 1-12, tm_mon is 0-11 */ + dev_dbg(dev, "%s secs=%d, mins=%d, " "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", "read alarm", tm->tm_sec, tm->tm_min, tm->tm_hour, tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + /* check if alarm enabled */ + fm3130->regs[FM3130_RTC_CONTROL] = + i2c_smbus_read_byte_data(fm3130->client, FM3130_RTC_CONTROL); + + if ((fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_AEN) && + (~fm3130->regs[FM3130_RTC_CONTROL] & + FM3130_RTC_CONTROL_BIT_CAL)) { + alrm->enabled = 1; + } + return 0; } @@ -251,25 +269,20 @@ static int fm3130_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) tm->tm_hour, tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); - if (tm->tm_sec != -1) - fm3130->regs[FM3130_ALARM_SECONDS] = - bin2bcd(tm->tm_sec) | 0x80; + fm3130->regs[FM3130_ALARM_SECONDS] = + (tm->tm_sec != -1) ? bin2bcd(tm->tm_sec) : 0x80; - if (tm->tm_min != -1) - fm3130->regs[FM3130_ALARM_MINUTES] = - bin2bcd(tm->tm_min) | 0x80; + fm3130->regs[FM3130_ALARM_MINUTES] = + (tm->tm_min != -1) ? bin2bcd(tm->tm_min) : 0x80; - if (tm->tm_hour != -1) - fm3130->regs[FM3130_ALARM_HOURS] = - bin2bcd(tm->tm_hour) | 0x80; + fm3130->regs[FM3130_ALARM_HOURS] = + (tm->tm_hour != -1) ? bin2bcd(tm->tm_hour) : 0x80; - if (tm->tm_mday != -1) - fm3130->regs[FM3130_ALARM_DATE] = - bin2bcd(tm->tm_mday) | 0x80; + fm3130->regs[FM3130_ALARM_DATE] = + (tm->tm_mday != -1) ? bin2bcd(tm->tm_mday) : 0x80; - if (tm->tm_mon != -1) - fm3130->regs[FM3130_ALARM_MONTHS] = - bin2bcd(tm->tm_mon + 1) | 0x80; + fm3130->regs[FM3130_ALARM_MONTHS] = + (tm->tm_mon != -1) ? bin2bcd(tm->tm_mon + 1) : 0x80; dev_dbg(dev, "alarm write %02x %02x %02x %02x %02x\n", fm3130->regs[FM3130_ALARM_SECONDS], @@ -285,11 +298,8 @@ static int fm3130_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) } fm3130->regs[FM3130_RTC_CONTROL] = i2c_smbus_read_byte_data(fm3130->client, FM3130_RTC_CONTROL); - /* Checking for alarm */ - if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_AF) { - fm3130->alarm = 1; - fm3130->regs[FM3130_RTC_CONTROL] &= ~FM3130_RTC_CONTROL_BIT_AF; - } + + /* enable or disable alarm */ if (alrm->enabled) { i2c_smbus_write_byte_data(fm3130->client, FM3130_RTC_CONTROL, (fm3130->regs[FM3130_RTC_CONTROL] & @@ -298,16 +308,55 @@ static int fm3130_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) } else { i2c_smbus_write_byte_data(fm3130->client, FM3130_RTC_CONTROL, fm3130->regs[FM3130_RTC_CONTROL] & - ~(FM3130_RTC_CONTROL_BIT_AEN)); + ~(FM3130_RTC_CONTROL_BIT_CAL) & + ~(FM3130_RTC_CONTROL_BIT_AEN)); } + + /* We assume here that data is valid once written */ + if (!fm3130->alarm_valid) + fm3130->alarm_valid = 1; + return 0; } +static int fm3130_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct fm3130 *fm3130 = dev_get_drvdata(dev); + int ret = 0; + + fm3130->regs[FM3130_RTC_CONTROL] = + i2c_smbus_read_byte_data(fm3130->client, FM3130_RTC_CONTROL); + + dev_dbg(dev, "alarm_irq_enable: enable=%d, FM3130_RTC_CONTROL=%02x\n", + enabled, fm3130->regs[FM3130_RTC_CONTROL]); + + switch (enabled) { + case 0: /* alarm off */ + ret = i2c_smbus_write_byte_data(fm3130->client, + FM3130_RTC_CONTROL, fm3130->regs[FM3130_RTC_CONTROL] & + ~(FM3130_RTC_CONTROL_BIT_CAL) & + ~(FM3130_RTC_CONTROL_BIT_AEN)); + break; + case 1: /* alarm on */ + ret = i2c_smbus_write_byte_data(fm3130->client, + FM3130_RTC_CONTROL, (fm3130->regs[FM3130_RTC_CONTROL] & + ~(FM3130_RTC_CONTROL_BIT_CAL)) | + FM3130_RTC_CONTROL_BIT_AEN); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + static const struct rtc_class_ops fm3130_rtc_ops = { .read_time = fm3130_get_time, .set_time = fm3130_set_time, .read_alarm = fm3130_read_alarm, .set_alarm = fm3130_set_alarm, + .alarm_irq_enable = fm3130_alarm_irq_enable, }; static struct i2c_driver fm3130_driver; @@ -356,6 +405,7 @@ static int __devinit fm3130_probe(struct i2c_client *client, fm3130->msg[3].len = FM3130_ALARM_REGS; fm3130->msg[3].buf = &fm3130->regs[FM3130_ALARM_SECONDS]; + fm3130->alarm_valid = 0; fm3130->data_valid = 0; tmp = i2c_transfer(adapter, fm3130->msg, 4); @@ -370,12 +420,6 @@ static int __devinit fm3130_probe(struct i2c_client *client, fm3130->regs[FM3130_CAL_CONTROL] = i2c_smbus_read_byte_data(client, FM3130_CAL_CONTROL); - /* Checking for alarm */ - if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_AF) { - fm3130->alarm = 1; - fm3130->regs[FM3130_RTC_CONTROL] &= ~FM3130_RTC_CONTROL_BIT_AF; - } - /* Disabling calibration mode */ if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_CAL) { i2c_smbus_write_byte_data(client, FM3130_RTC_CONTROL, @@ -400,44 +444,79 @@ static int __devinit fm3130_probe(struct i2c_client *client, fm3130->regs[FM3130_CAL_CONTROL] & ~(FM3130_CAL_CONTROL_BIT_nOSCEN)); - /* oscillator fault? clear flag, and warn */ - if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_LB) + /* low battery? clear flag, and warn */ + if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_LB) { + i2c_smbus_write_byte_data(client, FM3130_RTC_CONTROL, + fm3130->regs[FM3130_RTC_CONTROL] & + ~(FM3130_RTC_CONTROL_BIT_LB)); dev_warn(&client->dev, "Low battery!\n"); + } - /* oscillator fault? clear flag, and warn */ + /* check if Power On Reset bit is set */ if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_POR) { i2c_smbus_write_byte_data(client, FM3130_RTC_CONTROL, fm3130->regs[FM3130_RTC_CONTROL] & ~FM3130_RTC_CONTROL_BIT_POR); - dev_warn(&client->dev, "SET TIME!\n"); + dev_dbg(&client->dev, "POR bit is set\n"); } /* ACS is controlled by alarm */ i2c_smbus_write_byte_data(client, FM3130_ALARM_WP_CONTROL, 0x80); - /* TODO */ - /* TODO need to sanity check alarm */ - tmp = fm3130->regs[FM3130_RTC_SECONDS]; - tmp = bcd2bin(tmp & 0x7f); - if (tmp > 60) - goto exit_bad; + /* alarm registers sanity check */ + tmp = bcd2bin(fm3130->regs[FM3130_RTC_SECONDS] & 0x7f); + if (tmp > 59) + goto bad_alarm; + tmp = bcd2bin(fm3130->regs[FM3130_RTC_MINUTES] & 0x7f); - if (tmp > 60) - goto exit_bad; + if (tmp > 59) + goto bad_alarm; + + tmp = bcd2bin(fm3130->regs[FM3130_RTC_HOURS] & 0x3f); + if (tmp > 23) + goto bad_alarm; tmp = bcd2bin(fm3130->regs[FM3130_RTC_DATE] & 0x3f); if (tmp == 0 || tmp > 31) - goto exit_bad; + goto bad_alarm; tmp = bcd2bin(fm3130->regs[FM3130_RTC_MONTHS] & 0x1f); if (tmp == 0 || tmp > 12) - goto exit_bad; + goto bad_alarm; - tmp = fm3130->regs[FM3130_RTC_HOURS]; + fm3130->alarm_valid = 1; + +bad_alarm: + + /* clock registers sanity chek */ + tmp = bcd2bin(fm3130->regs[FM3130_RTC_SECONDS] & 0x7f); + if (tmp > 59) + goto bad_clock; + + tmp = bcd2bin(fm3130->regs[FM3130_RTC_MINUTES] & 0x7f); + if (tmp > 59) + goto bad_clock; + + tmp = bcd2bin(fm3130->regs[FM3130_RTC_HOURS] & 0x3f); + if (tmp > 23) + goto bad_clock; + + tmp = bcd2bin(fm3130->regs[FM3130_RTC_DAY] & 0x7); + if (tmp == 0 || tmp > 7) + goto bad_clock; + + tmp = bcd2bin(fm3130->regs[FM3130_RTC_DATE] & 0x3f); + if (tmp == 0 || tmp > 31) + goto bad_clock; + + tmp = bcd2bin(fm3130->regs[FM3130_RTC_MONTHS] & 0x1f); + if (tmp == 0 || tmp > 12) + goto bad_clock; fm3130->data_valid = 1; -exit_bad: - if (!fm3130->data_valid) +bad_clock: + + if (!fm3130->data_valid || !fm3130->alarm_valid) dev_dbg(&client->dev, "%s: %02x %02x %02x %02x %02x %02x %02x %02x" "%02x %02x %02x %02x %02x %02x %02x\n", diff --git a/drivers/rtc/rtc-imxdi.c b/drivers/rtc/rtc-imxdi.c new file mode 100644 index 00000000000..2dd3c016327 --- /dev/null +++ b/drivers/rtc/rtc-imxdi.c @@ -0,0 +1,519 @@ +/* + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2010 Orex Computed Radiography + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/* based on rtc-mc13892.c */ + +/* + * This driver uses the 47-bit 32 kHz counter in the Freescale DryIce block + * to implement a Linux RTC. Times and alarms are truncated to seconds. + * Since the RTC framework performs API locking via rtc->ops_lock the + * only simultaneous accesses we need to deal with is updating DryIce + * registers while servicing an alarm. + * + * Note that reading the DSR (DryIce Status Register) automatically clears + * the WCF (Write Complete Flag). All DryIce writes are synchronized to the + * LP (Low Power) domain and set the WCF upon completion. Writes to the + * DIER (DryIce Interrupt Enable Register) are the only exception. These + * occur at normal bus speeds and do not set WCF. Periodic interrupts are + * not supported by the hardware. + */ + +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/rtc.h> +#include <linux/workqueue.h> + +/* DryIce Register Definitions */ + +#define DTCMR 0x00 /* Time Counter MSB Reg */ +#define DTCLR 0x04 /* Time Counter LSB Reg */ + +#define DCAMR 0x08 /* Clock Alarm MSB Reg */ +#define DCALR 0x0c /* Clock Alarm LSB Reg */ +#define DCAMR_UNSET 0xFFFFFFFF /* doomsday - 1 sec */ + +#define DCR 0x10 /* Control Reg */ +#define DCR_TCE (1 << 3) /* Time Counter Enable */ + +#define DSR 0x14 /* Status Reg */ +#define DSR_WBF (1 << 10) /* Write Busy Flag */ +#define DSR_WNF (1 << 9) /* Write Next Flag */ +#define DSR_WCF (1 << 8) /* Write Complete Flag */ +#define DSR_WEF (1 << 7) /* Write Error Flag */ +#define DSR_CAF (1 << 4) /* Clock Alarm Flag */ +#define DSR_NVF (1 << 1) /* Non-Valid Flag */ +#define DSR_SVF (1 << 0) /* Security Violation Flag */ + +#define DIER 0x18 /* Interrupt Enable Reg */ +#define DIER_WNIE (1 << 9) /* Write Next Interrupt Enable */ +#define DIER_WCIE (1 << 8) /* Write Complete Interrupt Enable */ +#define DIER_WEIE (1 << 7) /* Write Error Interrupt Enable */ +#define DIER_CAIE (1 << 4) /* Clock Alarm Interrupt Enable */ + +/** + * struct imxdi_dev - private imxdi rtc data + * @pdev: pionter to platform dev + * @rtc: pointer to rtc struct + * @ioaddr: IO registers pointer + * @irq: dryice normal interrupt + * @clk: input reference clock + * @dsr: copy of the DSR register + * @irq_lock: interrupt enable register (DIER) lock + * @write_wait: registers write complete queue + * @write_mutex: serialize registers write + * @work: schedule alarm work + */ +struct imxdi_dev { + struct platform_device *pdev; + struct rtc_device *rtc; + void __iomem *ioaddr; + int irq; + struct clk *clk; + u32 dsr; + spinlock_t irq_lock; + wait_queue_head_t write_wait; + struct mutex write_mutex; + struct work_struct work; +}; + +/* + * enable a dryice interrupt + */ +static void di_int_enable(struct imxdi_dev *imxdi, u32 intr) +{ + unsigned long flags; + + spin_lock_irqsave(&imxdi->irq_lock, flags); + __raw_writel(__raw_readl(imxdi->ioaddr + DIER) | intr, + imxdi->ioaddr + DIER); + spin_unlock_irqrestore(&imxdi->irq_lock, flags); +} + +/* + * disable a dryice interrupt + */ +static void di_int_disable(struct imxdi_dev *imxdi, u32 intr) +{ + unsigned long flags; + + spin_lock_irqsave(&imxdi->irq_lock, flags); + __raw_writel(__raw_readl(imxdi->ioaddr + DIER) & ~intr, + imxdi->ioaddr + DIER); + spin_unlock_irqrestore(&imxdi->irq_lock, flags); +} + +/* + * This function attempts to clear the dryice write-error flag. + * + * A dryice write error is similar to a bus fault and should not occur in + * normal operation. Clearing the flag requires another write, so the root + * cause of the problem may need to be fixed before the flag can be cleared. + */ +static void clear_write_error(struct imxdi_dev *imxdi) +{ + int cnt; + + dev_warn(&imxdi->pdev->dev, "WARNING: Register write error!\n"); + + /* clear the write error flag */ + __raw_writel(DSR_WEF, imxdi->ioaddr + DSR); + + /* wait for it to take effect */ + for (cnt = 0; cnt < 1000; cnt++) { + if ((__raw_readl(imxdi->ioaddr + DSR) & DSR_WEF) == 0) + return; + udelay(10); + } + dev_err(&imxdi->pdev->dev, + "ERROR: Cannot clear write-error flag!\n"); +} + +/* + * Write a dryice register and wait until it completes. + * + * This function uses interrupts to determine when the + * write has completed. + */ +static int di_write_wait(struct imxdi_dev *imxdi, u32 val, int reg) +{ + int ret; + int rc = 0; + + /* serialize register writes */ + mutex_lock(&imxdi->write_mutex); + + /* enable the write-complete interrupt */ + di_int_enable(imxdi, DIER_WCIE); + + imxdi->dsr = 0; + + /* do the register write */ + __raw_writel(val, imxdi->ioaddr + reg); + + /* wait for the write to finish */ + ret = wait_event_interruptible_timeout(imxdi->write_wait, + imxdi->dsr & (DSR_WCF | DSR_WEF), msecs_to_jiffies(1)); + if (ret < 0) { + rc = ret; + goto out; + } else if (ret == 0) { + dev_warn(&imxdi->pdev->dev, + "Write-wait timeout " + "val = 0x%08x reg = 0x%08x\n", val, reg); + } + + /* check for write error */ + if (imxdi->dsr & DSR_WEF) { + clear_write_error(imxdi); + rc = -EIO; + } + +out: + mutex_unlock(&imxdi->write_mutex); + + return rc; +} + +/* + * read the seconds portion of the current time from the dryice time counter + */ +static int dryice_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct imxdi_dev *imxdi = dev_get_drvdata(dev); + unsigned long now; + + now = __raw_readl(imxdi->ioaddr + DTCMR); + rtc_time_to_tm(now, tm); + + return 0; +} + +/* + * set the seconds portion of dryice time counter and clear the + * fractional part. + */ +static int dryice_rtc_set_mmss(struct device *dev, unsigned long secs) +{ + struct imxdi_dev *imxdi = dev_get_drvdata(dev); + int rc; + + /* zero the fractional part first */ + rc = di_write_wait(imxdi, 0, DTCLR); + if (rc == 0) + rc = di_write_wait(imxdi, secs, DTCMR); + + return rc; +} + +static int dryice_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct imxdi_dev *imxdi = dev_get_drvdata(dev); + + if (enabled) + di_int_enable(imxdi, DIER_CAIE); + else + di_int_disable(imxdi, DIER_CAIE); + + return 0; +} + +/* + * read the seconds portion of the alarm register. + * the fractional part of the alarm register is always zero. + */ +static int dryice_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct imxdi_dev *imxdi = dev_get_drvdata(dev); + u32 dcamr; + + dcamr = __raw_readl(imxdi->ioaddr + DCAMR); + rtc_time_to_tm(dcamr, &alarm->time); + + /* alarm is enabled if the interrupt is enabled */ + alarm->enabled = (__raw_readl(imxdi->ioaddr + DIER) & DIER_CAIE) != 0; + + /* don't allow the DSR read to mess up DSR_WCF */ + mutex_lock(&imxdi->write_mutex); + + /* alarm is pending if the alarm flag is set */ + alarm->pending = (__raw_readl(imxdi->ioaddr + DSR) & DSR_CAF) != 0; + + mutex_unlock(&imxdi->write_mutex); + + return 0; +} + +/* + * set the seconds portion of dryice alarm register + */ +static int dryice_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct imxdi_dev *imxdi = dev_get_drvdata(dev); + unsigned long now; + unsigned long alarm_time; + int rc; + + rc = rtc_tm_to_time(&alarm->time, &alarm_time); + if (rc) + return rc; + + /* don't allow setting alarm in the past */ + now = __raw_readl(imxdi->ioaddr + DTCMR); + if (alarm_time < now) + return -EINVAL; + + /* write the new alarm time */ + rc = di_write_wait(imxdi, (u32)alarm_time, DCAMR); + if (rc) + return rc; + + if (alarm->enabled) + di_int_enable(imxdi, DIER_CAIE); /* enable alarm intr */ + else + di_int_disable(imxdi, DIER_CAIE); /* disable alarm intr */ + + return 0; +} + +static struct rtc_class_ops dryice_rtc_ops = { + .read_time = dryice_rtc_read_time, + .set_mmss = dryice_rtc_set_mmss, + .alarm_irq_enable = dryice_rtc_alarm_irq_enable, + .read_alarm = dryice_rtc_read_alarm, + .set_alarm = dryice_rtc_set_alarm, +}; + +/* + * dryice "normal" interrupt handler + */ +static irqreturn_t dryice_norm_irq(int irq, void *dev_id) +{ + struct imxdi_dev *imxdi = dev_id; + u32 dsr, dier; + irqreturn_t rc = IRQ_NONE; + + dier = __raw_readl(imxdi->ioaddr + DIER); + + /* handle write complete and write error cases */ + if ((dier & DIER_WCIE)) { + /*If the write wait queue is empty then there is no pending + operations. It means the interrupt is for DryIce -Security. + IRQ must be returned as none.*/ + if (list_empty_careful(&imxdi->write_wait.task_list)) + return rc; + + /* DSR_WCF clears itself on DSR read */ + dsr = __raw_readl(imxdi->ioaddr + DSR); + if ((dsr & (DSR_WCF | DSR_WEF))) { + /* mask the interrupt */ + di_int_disable(imxdi, DIER_WCIE); + + /* save the dsr value for the wait queue */ + imxdi->dsr |= dsr; + + wake_up_interruptible(&imxdi->write_wait); + rc = IRQ_HANDLED; + } + } + + /* handle the alarm case */ + if ((dier & DIER_CAIE)) { + /* DSR_WCF clears itself on DSR read */ + dsr = __raw_readl(imxdi->ioaddr + DSR); + if (dsr & DSR_CAF) { + /* mask the interrupt */ + di_int_disable(imxdi, DIER_CAIE); + + /* finish alarm in user context */ + schedule_work(&imxdi->work); + rc = IRQ_HANDLED; + } + } + return rc; +} + +/* + * post the alarm event from user context so it can sleep + * on the write completion. + */ +static void dryice_work(struct work_struct *work) +{ + struct imxdi_dev *imxdi = container_of(work, + struct imxdi_dev, work); + + /* dismiss the interrupt (ignore error) */ + di_write_wait(imxdi, DSR_CAF, DSR); + + /* pass the alarm event to the rtc framework. */ + rtc_update_irq(imxdi->rtc, 1, RTC_AF | RTC_IRQF); +} + +/* + * probe for dryice rtc device + */ +static int dryice_rtc_probe(struct platform_device *pdev) +{ + struct resource *res; + struct imxdi_dev *imxdi; + int rc; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + imxdi = devm_kzalloc(&pdev->dev, sizeof(*imxdi), GFP_KERNEL); + if (!imxdi) + return -ENOMEM; + + imxdi->pdev = pdev; + + if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res), + pdev->name)) + return -EBUSY; + + imxdi->ioaddr = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (imxdi->ioaddr == NULL) + return -ENOMEM; + + imxdi->irq = platform_get_irq(pdev, 0); + if (imxdi->irq < 0) + return imxdi->irq; + + init_waitqueue_head(&imxdi->write_wait); + + INIT_WORK(&imxdi->work, dryice_work); + + mutex_init(&imxdi->write_mutex); + + imxdi->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(imxdi->clk)) + return PTR_ERR(imxdi->clk); + clk_enable(imxdi->clk); + + /* + * Initialize dryice hardware + */ + + /* mask all interrupts */ + __raw_writel(0, imxdi->ioaddr + DIER); + + rc = devm_request_irq(&pdev->dev, imxdi->irq, dryice_norm_irq, + IRQF_SHARED, pdev->name, imxdi); + if (rc) { + dev_warn(&pdev->dev, "interrupt not available.\n"); + goto err; + } + + /* put dryice into valid state */ + if (__raw_readl(imxdi->ioaddr + DSR) & DSR_NVF) { + rc = di_write_wait(imxdi, DSR_NVF | DSR_SVF, DSR); + if (rc) + goto err; + } + + /* initialize alarm */ + rc = di_write_wait(imxdi, DCAMR_UNSET, DCAMR); + if (rc) + goto err; + rc = di_write_wait(imxdi, 0, DCALR); + if (rc) + goto err; + + /* clear alarm flag */ + if (__raw_readl(imxdi->ioaddr + DSR) & DSR_CAF) { + rc = di_write_wait(imxdi, DSR_CAF, DSR); + if (rc) + goto err; + } + + /* the timer won't count if it has never been written to */ + if (__raw_readl(imxdi->ioaddr + DTCMR) == 0) { + rc = di_write_wait(imxdi, 0, DTCMR); + if (rc) + goto err; + } + + /* start keeping time */ + if (!(__raw_readl(imxdi->ioaddr + DCR) & DCR_TCE)) { + rc = di_write_wait(imxdi, + __raw_readl(imxdi->ioaddr + DCR) | DCR_TCE, + DCR); + if (rc) + goto err; + } + + platform_set_drvdata(pdev, imxdi); + imxdi->rtc = rtc_device_register(pdev->name, &pdev->dev, + &dryice_rtc_ops, THIS_MODULE); + if (IS_ERR(imxdi->rtc)) { + rc = PTR_ERR(imxdi->rtc); + goto err; + } + + return 0; + +err: + clk_disable(imxdi->clk); + clk_put(imxdi->clk); + + return rc; +} + +static int __devexit dryice_rtc_remove(struct platform_device *pdev) +{ + struct imxdi_dev *imxdi = platform_get_drvdata(pdev); + + flush_work(&imxdi->work); + + /* mask all interrupts */ + __raw_writel(0, imxdi->ioaddr + DIER); + + rtc_device_unregister(imxdi->rtc); + + clk_disable(imxdi->clk); + clk_put(imxdi->clk); + + return 0; +} + +static struct platform_driver dryice_rtc_driver = { + .driver = { + .name = "imxdi_rtc", + .owner = THIS_MODULE, + }, + .remove = __devexit_p(dryice_rtc_remove), +}; + +static int __init dryice_rtc_init(void) +{ + return platform_driver_probe(&dryice_rtc_driver, dryice_rtc_probe); +} + +static void __exit dryice_rtc_exit(void) +{ + platform_driver_unregister(&dryice_rtc_driver); +} + +module_init(dryice_rtc_init); +module_exit(dryice_rtc_exit); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>"); +MODULE_DESCRIPTION("IMX DryIce Realtime Clock Driver (RTC)"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-isl12022.c b/drivers/rtc/rtc-isl12022.c new file mode 100644 index 00000000000..ddbc797ea6c --- /dev/null +++ b/drivers/rtc/rtc-isl12022.c @@ -0,0 +1,327 @@ +/* + * An I2C driver for the Intersil ISL 12022 + * + * Author: Roman Fietze <roman.fietze@telemotive.de> + * + * Based on the Philips PCF8563 RTC + * by Alessandro Zummo <a.zummo@towertech.it>. + * + * 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/i2c.h> +#include <linux/bcd.h> +#include <linux/rtc.h> +#include <linux/slab.h> + +#define DRV_VERSION "0.1" + +/* ISL register offsets */ +#define ISL12022_REG_SC 0x00 +#define ISL12022_REG_MN 0x01 +#define ISL12022_REG_HR 0x02 +#define ISL12022_REG_DT 0x03 +#define ISL12022_REG_MO 0x04 +#define ISL12022_REG_YR 0x05 +#define ISL12022_REG_DW 0x06 + +#define ISL12022_REG_SR 0x07 +#define ISL12022_REG_INT 0x08 + +/* ISL register bits */ +#define ISL12022_HR_MIL (1 << 7) /* military or 24 hour time */ + +#define ISL12022_SR_LBAT85 (1 << 2) +#define ISL12022_SR_LBAT75 (1 << 1) + +#define ISL12022_INT_WRTC (1 << 6) + + +static struct i2c_driver isl12022_driver; + +struct isl12022 { + struct rtc_device *rtc; + + bool write_enabled; /* true if write enable is set */ +}; + + +static int isl12022_read_regs(struct i2c_client *client, uint8_t reg, + uint8_t *data, size_t n) +{ + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = data + }, /* setup read ptr */ + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = n, + .buf = data + } + }; + + int ret; + + data[0] = reg; + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) { + dev_err(&client->dev, "%s: read error, ret=%d\n", + __func__, ret); + return -EIO; + } + + return 0; +} + + +static int isl12022_write_reg(struct i2c_client *client, + uint8_t reg, uint8_t val) +{ + uint8_t data[2] = { reg, val }; + int err; + + err = i2c_master_send(client, data, sizeof(data)); + if (err != sizeof(data)) { + dev_err(&client->dev, + "%s: err=%d addr=%02x, data=%02x\n", + __func__, err, data[0], data[1]); + return -EIO; + } + + return 0; +} + + +/* + * In the routines that deal directly with the isl12022 hardware, we use + * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch. + */ +static int isl12022_get_datetime(struct i2c_client *client, struct rtc_time *tm) +{ + uint8_t buf[ISL12022_REG_INT + 1]; + int ret; + + ret = isl12022_read_regs(client, ISL12022_REG_SC, buf, sizeof(buf)); + if (ret) + return ret; + + if (buf[ISL12022_REG_SR] & (ISL12022_SR_LBAT85 | ISL12022_SR_LBAT75)) { + dev_warn(&client->dev, + "voltage dropped below %u%%, " + "date and time is not reliable.\n", + buf[ISL12022_REG_SR] & ISL12022_SR_LBAT85 ? 85 : 75); + } + + dev_dbg(&client->dev, + "%s: raw data is sec=%02x, min=%02x, hr=%02x, " + "mday=%02x, mon=%02x, year=%02x, wday=%02x, " + "sr=%02x, int=%02x", + __func__, + buf[ISL12022_REG_SC], + buf[ISL12022_REG_MN], + buf[ISL12022_REG_HR], + buf[ISL12022_REG_DT], + buf[ISL12022_REG_MO], + buf[ISL12022_REG_YR], + buf[ISL12022_REG_DW], + buf[ISL12022_REG_SR], + buf[ISL12022_REG_INT]); + + tm->tm_sec = bcd2bin(buf[ISL12022_REG_SC] & 0x7F); + tm->tm_min = bcd2bin(buf[ISL12022_REG_MN] & 0x7F); + tm->tm_hour = bcd2bin(buf[ISL12022_REG_HR] & 0x3F); + tm->tm_mday = bcd2bin(buf[ISL12022_REG_DT] & 0x3F); + tm->tm_wday = buf[ISL12022_REG_DW] & 0x07; + tm->tm_mon = bcd2bin(buf[ISL12022_REG_MO] & 0x1F) - 1; + tm->tm_year = bcd2bin(buf[ISL12022_REG_YR]) + 100; + + dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, " + "mday=%d, mon=%d, year=%d, wday=%d\n", + __func__, + tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + + /* The clock can give out invalid datetime, but we cannot return + * -EINVAL otherwise hwclock will refuse to set the time on bootup. */ + if (rtc_valid_tm(tm) < 0) + dev_err(&client->dev, "retrieved date and time is invalid.\n"); + + return 0; +} + +static int isl12022_set_datetime(struct i2c_client *client, struct rtc_time *tm) +{ + struct isl12022 *isl12022 = i2c_get_clientdata(client); + size_t i; + int ret; + uint8_t buf[ISL12022_REG_DW + 1]; + + dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, " + "mday=%d, mon=%d, year=%d, wday=%d\n", + __func__, + tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + + if (!isl12022->write_enabled) { + + ret = isl12022_read_regs(client, ISL12022_REG_INT, buf, 1); + if (ret) + return ret; + + /* Check if WRTC (write rtc enable) is set factory default is + * 0 (not set) */ + if (!(buf[0] & ISL12022_INT_WRTC)) { + dev_info(&client->dev, + "init write enable and 24 hour format\n"); + + /* Set the write enable bit. */ + ret = isl12022_write_reg(client, + ISL12022_REG_INT, + buf[0] | ISL12022_INT_WRTC); + if (ret) + return ret; + + /* Write to any RTC register to start RTC, we use the + * HR register, setting the MIL bit to use the 24 hour + * format. */ + ret = isl12022_read_regs(client, ISL12022_REG_HR, + buf, 1); + if (ret) + return ret; + + ret = isl12022_write_reg(client, + ISL12022_REG_HR, + buf[0] | ISL12022_HR_MIL); + if (ret) + return ret; + } + + isl12022->write_enabled = 1; + } + + /* hours, minutes and seconds */ + buf[ISL12022_REG_SC] = bin2bcd(tm->tm_sec); + buf[ISL12022_REG_MN] = bin2bcd(tm->tm_min); + buf[ISL12022_REG_HR] = bin2bcd(tm->tm_hour) | ISL12022_HR_MIL; + + buf[ISL12022_REG_DT] = bin2bcd(tm->tm_mday); + + /* month, 1 - 12 */ + buf[ISL12022_REG_MO] = bin2bcd(tm->tm_mon + 1); + + /* year and century */ + buf[ISL12022_REG_YR] = bin2bcd(tm->tm_year % 100); + + buf[ISL12022_REG_DW] = tm->tm_wday & 0x07; + + /* write register's data */ + for (i = 0; i < ARRAY_SIZE(buf); i++) { + ret = isl12022_write_reg(client, ISL12022_REG_SC + i, + buf[ISL12022_REG_SC + i]); + if (ret) + return -EIO; + }; + + return 0; +} + +static int isl12022_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + return isl12022_get_datetime(to_i2c_client(dev), tm); +} + +static int isl12022_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + return isl12022_set_datetime(to_i2c_client(dev), tm); +} + +static const struct rtc_class_ops isl12022_rtc_ops = { + .read_time = isl12022_rtc_read_time, + .set_time = isl12022_rtc_set_time, +}; + +static int isl12022_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct isl12022 *isl12022; + + int ret = 0; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + isl12022 = kzalloc(sizeof(struct isl12022), GFP_KERNEL); + if (!isl12022) + return -ENOMEM; + + dev_dbg(&client->dev, "chip found, driver version " DRV_VERSION "\n"); + + i2c_set_clientdata(client, isl12022); + + isl12022->rtc = rtc_device_register(isl12022_driver.driver.name, + &client->dev, + &isl12022_rtc_ops, + THIS_MODULE); + + if (IS_ERR(isl12022->rtc)) { + ret = PTR_ERR(isl12022->rtc); + goto exit_kfree; + } + + return 0; + +exit_kfree: + kfree(isl12022); + + return ret; +} + +static int isl12022_remove(struct i2c_client *client) +{ + struct isl12022 *isl12022 = i2c_get_clientdata(client); + + rtc_device_unregister(isl12022->rtc); + kfree(isl12022); + + return 0; +} + +static const struct i2c_device_id isl12022_id[] = { + { "isl12022", 0 }, + { "rtc8564", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, isl12022_id); + +static struct i2c_driver isl12022_driver = { + .driver = { + .name = "rtc-isl12022", + }, + .probe = isl12022_probe, + .remove = isl12022_remove, + .id_table = isl12022_id, +}; + +static int __init isl12022_init(void) +{ + return i2c_add_driver(&isl12022_driver); +} + +static void __exit isl12022_exit(void) +{ + i2c_del_driver(&isl12022_driver); +} + +module_init(isl12022_init); +module_exit(isl12022_exit); + +MODULE_AUTHOR("roman.fietze@telemotive.de"); +MODULE_DESCRIPTION("ISL 12022 RTC driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c index 6dc4e624141..66377f3e28b 100644 --- a/drivers/rtc/rtc-m41t80.c +++ b/drivers/rtc/rtc-m41t80.c @@ -121,7 +121,7 @@ static int m41t80_get_datetime(struct i2c_client *client, /* assume 20YY not 19YY, and ignore the Century Bit */ tm->tm_year = bcd2bin(buf[M41T80_REG_YEAR]) + 100; - return 0; + return rtc_valid_tm(tm); } /* Sets the given date and time to the real time clock. */ @@ -364,7 +364,7 @@ static int m41t80_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *t) t->time.tm_isdst = -1; t->enabled = !!(reg[M41T80_REG_ALARM_MON] & M41T80_ALMON_AFE); t->pending = !!(reg[M41T80_REG_FLAGS] & M41T80_FLAGS_AF); - return 0; + return rtc_valid_tm(t); } static struct rtc_class_ops m41t80_rtc_ops = { diff --git a/drivers/rtc/rtc-m48t59.c b/drivers/rtc/rtc-m48t59.c index be8359fdb65..a99a0b554eb 100644 --- a/drivers/rtc/rtc-m48t59.c +++ b/drivers/rtc/rtc-m48t59.c @@ -105,7 +105,7 @@ static int m48t59_rtc_read_time(struct device *dev, struct rtc_time *tm) dev_dbg(dev, "RTC read time %04d-%02d-%02d %02d/%02d/%02d\n", tm->tm_year + 1900, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); - return 0; + return rtc_valid_tm(tm); } static int m48t59_rtc_set_time(struct device *dev, struct rtc_time *tm) @@ -196,7 +196,7 @@ static int m48t59_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm) dev_dbg(dev, "RTC read alarm time %04d-%02d-%02d %02d/%02d/%02d\n", tm->tm_year + 1900, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); - return 0; + return rtc_valid_tm(tm); } /* @@ -506,7 +506,6 @@ out: free_irq(m48t59->irq, &pdev->dev); if (m48t59->ioaddr) iounmap(m48t59->ioaddr); - if (m48t59) kfree(m48t59); return ret; } diff --git a/drivers/rtc/rtc-m48t86.c b/drivers/rtc/rtc-m48t86.c index 7c045cffa9f..f981287d582 100644 --- a/drivers/rtc/rtc-m48t86.c +++ b/drivers/rtc/rtc-m48t86.c @@ -77,7 +77,7 @@ static int m48t86_rtc_read_time(struct device *dev, struct rtc_time *tm) if (ops->readbyte(M48T86_REG_HOUR) & 0x80) tm->tm_hour += 12; - return 0; + return rtc_valid_tm(tm); } static int m48t86_rtc_set_time(struct device *dev, struct rtc_time *tm) diff --git a/drivers/rtc/rtc-max6900.c b/drivers/rtc/rtc-max6900.c index a4f6665ab3c..486142c2637 100644 --- a/drivers/rtc/rtc-max6900.c +++ b/drivers/rtc/rtc-max6900.c @@ -159,7 +159,7 @@ static int max6900_i2c_read_time(struct i2c_client *client, struct rtc_time *tm) bcd2bin(regs[MAX6900_REG_CENTURY]) * 100 - 1900; tm->tm_wday = bcd2bin(regs[MAX6900_REG_DW]); - return 0; + return rtc_valid_tm(tm); } static int max6900_i2c_clear_write_protect(struct i2c_client *client) diff --git a/drivers/rtc/rtc-mpc5121.c b/drivers/rtc/rtc-mpc5121.c index db5d8c416d2..dfcdf0901d2 100644 --- a/drivers/rtc/rtc-mpc5121.c +++ b/drivers/rtc/rtc-mpc5121.c @@ -268,7 +268,7 @@ static const struct rtc_class_ops mpc5121_rtc_ops = { .update_irq_enable = mpc5121_rtc_update_irq_enable, }; -static int __devinit mpc5121_rtc_probe(struct of_device *op, +static int __devinit mpc5121_rtc_probe(struct platform_device *op, const struct of_device_id *match) { struct mpc5121_rtc_data *rtc; @@ -338,7 +338,7 @@ out_free: return err; } -static int __devexit mpc5121_rtc_remove(struct of_device *op) +static int __devexit mpc5121_rtc_remove(struct platform_device *op) { struct mpc5121_rtc_data *rtc = dev_get_drvdata(&op->dev); struct mpc5121_rtc_regs __iomem *regs = rtc->regs; diff --git a/drivers/rtc/rtc-mxc.c b/drivers/rtc/rtc-mxc.c index 25ec921db07..0b06c1e03fd 100644 --- a/drivers/rtc/rtc-mxc.c +++ b/drivers/rtc/rtc-mxc.c @@ -83,12 +83,6 @@ struct rtc_plat_data { void __iomem *ioaddr; int irq; struct clk *clk; - unsigned int irqen; - int alrm_sec; - int alrm_min; - int alrm_hour; - int alrm_mday; - struct timespec mxc_rtc_delta; struct rtc_time g_rtc_alarm; }; diff --git a/drivers/rtc/rtc-nuc900.c b/drivers/rtc/rtc-nuc900.c index a351bd5d817..62de66af0a6 100644 --- a/drivers/rtc/rtc-nuc900.c +++ b/drivers/rtc/rtc-nuc900.c @@ -85,25 +85,24 @@ static irqreturn_t nuc900_rtc_interrupt(int irq, void *_rtc) static int *check_rtc_access_enable(struct nuc900_rtc *nuc900_rtc) { - unsigned int i; + unsigned int timeout = 0x1000; __raw_writel(INIRRESET, nuc900_rtc->rtc_reg + REG_RTC_INIR); mdelay(10); __raw_writel(AERPOWERON, nuc900_rtc->rtc_reg + REG_RTC_AER); - for (i = 0; i < 1000; i++) { - if (__raw_readl(nuc900_rtc->rtc_reg + REG_RTC_AER) & AERRWENB) - return 0; - } + while (!(__raw_readl(nuc900_rtc->rtc_reg + REG_RTC_AER) & AERRWENB) + && timeout--) + mdelay(1); - if ((__raw_readl(nuc900_rtc->rtc_reg + REG_RTC_AER) & AERRWENB) == 0x0) - return ERR_PTR(-ENODEV); + if (!timeout) + return ERR_PTR(-EPERM); - return ERR_PTR(-EPERM); + return 0; } -static void nuc900_rtc_bcd2bin(unsigned int timereg, +static int nuc900_rtc_bcd2bin(unsigned int timereg, unsigned int calreg, struct rtc_time *tm) { tm->tm_mday = bcd2bin(calreg >> 0); @@ -114,15 +113,21 @@ static void nuc900_rtc_bcd2bin(unsigned int timereg, tm->tm_min = bcd2bin(timereg >> 8); tm->tm_hour = bcd2bin(timereg >> 16); - rtc_valid_tm(tm); + return rtc_valid_tm(tm); } -static void nuc900_rtc_bin2bcd(struct rtc_time *settm, +static void nuc900_rtc_bin2bcd(struct device *dev, struct rtc_time *settm, struct nuc900_bcd_time *gettm) { gettm->bcd_mday = bin2bcd(settm->tm_mday) << 0; gettm->bcd_mon = bin2bcd(settm->tm_mon) << 8; - gettm->bcd_year = bin2bcd(settm->tm_year - 100) << 16; + + if (settm->tm_year < 100) { + dev_warn(dev, "The year will be between 1970-1999, right?\n"); + gettm->bcd_year = bin2bcd(settm->tm_year) << 16; + } else { + gettm->bcd_year = bin2bcd(settm->tm_year - 100) << 16; + } gettm->bcd_sec = bin2bcd(settm->tm_sec) << 0; gettm->bcd_min = bin2bcd(settm->tm_min) << 8; @@ -165,9 +170,7 @@ static int nuc900_rtc_read_time(struct device *dev, struct rtc_time *tm) timeval = __raw_readl(rtc->rtc_reg + REG_RTC_TLR); clrval = __raw_readl(rtc->rtc_reg + REG_RTC_CLR); - nuc900_rtc_bcd2bin(timeval, clrval, tm); - - return 0; + return nuc900_rtc_bcd2bin(timeval, clrval, tm); } static int nuc900_rtc_set_time(struct device *dev, struct rtc_time *tm) @@ -177,7 +180,7 @@ static int nuc900_rtc_set_time(struct device *dev, struct rtc_time *tm) unsigned long val; int *err; - nuc900_rtc_bin2bcd(tm, &gettm); + nuc900_rtc_bin2bcd(dev, tm, &gettm); err = check_rtc_access_enable(rtc); if (IS_ERR(err)) @@ -200,9 +203,7 @@ static int nuc900_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) timeval = __raw_readl(rtc->rtc_reg + REG_RTC_TAR); carval = __raw_readl(rtc->rtc_reg + REG_RTC_CAR); - nuc900_rtc_bcd2bin(timeval, carval, &alrm->time); - - return 0; + return nuc900_rtc_bcd2bin(timeval, carval, &alrm->time); } static int nuc900_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) @@ -212,7 +213,7 @@ static int nuc900_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) unsigned long val; int *err; - nuc900_rtc_bin2bcd(&alrm->time, &tm); + nuc900_rtc_bin2bcd(dev, &alrm->time, &tm); err = check_rtc_access_enable(rtc); if (IS_ERR(err)) @@ -268,29 +269,30 @@ static int __devinit nuc900_rtc_probe(struct platform_device *pdev) goto fail2; } - nuc900_rtc->irq_num = platform_get_irq(pdev, 0); - if (request_irq(nuc900_rtc->irq_num, nuc900_rtc_interrupt, - IRQF_DISABLED, "nuc900rtc", nuc900_rtc)) { - dev_err(&pdev->dev, "NUC900 RTC request irq failed\n"); - err = -EBUSY; - goto fail3; - } + platform_set_drvdata(pdev, nuc900_rtc); nuc900_rtc->rtcdev = rtc_device_register(pdev->name, &pdev->dev, &nuc900_rtc_ops, THIS_MODULE); if (IS_ERR(nuc900_rtc->rtcdev)) { dev_err(&pdev->dev, "rtc device register faild\n"); err = PTR_ERR(nuc900_rtc->rtcdev); - goto fail4; + goto fail3; } - platform_set_drvdata(pdev, nuc900_rtc); __raw_writel(__raw_readl(nuc900_rtc->rtc_reg + REG_RTC_TSSR) | MODE24, nuc900_rtc->rtc_reg + REG_RTC_TSSR); + nuc900_rtc->irq_num = platform_get_irq(pdev, 0); + if (request_irq(nuc900_rtc->irq_num, nuc900_rtc_interrupt, + IRQF_DISABLED, "nuc900rtc", nuc900_rtc)) { + dev_err(&pdev->dev, "NUC900 RTC request irq failed\n"); + err = -EBUSY; + goto fail4; + } + return 0; -fail4: free_irq(nuc900_rtc->irq_num, nuc900_rtc); +fail4: rtc_device_unregister(nuc900_rtc->rtcdev); fail3: iounmap(nuc900_rtc->rtc_reg); fail2: release_mem_region(res->start, resource_size(res)); fail1: kfree(nuc900_rtc); @@ -302,8 +304,8 @@ static int __devexit nuc900_rtc_remove(struct platform_device *pdev) struct nuc900_rtc *nuc900_rtc = platform_get_drvdata(pdev); struct resource *res; - rtc_device_unregister(nuc900_rtc->rtcdev); free_irq(nuc900_rtc->irq_num, nuc900_rtc); + rtc_device_unregister(nuc900_rtc->rtcdev); iounmap(nuc900_rtc->rtc_reg); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c index 1af42b4a6f5..b42c0c67926 100644 --- a/drivers/rtc/rtc-pcf8563.c +++ b/drivers/rtc/rtc-pcf8563.c @@ -172,14 +172,6 @@ static int pcf8563_set_datetime(struct i2c_client *client, struct rtc_time *tm) return 0; } -struct pcf8563_limit -{ - unsigned char reg; - unsigned char mask; - unsigned char min; - unsigned char max; -}; - static int pcf8563_rtc_read_time(struct device *dev, struct rtc_time *tm) { return pcf8563_get_datetime(to_i2c_client(dev), tm); diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c index 71bbefc3544..6c418fe7f28 100644 --- a/drivers/rtc/rtc-pl031.c +++ b/drivers/rtc/rtc-pl031.c @@ -23,7 +23,6 @@ #include <linux/io.h> #include <linux/bcd.h> #include <linux/delay.h> -#include <linux/version.h> #include <linux/slab.h> /* diff --git a/drivers/rtc/rtc-pxa.c b/drivers/rtc/rtc-pxa.c index e9c6fa03598..29e867a1aaa 100644 --- a/drivers/rtc/rtc-pxa.c +++ b/drivers/rtc/rtc-pxa.c @@ -87,7 +87,6 @@ struct pxa_rtc { int irq_Alrm; struct rtc_device *rtc; spinlock_t lock; /* Protects this structure */ - struct rtc_time rtc_alarm; }; static u32 ryxr_calc(struct rtc_time *tm) @@ -236,32 +235,34 @@ static int pxa_periodic_irq_set_state(struct device *dev, int enabled) return 0; } -static int pxa_rtc_ioctl(struct device *dev, unsigned int cmd, - unsigned long arg) +static int pxa_alarm_irq_enable(struct device *dev, unsigned int enabled) { struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev); - int ret = 0; spin_lock_irq(&pxa_rtc->lock); - switch (cmd) { - case RTC_AIE_OFF: - rtsr_clear_bits(pxa_rtc, RTSR_RDALE1); - break; - case RTC_AIE_ON: + + if (enabled) rtsr_set_bits(pxa_rtc, RTSR_RDALE1); - break; - case RTC_UIE_OFF: - rtsr_clear_bits(pxa_rtc, RTSR_HZE); - break; - case RTC_UIE_ON: + else + rtsr_clear_bits(pxa_rtc, RTSR_RDALE1); + + spin_unlock_irq(&pxa_rtc->lock); + return 0; +} + +static int pxa_update_irq_enable(struct device *dev, unsigned int enabled) +{ + struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev); + + spin_lock_irq(&pxa_rtc->lock); + + if (enabled) rtsr_set_bits(pxa_rtc, RTSR_HZE); - break; - default: - ret = -ENOIOCTLCMD; - } + else + rtsr_clear_bits(pxa_rtc, RTSR_HZE); spin_unlock_irq(&pxa_rtc->lock); - return ret; + return 0; } static int pxa_rtc_read_time(struct device *dev, struct rtc_time *tm) @@ -340,11 +341,12 @@ static int pxa_rtc_proc(struct device *dev, struct seq_file *seq) static const struct rtc_class_ops pxa_rtc_ops = { .open = pxa_rtc_open, .release = pxa_rtc_release, - .ioctl = pxa_rtc_ioctl, .read_time = pxa_rtc_read_time, .set_time = pxa_rtc_set_time, .read_alarm = pxa_rtc_read_alarm, .set_alarm = pxa_rtc_set_alarm, + .alarm_irq_enable = pxa_alarm_irq_enable, + .update_irq_enable = pxa_update_irq_enable, .proc = pxa_rtc_proc, .irq_set_state = pxa_periodic_irq_set_state, .irq_set_freq = pxa_periodic_irq_set_freq, diff --git a/drivers/rtc/rtc-rp5c01.c b/drivers/rtc/rtc-rp5c01.c index a95f733bb15..36eb6618446 100644 --- a/drivers/rtc/rtc-rp5c01.c +++ b/drivers/rtc/rtc-rp5c01.c @@ -63,6 +63,8 @@ enum { struct rp5c01_priv { u32 __iomem *regs; struct rtc_device *rtc; + spinlock_t lock; /* against concurrent RTC/NVRAM access */ + struct bin_attribute nvram_attr; }; static inline unsigned int rp5c01_read(struct rp5c01_priv *priv, @@ -92,6 +94,7 @@ static int rp5c01_read_time(struct device *dev, struct rtc_time *tm) { struct rp5c01_priv *priv = dev_get_drvdata(dev); + spin_lock_irq(&priv->lock); rp5c01_lock(priv); tm->tm_sec = rp5c01_read(priv, RP5C01_10_SECOND) * 10 + @@ -111,6 +114,7 @@ static int rp5c01_read_time(struct device *dev, struct rtc_time *tm) tm->tm_year += 100; rp5c01_unlock(priv); + spin_unlock_irq(&priv->lock); return rtc_valid_tm(tm); } @@ -119,6 +123,7 @@ static int rp5c01_set_time(struct device *dev, struct rtc_time *tm) { struct rp5c01_priv *priv = dev_get_drvdata(dev); + spin_lock_irq(&priv->lock); rp5c01_lock(priv); rp5c01_write(priv, tm->tm_sec / 10, RP5C01_10_SECOND); @@ -139,6 +144,7 @@ static int rp5c01_set_time(struct device *dev, struct rtc_time *tm) rp5c01_write(priv, tm->tm_year % 10, RP5C01_1_YEAR); rp5c01_unlock(priv); + spin_unlock_irq(&priv->lock); return 0; } @@ -147,6 +153,72 @@ static const struct rtc_class_ops rp5c01_rtc_ops = { .set_time = rp5c01_set_time, }; + +/* + * The NVRAM is organized as 2 blocks of 13 nibbles of 4 bits. + * We provide access to them like AmigaOS does: the high nibble of each 8-bit + * byte is stored in BLOCK10, the low nibble in BLOCK11. + */ + +static ssize_t rp5c01_nvram_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t pos, size_t size) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct rp5c01_priv *priv = dev_get_drvdata(dev); + ssize_t count; + + spin_lock_irq(&priv->lock); + + for (count = 0; size > 0 && pos < RP5C01_MODE; count++, size--) { + u8 data; + + rp5c01_write(priv, + RP5C01_MODE_TIMER_EN | RP5C01_MODE_RAM_BLOCK10, + RP5C01_MODE); + data = rp5c01_read(priv, pos) << 4; + rp5c01_write(priv, + RP5C01_MODE_TIMER_EN | RP5C01_MODE_RAM_BLOCK11, + RP5C01_MODE); + data |= rp5c01_read(priv, pos++); + rp5c01_write(priv, RP5C01_MODE_TIMER_EN | RP5C01_MODE_MODE01, + RP5C01_MODE); + *buf++ = data; + } + + spin_unlock_irq(&priv->lock); + return count; +} + +static ssize_t rp5c01_nvram_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t pos, size_t size) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct rp5c01_priv *priv = dev_get_drvdata(dev); + ssize_t count; + + spin_lock_irq(&priv->lock); + + for (count = 0; size > 0 && pos < RP5C01_MODE; count++, size--) { + u8 data = *buf++; + + rp5c01_write(priv, + RP5C01_MODE_TIMER_EN | RP5C01_MODE_RAM_BLOCK10, + RP5C01_MODE); + rp5c01_write(priv, data >> 4, pos); + rp5c01_write(priv, + RP5C01_MODE_TIMER_EN | RP5C01_MODE_RAM_BLOCK11, + RP5C01_MODE); + rp5c01_write(priv, data & 0xf, pos++); + rp5c01_write(priv, RP5C01_MODE_TIMER_EN | RP5C01_MODE_MODE01, + RP5C01_MODE); + } + + spin_unlock_irq(&priv->lock); + return count; +} + static int __init rp5c01_rtc_probe(struct platform_device *dev) { struct resource *res; @@ -168,6 +240,15 @@ static int __init rp5c01_rtc_probe(struct platform_device *dev) goto out_free_priv; } + sysfs_bin_attr_init(&priv->nvram_attr); + priv->nvram_attr.attr.name = "nvram"; + priv->nvram_attr.attr.mode = S_IRUGO | S_IWUSR; + priv->nvram_attr.read = rp5c01_nvram_read; + priv->nvram_attr.write = rp5c01_nvram_write; + priv->nvram_attr.size = RP5C01_MODE; + + spin_lock_init(&priv->lock); + rtc = rtc_device_register("rtc-rp5c01", &dev->dev, &rp5c01_rtc_ops, THIS_MODULE); if (IS_ERR(rtc)) { @@ -177,8 +258,15 @@ static int __init rp5c01_rtc_probe(struct platform_device *dev) priv->rtc = rtc; platform_set_drvdata(dev, priv); + + error = sysfs_create_bin_file(&dev->dev.kobj, &priv->nvram_attr); + if (error) + goto out_unregister; + return 0; +out_unregister: + rtc_device_unregister(rtc); out_unmap: iounmap(priv->regs); out_free_priv: @@ -190,6 +278,7 @@ static int __exit rp5c01_rtc_remove(struct platform_device *dev) { struct rp5c01_priv *priv = platform_get_drvdata(dev); + sysfs_remove_bin_file(&dev->dev.kobj, &priv->nvram_attr); rtc_device_unregister(priv->rtc); iounmap(priv->regs); kfree(priv); diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index 70b68d35f96..a0d3ec89d41 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -1,5 +1,8 @@ /* drivers/rtc/rtc-s3c.c * + * Copyright (c) 2010 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * * Copyright (c) 2004,2006 Simtec Electronics * Ben Dooks, <ben@simtec.co.uk> * http://armlinux.simtec.co.uk/ @@ -39,6 +42,7 @@ enum s3c_cpu_type { static struct resource *s3c_rtc_mem; +static struct clk *rtc_clk; static void __iomem *s3c_rtc_base; static int s3c_rtc_alarmno = NO_IRQ; static int s3c_rtc_tickno = NO_IRQ; @@ -53,6 +57,10 @@ static irqreturn_t s3c_rtc_alarmirq(int irq, void *id) struct rtc_device *rdev = id; rtc_update_irq(rdev, 1, RTC_AF | RTC_IRQF); + + if (s3c_rtc_cpu_type == TYPE_S3C64XX) + writeb(S3C2410_INTP_ALM, s3c_rtc_base + S3C2410_INTP); + return IRQ_HANDLED; } @@ -61,6 +69,10 @@ static irqreturn_t s3c_rtc_tickirq(int irq, void *id) struct rtc_device *rdev = id; rtc_update_irq(rdev, 1, RTC_PF | RTC_IRQF); + + if (s3c_rtc_cpu_type == TYPE_S3C64XX) + writeb(S3C2410_INTP_TIC, s3c_rtc_base + S3C2410_INTP); + return IRQ_HANDLED; } @@ -94,7 +106,7 @@ static int s3c_rtc_setpie(struct device *dev, int enabled) if (enabled) tmp |= S3C64XX_RTCCON_TICEN; - writeb(tmp, s3c_rtc_base + S3C2410_RTCCON); + writew(tmp, s3c_rtc_base + S3C2410_RTCCON); } else { tmp = readb(s3c_rtc_base + S3C2410_TICNT); tmp &= ~S3C2410_TICNT_ENABLE; @@ -128,7 +140,7 @@ static int s3c_rtc_setfreq(struct device *dev, int freq) tmp |= (rtc_dev->max_user_freq / freq)-1; - writeb(tmp, s3c_rtc_base + S3C2410_TICNT); + writel(tmp, s3c_rtc_base + S3C2410_TICNT); spin_unlock_irq(&s3c_rtc_pie_lock); return 0; @@ -431,6 +443,10 @@ static int __devexit s3c_rtc_remove(struct platform_device *dev) s3c_rtc_setpie(&dev->dev, 0); s3c_rtc_setaie(0); + clk_disable(rtc_clk); + clk_put(rtc_clk); + rtc_clk = NULL; + iounmap(s3c_rtc_base); release_resource(s3c_rtc_mem); kfree(s3c_rtc_mem); @@ -442,6 +458,7 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev) { struct rtc_device *rtc; struct resource *res; + unsigned int tmp, i; int ret; pr_debug("%s: probe=%p\n", __func__, pdev); @@ -488,6 +505,16 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev) goto err_nomap; } + rtc_clk = clk_get(&pdev->dev, "rtc"); + if (IS_ERR(rtc_clk)) { + dev_err(&pdev->dev, "failed to find rtc clock source\n"); + ret = PTR_ERR(rtc_clk); + rtc_clk = NULL; + goto err_clk; + } + + clk_enable(rtc_clk); + /* check to see if everything is setup correctly */ s3c_rtc_enable(pdev, 1); @@ -510,6 +537,15 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev) s3c_rtc_cpu_type = platform_get_device_id(pdev)->driver_data; + /* Check RTC Time */ + + for (i = S3C2410_RTCSEC; i <= S3C2410_RTCYEAR; i += 0x4) { + tmp = readb(s3c_rtc_base + i); + + if ((tmp & 0xf) > 0x9 || ((tmp >> 4) & 0xf) > 0x9) + writeb(0, s3c_rtc_base + i); + } + if (s3c_rtc_cpu_type == TYPE_S3C64XX) rtc->max_user_freq = 32768; else @@ -523,6 +559,10 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev) err_nortc: s3c_rtc_enable(pdev, 0); + clk_disable(rtc_clk); + clk_put(rtc_clk); + + err_clk: iounmap(s3c_rtc_base); err_nomap: diff --git a/drivers/sbus/char/bbc_envctrl.c b/drivers/sbus/char/bbc_envctrl.c index 103fdf6b0b8..160e7510aca 100644 --- a/drivers/sbus/char/bbc_envctrl.c +++ b/drivers/sbus/char/bbc_envctrl.c @@ -443,7 +443,7 @@ static int kenvctrld(void *__unused) return 0; } -static void attach_one_temp(struct bbc_i2c_bus *bp, struct of_device *op, +static void attach_one_temp(struct bbc_i2c_bus *bp, struct platform_device *op, int temp_idx) { struct bbc_cpu_temperature *tp; @@ -488,7 +488,7 @@ static void attach_one_temp(struct bbc_i2c_bus *bp, struct of_device *op, tp->fan_todo[FAN_CPU] = FAN_SAME; } -static void attach_one_fan(struct bbc_i2c_bus *bp, struct of_device *op, +static void attach_one_fan(struct bbc_i2c_bus *bp, struct platform_device *op, int fan_idx) { struct bbc_fan_control *fp; @@ -559,7 +559,7 @@ static void destroy_all_fans(struct bbc_i2c_bus *bp) int bbc_envctrl_init(struct bbc_i2c_bus *bp) { - struct of_device *op; + struct platform_device *op; int temp_index = 0; int fan_index = 0; int devidx = 0; diff --git a/drivers/sbus/char/bbc_i2c.c b/drivers/sbus/char/bbc_i2c.c index 3e89c313e98..614a5e114a1 100644 --- a/drivers/sbus/char/bbc_i2c.c +++ b/drivers/sbus/char/bbc_i2c.c @@ -51,7 +51,7 @@ * The second controller also connects to the smartcard reader, if present. */ -static void set_device_claimage(struct bbc_i2c_bus *bp, struct of_device *op, int val) +static void set_device_claimage(struct bbc_i2c_bus *bp, struct platform_device *op, int val) { int i; @@ -66,9 +66,9 @@ static void set_device_claimage(struct bbc_i2c_bus *bp, struct of_device *op, in #define claim_device(BP,ECHILD) set_device_claimage(BP,ECHILD,1) #define release_device(BP,ECHILD) set_device_claimage(BP,ECHILD,0) -struct of_device *bbc_i2c_getdev(struct bbc_i2c_bus *bp, int index) +struct platform_device *bbc_i2c_getdev(struct bbc_i2c_bus *bp, int index) { - struct of_device *op = NULL; + struct platform_device *op = NULL; int curidx = 0, i; for (i = 0; i < NUM_CHILDREN; i++) { @@ -86,7 +86,7 @@ out: return NULL; } -struct bbc_i2c_client *bbc_i2c_attach(struct bbc_i2c_bus *bp, struct of_device *op) +struct bbc_i2c_client *bbc_i2c_attach(struct bbc_i2c_bus *bp, struct platform_device *op) { struct bbc_i2c_client *client; const u32 *reg; @@ -114,7 +114,7 @@ struct bbc_i2c_client *bbc_i2c_attach(struct bbc_i2c_bus *bp, struct of_device * void bbc_i2c_detach(struct bbc_i2c_client *client) { struct bbc_i2c_bus *bp = client->bp; - struct of_device *op = client->op; + struct platform_device *op = client->op; release_device(bp, op); kfree(client); @@ -297,7 +297,7 @@ static void __init reset_one_i2c(struct bbc_i2c_bus *bp) writeb(I2C_PCF_IDLE, bp->i2c_control_regs + 0x0); } -static struct bbc_i2c_bus * __init attach_one_i2c(struct of_device *op, int index) +static struct bbc_i2c_bus * __init attach_one_i2c(struct platform_device *op, int index) { struct bbc_i2c_bus *bp; struct device_node *dp; @@ -330,7 +330,7 @@ static struct bbc_i2c_bus * __init attach_one_i2c(struct of_device *op, int inde for (dp = op->dev.of_node->child; dp && entry < 8; dp = dp->sibling, entry++) { - struct of_device *child_op; + struct platform_device *child_op; child_op = of_find_device_by_node(dp); bp->devs[entry].device = child_op; @@ -361,7 +361,7 @@ fail: extern int bbc_envctrl_init(struct bbc_i2c_bus *bp); extern void bbc_envctrl_cleanup(struct bbc_i2c_bus *bp); -static int __devinit bbc_i2c_probe(struct of_device *op, +static int __devinit bbc_i2c_probe(struct platform_device *op, const struct of_device_id *match) { struct bbc_i2c_bus *bp; @@ -386,7 +386,7 @@ static int __devinit bbc_i2c_probe(struct of_device *op, return err; } -static int __devexit bbc_i2c_remove(struct of_device *op) +static int __devexit bbc_i2c_remove(struct platform_device *op) { struct bbc_i2c_bus *bp = dev_get_drvdata(&op->dev); diff --git a/drivers/sbus/char/bbc_i2c.h b/drivers/sbus/char/bbc_i2c.h index 83c4811b7b5..4b4531066e7 100644 --- a/drivers/sbus/char/bbc_i2c.h +++ b/drivers/sbus/char/bbc_i2c.h @@ -7,7 +7,7 @@ struct bbc_i2c_client { struct bbc_i2c_bus *bp; - struct of_device *op; + struct platform_device *op; int bus; int address; }; @@ -64,16 +64,16 @@ struct bbc_i2c_bus { struct list_head temps; struct list_head fans; - struct of_device *op; + struct platform_device *op; struct { - struct of_device *device; + struct platform_device *device; int client_claimed; } devs[NUM_CHILDREN]; }; /* Probing and attachment. */ -extern struct of_device *bbc_i2c_getdev(struct bbc_i2c_bus *, int); -extern struct bbc_i2c_client *bbc_i2c_attach(struct bbc_i2c_bus *bp, struct of_device *); +extern struct platform_device *bbc_i2c_getdev(struct bbc_i2c_bus *, int); +extern struct bbc_i2c_client *bbc_i2c_attach(struct bbc_i2c_bus *bp, struct platform_device *); extern void bbc_i2c_detach(struct bbc_i2c_client *); /* Register read/write. NOTE: Blocking! */ diff --git a/drivers/sbus/char/display7seg.c b/drivers/sbus/char/display7seg.c index 47db97583ea..1690e53fb84 100644 --- a/drivers/sbus/char/display7seg.c +++ b/drivers/sbus/char/display7seg.c @@ -170,7 +170,7 @@ static struct miscdevice d7s_miscdev = { .fops = &d7s_fops }; -static int __devinit d7s_probe(struct of_device *op, +static int __devinit d7s_probe(struct platform_device *op, const struct of_device_id *match) { struct device_node *opts; @@ -236,7 +236,7 @@ out_free: goto out; } -static int __devexit d7s_remove(struct of_device *op) +static int __devexit d7s_remove(struct platform_device *op) { struct d7s *p = dev_get_drvdata(&op->dev); u8 regs = readb(p->regs); diff --git a/drivers/sbus/char/envctrl.c b/drivers/sbus/char/envctrl.c index 3c27f45e2b6..078e5f4520e 100644 --- a/drivers/sbus/char/envctrl.c +++ b/drivers/sbus/char/envctrl.c @@ -1027,7 +1027,7 @@ static int kenvctrld(void *__unused) return 0; } -static int __devinit envctrl_probe(struct of_device *op, +static int __devinit envctrl_probe(struct platform_device *op, const struct of_device_id *match) { struct device_node *dp; @@ -1104,7 +1104,7 @@ out_iounmap: return err; } -static int __devexit envctrl_remove(struct of_device *op) +static int __devexit envctrl_remove(struct platform_device *op) { int index; diff --git a/drivers/sbus/char/flash.c b/drivers/sbus/char/flash.c index 8bb31c584b6..2b4b4b613c4 100644 --- a/drivers/sbus/char/flash.c +++ b/drivers/sbus/char/flash.c @@ -160,7 +160,7 @@ static const struct file_operations flash_fops = { static struct miscdevice flash_dev = { FLASH_MINOR, "flash", &flash_fops }; -static int __devinit flash_probe(struct of_device *op, +static int __devinit flash_probe(struct platform_device *op, const struct of_device_id *match) { struct device_node *dp = op->dev.of_node; @@ -192,7 +192,7 @@ static int __devinit flash_probe(struct of_device *op, return misc_register(&flash_dev); } -static int __devexit flash_remove(struct of_device *op) +static int __devexit flash_remove(struct platform_device *op) { misc_deregister(&flash_dev); diff --git a/drivers/sbus/char/uctrl.c b/drivers/sbus/char/uctrl.c index 41eb6725ff5..1b345be5cc0 100644 --- a/drivers/sbus/char/uctrl.c +++ b/drivers/sbus/char/uctrl.c @@ -348,7 +348,7 @@ static void uctrl_get_external_status(struct uctrl_driver *driver) } -static int __devinit uctrl_probe(struct of_device *op, +static int __devinit uctrl_probe(struct platform_device *op, const struct of_device_id *match) { struct uctrl_driver *p; @@ -404,7 +404,7 @@ out_free: goto out; } -static int __devexit uctrl_remove(struct of_device *op) +static int __devexit uctrl_remove(struct platform_device *op) { struct uctrl_driver *p = dev_get_drvdata(&op->dev); diff --git a/drivers/scsi/53c700.c b/drivers/scsi/53c700.c index 80dc3ac12cd..89fc1c8af86 100644 --- a/drivers/scsi/53c700.c +++ b/drivers/scsi/53c700.c @@ -309,9 +309,6 @@ NCR_700_detect(struct scsi_host_template *tpnt, hostdata->msgin = memory + MSGIN_OFFSET; hostdata->msgout = memory + MSGOUT_OFFSET; hostdata->status = memory + STATUS_OFFSET; - /* all of these offsets are L1_CACHE_BYTES separated. It is fatal - * if this isn't sufficient separation to avoid dma flushing issues */ - BUG_ON(!dma_is_consistent(hostdata->dev, pScript) && L1_CACHE_BYTES < dma_get_cache_alignment()); hostdata->slots = (struct NCR_700_command_slot *)(memory + SLOTS_OFFSET); hostdata->dev = dev; diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 158284f0573..6466231f338 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -1853,7 +1853,7 @@ config ZFCP_DIF config SCSI_PMCRAID tristate "PMC SIERRA Linux MaxRAID adapter support" - depends on PCI && SCSI + depends on PCI && SCSI && NET ---help--- This driver supports the PMC SIERRA MaxRAID adapters. diff --git a/drivers/scsi/NCR5380.c b/drivers/scsi/NCR5380.c index f92da9fd5f2..5d2f148889a 100644 --- a/drivers/scsi/NCR5380.c +++ b/drivers/scsi/NCR5380.c @@ -1857,7 +1857,9 @@ static int NCR5380_transfer_dma(struct Scsi_Host *instance, unsigned char *phase #endif /* KLL May need eop and parity in 53c400 */ if (hostdata->flags & FLAG_NCR53C400) - NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE | MR_ENABLE_PAR_CHECK | MR_ENABLE_PAR_INTR | MR_ENABLE_EOP_INTR | MR_DMA_MODE | MR_MONITOR_BSY); + NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE | + MR_ENABLE_PAR_CHECK | MR_ENABLE_PAR_INTR | + MR_ENABLE_EOP_INTR | MR_MONITOR_BSY); else NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE); #endif /* def REAL_DMA */ diff --git a/drivers/scsi/aacraid/rx.c b/drivers/scsi/aacraid/rx.c index 04057ab72a8..84d77fd86e5 100644 --- a/drivers/scsi/aacraid/rx.c +++ b/drivers/scsi/aacraid/rx.c @@ -352,9 +352,8 @@ static int aac_rx_check_health(struct aac_dev *dev) pci_free_consistent(dev->pdev, sizeof(struct POSTSTATUS), post, paddr); if (likely((buffer[0] == '0') && ((buffer[1] == 'x') || (buffer[1] == 'X')))) { - ret = (buffer[2] <= '9') ? (buffer[2] - '0') : (buffer[2] - 'A' + 10); - ret <<= 4; - ret += (buffer[3] <= '9') ? (buffer[3] - '0') : (buffer[3] - 'A' + 10); + ret = (hex_to_bin(buffer[2]) << 4) + + hex_to_bin(buffer[3]); } pci_free_consistent(dev->pdev, 512, buffer, baddr); return ret; diff --git a/drivers/scsi/aic94xx/aic94xx_init.c b/drivers/scsi/aic94xx/aic94xx_init.c index 24ac2315c5c..3b7e83d2dab 100644 --- a/drivers/scsi/aic94xx/aic94xx_init.c +++ b/drivers/scsi/aic94xx/aic94xx_init.c @@ -688,9 +688,9 @@ static int asd_register_sas_ha(struct asd_ha_struct *asd_ha) { int i; struct asd_sas_phy **sas_phys = - kmalloc(ASD_MAX_PHYS * sizeof(struct asd_sas_phy), GFP_KERNEL); + kcalloc(ASD_MAX_PHYS, sizeof(*sas_phys), GFP_KERNEL); struct asd_sas_port **sas_ports = - kmalloc(ASD_MAX_PHYS * sizeof(struct asd_sas_port), GFP_KERNEL); + kcalloc(ASD_MAX_PHYS, sizeof(*sas_ports), GFP_KERNEL); if (!sas_phys || !sas_ports) { kfree(sas_phys); diff --git a/drivers/scsi/bfa/bfa_fcport.c b/drivers/scsi/bfa/bfa_fcport.c index f0933d8d1ed..76867b5577f 100644 --- a/drivers/scsi/bfa/bfa_fcport.c +++ b/drivers/scsi/bfa/bfa_fcport.c @@ -1310,7 +1310,7 @@ bfa_fcport_isr(struct bfa_s *bfa, struct bfi_msg_s *msg) break; case BFI_FCPORT_I2H_DISABLE_RSP: - if (fcport->msgtag == i2hmsg.penable_rsp->msgtag) + if (fcport->msgtag == i2hmsg.pdisable_rsp->msgtag) bfa_sm_send_event(fcport, BFA_FCPORT_SM_FWRSP); break; diff --git a/drivers/scsi/bfa/bfad_im.c b/drivers/scsi/bfa/bfad_im.c index 678120b7046..6ef87f6fcdb 100644 --- a/drivers/scsi/bfa/bfad_im.c +++ b/drivers/scsi/bfa/bfad_im.c @@ -291,7 +291,7 @@ bfad_im_reset_lun_handler(struct scsi_cmnd *cmnd) struct bfa_tskim_s *tskim; struct bfad_itnim_s *itnim; struct bfa_itnim_s *bfa_itnim; - DECLARE_WAIT_QUEUE_HEAD(wq); + DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq); int rc = SUCCESS; unsigned long flags; enum bfi_tskim_status task_status; @@ -353,7 +353,7 @@ bfad_im_reset_bus_handler(struct scsi_cmnd *cmnd) struct bfad_itnim_s *itnim; unsigned long flags; u32 i, rc, err_cnt = 0; - DECLARE_WAIT_QUEUE_HEAD(wq); + DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq); enum bfi_tskim_status task_status; spin_lock_irqsave(&bfad->bfad_lock, flags); diff --git a/drivers/scsi/bfa/include/protocol/fcp.h b/drivers/scsi/bfa/include/protocol/fcp.h index 9ade68ad285..74ea63ce84b 100644 --- a/drivers/scsi/bfa/include/protocol/fcp.h +++ b/drivers/scsi/bfa/include/protocol/fcp.h @@ -18,6 +18,7 @@ #ifndef __FCPPROTO_H__ #define __FCPPROTO_H__ +#include <linux/bitops.h> #include <protocol/scsi.h> #pragma pack(1) @@ -102,9 +103,6 @@ enum { /* * Task management flags field - only one bit shall be set */ -#ifndef BIT -#define BIT(_x) (1 << (_x)) -#endif enum fcp_tm_cmnd{ FCP_TM_ABORT_TASK_SET = BIT(1), FCP_TM_CLEAR_TASK_SET = BIT(2), diff --git a/drivers/scsi/bnx2i/Kconfig b/drivers/scsi/bnx2i/Kconfig index 1e9f7141102..45a6154ce97 100644 --- a/drivers/scsi/bnx2i/Kconfig +++ b/drivers/scsi/bnx2i/Kconfig @@ -1,10 +1,11 @@ config SCSI_BNX2_ISCSI tristate "Broadcom NetXtreme II iSCSI support" + depends on NET + depends on PCI select SCSI_ISCSI_ATTRS select NETDEVICES select NETDEV_1000 select CNIC - depends on PCI ---help--- This driver supports iSCSI offload for the Broadcom NetXtreme II devices. diff --git a/drivers/scsi/ch.c b/drivers/scsi/ch.c index 4799d439120..d6532187f61 100644 --- a/drivers/scsi/ch.c +++ b/drivers/scsi/ch.c @@ -84,10 +84,16 @@ static const char * vendor_labels[CH_TYPES-4] = { }; // module_param_string_array(vendor_labels, NULL, 0444); -#define dprintk(fmt, arg...) if (debug) \ - printk(KERN_DEBUG "%s: " fmt, ch->name , ## arg) -#define vprintk(fmt, arg...) if (verbose) \ - printk(KERN_INFO "%s: " fmt, ch->name , ## arg) +#define DPRINTK(fmt, arg...) \ +do { \ + if (debug) \ + printk(KERN_DEBUG "%s: " fmt, ch->name, ##arg); \ +} while (0) +#define VPRINTK(level, fmt, arg...) \ +do { \ + if (verbose) \ + printk(level "%s: " fmt, ch->name, ##arg); \ +} while (0) /* ------------------------------------------------------------------- */ @@ -186,7 +192,7 @@ ch_do_scsi(scsi_changer *ch, unsigned char *cmd, retry: errno = 0; if (debug) { - dprintk("command: "); + DPRINTK("command: "); __scsi_print_command(cmd); } @@ -194,7 +200,7 @@ ch_do_scsi(scsi_changer *ch, unsigned char *cmd, buflength, &sshdr, timeout * HZ, MAX_RETRIES, NULL); - dprintk("result: 0x%x\n",result); + DPRINTK("result: 0x%x\n",result); if (driver_byte(result) & DRIVER_SENSE) { if (debug) scsi_print_sense_hdr(ch->name, &sshdr); @@ -250,7 +256,7 @@ ch_read_element_status(scsi_changer *ch, u_int elem, char *data) cmd[9] = 255; if (0 == (result = ch_do_scsi(ch, cmd, buffer, 256, DMA_FROM_DEVICE))) { if (((buffer[16] << 8) | buffer[17]) != elem) { - dprintk("asked for element 0x%02x, got 0x%02x\n", + DPRINTK("asked for element 0x%02x, got 0x%02x\n", elem,(buffer[16] << 8) | buffer[17]); kfree(buffer); return -EIO; @@ -259,10 +265,10 @@ ch_read_element_status(scsi_changer *ch, u_int elem, char *data) } else { if (ch->voltags) { ch->voltags = 0; - vprintk("device has no volume tag support\n"); + VPRINTK(KERN_INFO, "device has no volume tag support\n"); goto retry; } - dprintk("READ ELEMENT STATUS for element 0x%x failed\n",elem); + DPRINTK("READ ELEMENT STATUS for element 0x%x failed\n",elem); } kfree(buffer); return result; @@ -274,12 +280,12 @@ ch_init_elem(scsi_changer *ch) int err; u_char cmd[6]; - vprintk("INITIALIZE ELEMENT STATUS, may take some time ...\n"); + VPRINTK(KERN_INFO, "INITIALIZE ELEMENT STATUS, may take some time ...\n"); memset(cmd,0,sizeof(cmd)); cmd[0] = INITIALIZE_ELEMENT_STATUS; cmd[1] = ch->device->lun << 5; err = ch_do_scsi(ch, cmd, NULL, 0, DMA_NONE); - vprintk("... finished\n"); + VPRINTK(KERN_INFO, "... finished\n"); return err; } @@ -322,20 +328,20 @@ ch_readconfig(scsi_changer *ch) (buffer[buffer[3]+18] << 8) | buffer[buffer[3]+19]; ch->counts[CHET_DT] = (buffer[buffer[3]+20] << 8) | buffer[buffer[3]+21]; - vprintk("type #1 (mt): 0x%x+%d [medium transport]\n", + VPRINTK(KERN_INFO, "type #1 (mt): 0x%x+%d [medium transport]\n", ch->firsts[CHET_MT], ch->counts[CHET_MT]); - vprintk("type #2 (st): 0x%x+%d [storage]\n", + VPRINTK(KERN_INFO, "type #2 (st): 0x%x+%d [storage]\n", ch->firsts[CHET_ST], ch->counts[CHET_ST]); - vprintk("type #3 (ie): 0x%x+%d [import/export]\n", + VPRINTK(KERN_INFO, "type #3 (ie): 0x%x+%d [import/export]\n", ch->firsts[CHET_IE], ch->counts[CHET_IE]); - vprintk("type #4 (dt): 0x%x+%d [data transfer]\n", + VPRINTK(KERN_INFO, "type #4 (dt): 0x%x+%d [data transfer]\n", ch->firsts[CHET_DT], ch->counts[CHET_DT]); } else { - vprintk("reading element address assigment page failed!\n"); + VPRINTK(KERN_INFO, "reading element address assigment page failed!\n"); } /* vendor specific element types */ @@ -346,13 +352,13 @@ ch_readconfig(scsi_changer *ch) continue; ch->firsts[CHET_V1+i] = vendor_firsts[i]; ch->counts[CHET_V1+i] = vendor_counts[i]; - vprintk("type #%d (v%d): 0x%x+%d [%s, vendor specific]\n", + VPRINTK(KERN_INFO, "type #%d (v%d): 0x%x+%d [%s, vendor specific]\n", i+5,i+1,vendor_firsts[i],vendor_counts[i], vendor_labels[i]); } /* look up the devices of the data transfer elements */ - ch->dt = kmalloc(ch->counts[CHET_DT]*sizeof(struct scsi_device), + ch->dt = kcalloc(ch->counts[CHET_DT], sizeof(*ch->dt), GFP_KERNEL); if (!ch->dt) { @@ -366,21 +372,19 @@ ch_readconfig(scsi_changer *ch) if (elem < CH_DT_MAX && -1 != dt_id[elem]) { id = dt_id[elem]; lun = dt_lun[elem]; - vprintk("dt 0x%x: [insmod option] ", + VPRINTK(KERN_INFO, "dt 0x%x: [insmod option] ", elem+ch->firsts[CHET_DT]); } else if (0 != ch_read_element_status (ch,elem+ch->firsts[CHET_DT],data)) { - vprintk("dt 0x%x: READ ELEMENT STATUS failed\n", + VPRINTK(KERN_INFO, "dt 0x%x: READ ELEMENT STATUS failed\n", elem+ch->firsts[CHET_DT]); } else { - vprintk("dt 0x%x: ",elem+ch->firsts[CHET_DT]); + VPRINTK(KERN_INFO, "dt 0x%x: ",elem+ch->firsts[CHET_DT]); if (data[6] & 0x80) { - if (verbose) - printk("not this SCSI bus\n"); + VPRINTK(KERN_CONT, "not this SCSI bus\n"); ch->dt[elem] = NULL; } else if (0 == (data[6] & 0x30)) { - if (verbose) - printk("ID/LUN unknown\n"); + VPRINTK(KERN_CONT, "ID/LUN unknown\n"); ch->dt[elem] = NULL; } else { id = ch->device->id; @@ -390,22 +394,19 @@ ch_readconfig(scsi_changer *ch) } } if (-1 != id) { - if (verbose) - printk("ID %i, LUN %i, ",id,lun); + VPRINTK(KERN_CONT, "ID %i, LUN %i, ",id,lun); ch->dt[elem] = scsi_device_lookup(ch->device->host, ch->device->channel, id,lun); if (!ch->dt[elem]) { /* should not happen */ - if (verbose) - printk("Huh? device not found!\n"); + VPRINTK(KERN_CONT, "Huh? device not found!\n"); } else { - if (verbose) - printk("name: %8.8s %16.16s %4.4s\n", - ch->dt[elem]->vendor, - ch->dt[elem]->model, - ch->dt[elem]->rev); + VPRINTK(KERN_CONT, "name: %8.8s %16.16s %4.4s\n", + ch->dt[elem]->vendor, + ch->dt[elem]->model, + ch->dt[elem]->rev); } } } @@ -422,7 +423,7 @@ ch_position(scsi_changer *ch, u_int trans, u_int elem, int rotate) { u_char cmd[10]; - dprintk("position: 0x%x\n",elem); + DPRINTK("position: 0x%x\n",elem); if (0 == trans) trans = ch->firsts[CHET_MT]; memset(cmd,0,sizeof(cmd)); @@ -441,7 +442,7 @@ ch_move(scsi_changer *ch, u_int trans, u_int src, u_int dest, int rotate) { u_char cmd[12]; - dprintk("move: 0x%x => 0x%x\n",src,dest); + DPRINTK("move: 0x%x => 0x%x\n",src,dest); if (0 == trans) trans = ch->firsts[CHET_MT]; memset(cmd,0,sizeof(cmd)); @@ -463,7 +464,7 @@ ch_exchange(scsi_changer *ch, u_int trans, u_int src, { u_char cmd[12]; - dprintk("exchange: 0x%x => 0x%x => 0x%x\n", + DPRINTK("exchange: 0x%x => 0x%x => 0x%x\n", src,dest1,dest2); if (0 == trans) trans = ch->firsts[CHET_MT]; @@ -511,7 +512,7 @@ ch_set_voltag(scsi_changer *ch, u_int elem, if (!buffer) return -ENOMEM; - dprintk("%s %s voltag: 0x%x => \"%s\"\n", + DPRINTK("%s %s voltag: 0x%x => \"%s\"\n", clear ? "clear" : "set", alternate ? "alternate" : "primary", elem, tag); @@ -550,7 +551,7 @@ static int ch_gstatus(scsi_changer *ch, int type, unsigned char __user *dest) } put_user(data[2], dest+i); if (data[2] & CESTATUS_EXCEPT) - vprintk("element 0x%x: asc=0x%x, ascq=0x%x\n", + VPRINTK(KERN_INFO, "element 0x%x: asc=0x%x, ascq=0x%x\n", ch->firsts[type]+i, (int)data[4],(int)data[5]); retval = ch_read_element_status @@ -660,7 +661,7 @@ static long ch_ioctl(struct file *file, return -EFAULT; if (0 != ch_checkrange(ch, pos.cp_type, pos.cp_unit)) { - dprintk("CHIOPOSITION: invalid parameter\n"); + DPRINTK("CHIOPOSITION: invalid parameter\n"); return -EBADSLT; } mutex_lock(&ch->lock); @@ -680,7 +681,7 @@ static long ch_ioctl(struct file *file, if (0 != ch_checkrange(ch, mv.cm_fromtype, mv.cm_fromunit) || 0 != ch_checkrange(ch, mv.cm_totype, mv.cm_tounit )) { - dprintk("CHIOMOVE: invalid parameter\n"); + DPRINTK("CHIOMOVE: invalid parameter\n"); return -EBADSLT; } @@ -703,7 +704,7 @@ static long ch_ioctl(struct file *file, if (0 != ch_checkrange(ch, mv.ce_srctype, mv.ce_srcunit ) || 0 != ch_checkrange(ch, mv.ce_fdsttype, mv.ce_fdstunit) || 0 != ch_checkrange(ch, mv.ce_sdsttype, mv.ce_sdstunit)) { - dprintk("CHIOEXCHANGE: invalid parameter\n"); + DPRINTK("CHIOEXCHANGE: invalid parameter\n"); return -EBADSLT; } @@ -796,7 +797,7 @@ static long ch_ioctl(struct file *file, } } else if (ch->voltags) { ch->voltags = 0; - vprintk("device has no volume tag support\n"); + VPRINTK(KERN_INFO, "device has no volume tag support\n"); goto voltag_retry; } kfree(buffer); @@ -824,7 +825,7 @@ static long ch_ioctl(struct file *file, return -EFAULT; if (0 != ch_checkrange(ch, csv.csv_type, csv.csv_unit)) { - dprintk("CHIOSVOLTAG: invalid parameter\n"); + DPRINTK("CHIOSVOLTAG: invalid parameter\n"); return -EBADSLT; } elem = ch->firsts[csv.csv_type] + csv.csv_unit; diff --git a/drivers/scsi/dc395x.c b/drivers/scsi/dc395x.c index bd977be7544..54f50b07dac 100644 --- a/drivers/scsi/dc395x.c +++ b/drivers/scsi/dc395x.c @@ -1597,7 +1597,7 @@ static u8 start_scsi(struct AdapterCtlBlk* acb, struct DeviceCtlBlk* dcb, u32 tag_mask = 1; u8 tag_number = 0; while (tag_mask & dcb->tag_mask - && tag_number <= dcb->max_command) { + && tag_number < dcb->max_command) { tag_mask = tag_mask << 1; tag_number++; } diff --git a/drivers/scsi/fnic/fnic.h b/drivers/scsi/fnic/fnic.h index 19338e0ba2c..cbb20b13b22 100644 --- a/drivers/scsi/fnic/fnic.h +++ b/drivers/scsi/fnic/fnic.h @@ -21,6 +21,7 @@ #include <linux/interrupt.h> #include <linux/netdevice.h> #include <linux/workqueue.h> +#include <linux/bitops.h> #include <scsi/libfc.h> #include <scsi/libfcoe.h> #include "fnic_io.h" @@ -49,7 +50,6 @@ /* * Tag bits used for special requests. */ -#define BIT(nr) (1UL << (nr)) #define FNIC_TAG_ABORT BIT(30) /* tag bit indicating abort */ #define FNIC_TAG_DEV_RST BIT(29) /* indicates device reset */ #define FNIC_TAG_MASK (BIT(24) - 1) /* mask for lookup */ diff --git a/drivers/scsi/g_NCR5380.c b/drivers/scsi/g_NCR5380.c index 75585a52c88..427a56d3117 100644 --- a/drivers/scsi/g_NCR5380.c +++ b/drivers/scsi/g_NCR5380.c @@ -285,9 +285,12 @@ static int __init do_DTC3181E_setup(char *str) int __init generic_NCR5380_detect(struct scsi_host_template * tpnt) { static int current_override = 0; - int count, i; + int count; unsigned int *ports; +#ifndef SCSI_G_NCR5380_MEM + int i; unsigned long region_size = 16; +#endif static unsigned int __initdata ncr_53c400a_ports[] = { 0x280, 0x290, 0x300, 0x310, 0x330, 0x340, 0x348, 0x350, 0 }; @@ -296,7 +299,7 @@ int __init generic_NCR5380_detect(struct scsi_host_template * tpnt) }; int flags = 0; struct Scsi_Host *instance; -#ifdef CONFIG_SCSI_G_NCR5380_MEM +#ifdef SCSI_G_NCR5380_MEM unsigned long base; void __iomem *iomem; #endif @@ -315,17 +318,15 @@ int __init generic_NCR5380_detect(struct scsi_host_template * tpnt) overrides[0].board = BOARD_NCR53C400A; else if (dtc_3181e != NCR_NOT_SET) overrides[0].board = BOARD_DTC3181E; - +#ifndef SCSI_G_NCR5380_MEM if (!current_override && isapnp_present()) { struct pnp_dev *dev = NULL; count = 0; while ((dev = pnp_find_dev(NULL, ISAPNP_VENDOR('D', 'T', 'C'), ISAPNP_FUNCTION(0x436e), dev))) { if (count >= NO_OVERRIDES) break; - if (pnp_device_attach(dev) < 0) { - printk(KERN_ERR "dtc436e probe: attach failed\n"); + if (pnp_device_attach(dev) < 0) continue; - } if (pnp_activate_dev(dev) < 0) { printk(KERN_ERR "dtc436e probe: activate failed\n"); pnp_device_detach(dev); @@ -349,7 +350,7 @@ int __init generic_NCR5380_detect(struct scsi_host_template * tpnt) count++; } } - +#endif tpnt->proc_name = "g_NCR5380"; for (count = 0; current_override < NO_OVERRIDES; ++current_override) { @@ -374,7 +375,7 @@ int __init generic_NCR5380_detect(struct scsi_host_template * tpnt) break; } -#ifndef CONFIG_SCSI_G_NCR5380_MEM +#ifndef SCSI_G_NCR5380_MEM if (ports) { /* wakeup sequence for the NCR53C400A and DTC3181E */ @@ -436,7 +437,7 @@ int __init generic_NCR5380_detect(struct scsi_host_template * tpnt) #endif instance = scsi_register(tpnt, sizeof(struct NCR5380_hostdata)); if (instance == NULL) { -#ifndef CONFIG_SCSI_G_NCR5380_MEM +#ifndef SCSI_G_NCR5380_MEM release_region(overrides[current_override].NCR5380_map_name, region_size); #else iounmap(iomem); @@ -446,10 +447,10 @@ int __init generic_NCR5380_detect(struct scsi_host_template * tpnt) } instance->NCR5380_instance_name = overrides[current_override].NCR5380_map_name; -#ifndef CONFIG_SCSI_G_NCR5380_MEM +#ifndef SCSI_G_NCR5380_MEM instance->n_io_port = region_size; #else - ((struct NCR5380_hostdata *)instance->hostdata).iomem = iomem; + ((struct NCR5380_hostdata *)instance->hostdata)->iomem = iomem; #endif NCR5380_init(instance, flags); @@ -517,10 +518,10 @@ int generic_NCR5380_release_resources(struct Scsi_Host *instance) free_irq(instance->irq, instance); NCR5380_exit(instance); -#ifndef CONFIG_SCSI_G_NCR5380_MEM +#ifndef SCSI_G_NCR5380_MEM release_region(instance->NCR5380_instance_name, instance->n_io_port); #else - iounmap(((struct NCR5380_hostdata *)instance->hostdata).iomem); + iounmap(((struct NCR5380_hostdata *)instance->hostdata)->iomem); release_mem_region(instance->NCR5380_instance_name, NCR5380_region_size); #endif @@ -590,14 +591,14 @@ static inline int NCR5380_pread(struct Scsi_Host *instance, unsigned char *dst, } while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_HOST_BUF_NOT_RDY); -#ifndef CONFIG_SCSI_G_NCR5380_MEM +#ifndef SCSI_G_NCR5380_MEM { int i; for (i = 0; i < 128; i++) dst[start + i] = NCR5380_read(C400_HOST_BUFFER); } #else - /* implies CONFIG_SCSI_G_NCR5380_MEM */ + /* implies SCSI_G_NCR5380_MEM */ memcpy_fromio(dst + start, iomem + NCR53C400_host_buffer, 128); #endif start += 128; @@ -610,14 +611,14 @@ static inline int NCR5380_pread(struct Scsi_Host *instance, unsigned char *dst, // FIXME - no timeout } -#ifndef CONFIG_SCSI_G_NCR5380_MEM +#ifndef SCSI_G_NCR5380_MEM { int i; for (i = 0; i < 128; i++) dst[start + i] = NCR5380_read(C400_HOST_BUFFER); } #else - /* implies CONFIG_SCSI_G_NCR5380_MEM */ + /* implies SCSI_G_NCR5380_MEM */ memcpy_fromio(dst + start, iomem + NCR53C400_host_buffer, 128); #endif start += 128; @@ -676,13 +677,13 @@ static inline int NCR5380_pwrite(struct Scsi_Host *instance, unsigned char *src, } while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_HOST_BUF_NOT_RDY) ; // FIXME - timeout -#ifndef CONFIG_SCSI_G_NCR5380_MEM +#ifndef SCSI_G_NCR5380_MEM { for (i = 0; i < 128; i++) NCR5380_write(C400_HOST_BUFFER, src[start + i]); } #else - /* implies CONFIG_SCSI_G_NCR5380_MEM */ + /* implies SCSI_G_NCR5380_MEM */ memcpy_toio(iomem + NCR53C400_host_buffer, src + start, 128); #endif start += 128; @@ -692,13 +693,13 @@ static inline int NCR5380_pwrite(struct Scsi_Host *instance, unsigned char *src, while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_HOST_BUF_NOT_RDY) ; // FIXME - no timeout -#ifndef CONFIG_SCSI_G_NCR5380_MEM +#ifndef SCSI_G_NCR5380_MEM { for (i = 0; i < 128; i++) NCR5380_write(C400_HOST_BUFFER, src[start + i]); } #else - /* implies CONFIG_SCSI_G_NCR5380_MEM */ + /* implies SCSI_G_NCR5380_MEM */ memcpy_toio(iomem + NCR53C400_host_buffer, src + start, 128); #endif start += 128; @@ -938,7 +939,7 @@ module_param(ncr_53c400a, int, 0); module_param(dtc_3181e, int, 0); MODULE_LICENSE("GPL"); - +#ifndef SCSI_G_NCR5380_MEM static struct isapnp_device_id id_table[] __devinitdata = { { ISAPNP_ANY_ID, ISAPNP_ANY_ID, @@ -948,7 +949,7 @@ static struct isapnp_device_id id_table[] __devinitdata = { }; MODULE_DEVICE_TABLE(isapnp, id_table); - +#endif __setup("ncr5380=", do_NCR5380_setup); __setup("ncr53c400=", do_NCR53C400_setup); diff --git a/drivers/scsi/g_NCR5380.h b/drivers/scsi/g_NCR5380.h index df0b3f69ef6..921764c9ab2 100644 --- a/drivers/scsi/g_NCR5380.h +++ b/drivers/scsi/g_NCR5380.h @@ -63,7 +63,7 @@ static const char* generic_NCR5380_info(struct Scsi_Host *); #define __STRVAL(x) #x #define STRVAL(x) __STRVAL(x) -#ifndef CONFIG_SCSI_G_NCR5380_MEM +#ifndef SCSI_G_NCR5380_MEM #define NCR5380_map_config port #define NCR5380_map_type int @@ -91,7 +91,7 @@ static const char* generic_NCR5380_info(struct Scsi_Host *); NCR5380_map_name = (NCR5380_map_type)((instance)->NCR5380_instance_name) #else -/* therefore CONFIG_SCSI_G_NCR5380_MEM */ +/* therefore SCSI_G_NCR5380_MEM */ #define NCR5380_map_config memory #define NCR5380_map_type unsigned long @@ -114,7 +114,7 @@ static const char* generic_NCR5380_info(struct Scsi_Host *); register void __iomem *iomem #define NCR5380_setup(instance) \ - iomem = (((struct NCR5380_hostdata *)(instance)->hostdata).iomem) + iomem = (((struct NCR5380_hostdata *)(instance)->hostdata)->iomem) #endif diff --git a/drivers/scsi/gdth.c b/drivers/scsi/gdth.c index f672d6213ee..b860d650a56 100644 --- a/drivers/scsi/gdth.c +++ b/drivers/scsi/gdth.c @@ -4914,7 +4914,7 @@ static int __init gdth_eisa_probe_one(u16 eisa_slot) error = scsi_add_host(shp, NULL); if (error) - goto out_free_coal_stat; + goto out_free_ccb_phys; list_add_tail(&ha->list, &gdth_instances); gdth_timer_init(); diff --git a/drivers/scsi/initio.c b/drivers/scsi/initio.c index a7714160fbc..108797761b9 100644 --- a/drivers/scsi/initio.c +++ b/drivers/scsi/initio.c @@ -2817,7 +2817,6 @@ static void i91uSCBPost(u8 * host_mem, u8 * cblk_mem) } cmnd->result = cblk->tastat | (cblk->hastat << 16); - WARN_ON(cmnd == NULL); i91u_unmap_scb(host->pci_dev, cmnd); cmnd->scsi_done(cmnd); /* Notify system DONE */ initio_release_scb(host, cblk); /* Release SCB for current channel */ diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 162704cf6a9..ad05b266e95 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -25,6 +25,7 @@ #include <linux/interrupt.h> #include <linux/aer.h> #include <linux/gfp.h> +#include <linux/kernel.h> #include <scsi/scsi.h> #include <scsi/scsi_device.h> @@ -1795,12 +1796,11 @@ lpfc_soft_wwpn_store(struct device *dev, struct device_attribute *attr, /* Validate and store the new name */ for (i=0, j=0; i < 16; i++) { - if ((*buf >= 'a') && (*buf <= 'f')) - j = ((j << 4) | ((*buf++ -'a') + 10)); - else if ((*buf >= 'A') && (*buf <= 'F')) - j = ((j << 4) | ((*buf++ -'A') + 10)); - else if ((*buf >= '0') && (*buf <= '9')) - j = ((j << 4) | (*buf++ -'0')); + int value; + + value = hex_to_bin(*buf++); + if (value >= 0) + j = (j << 4) | value; else return -EINVAL; if (i % 2) { @@ -1888,12 +1888,11 @@ lpfc_soft_wwnn_store(struct device *dev, struct device_attribute *attr, /* Validate and store the new name */ for (i=0, j=0; i < 16; i++) { - if ((*buf >= 'a') && (*buf <= 'f')) - j = ((j << 4) | ((*buf++ -'a') + 10)); - else if ((*buf >= 'A') && (*buf <= 'F')) - j = ((j << 4) | ((*buf++ -'A') + 10)); - else if ((*buf >= '0') && (*buf <= '9')) - j = ((j << 4) | (*buf++ -'0')); + int value; + + value = hex_to_bin(*buf++); + if (value >= 0) + j = (j << 4) | value; else return -EINVAL; if (i % 2) { diff --git a/drivers/scsi/osst.c b/drivers/scsi/osst.c index d64b7178fa0..278b352ae78 100644 --- a/drivers/scsi/osst.c +++ b/drivers/scsi/osst.c @@ -5868,7 +5868,8 @@ static int osst_probe(struct device *dev) } /* find a free minor number */ - for (i=0; os_scsi_tapes[i] && i<osst_max_dev; i++); + for (i = 0; i < osst_max_dev && os_scsi_tapes[i]; i++) + ; if(i >= osst_max_dev) panic ("Scsi_devices corrupt (osst)"); dev_num = i; diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index ff2172da7c1..8c80b49ac1c 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -115,8 +115,8 @@ int ql2xmaxqueues = 1; module_param(ql2xmaxqueues, int, S_IRUGO|S_IRUSR); MODULE_PARM_DESC(ql2xmaxqueues, "Enables MQ settings " - "Default is 1 for single queue. Set it to number \ - of queues in MQ mode."); + "Default is 1 for single queue. Set it to number " + "of queues in MQ mode."); int ql2xmultique_tag; module_param(ql2xmultique_tag, int, S_IRUGO|S_IRUSR); diff --git a/drivers/scsi/qlogicpti.c b/drivers/scsi/qlogicpti.c index 53d7ed0dc16..f8c561cf751 100644 --- a/drivers/scsi/qlogicpti.c +++ b/drivers/scsi/qlogicpti.c @@ -704,7 +704,7 @@ static void __devexit qpti_chain_del(struct qlogicpti *qpti) static int __devinit qpti_map_regs(struct qlogicpti *qpti) { - struct of_device *op = qpti->op; + struct platform_device *op = qpti->op; qpti->qregs = of_ioremap(&op->resource[0], 0, resource_size(&op->resource[0]), @@ -727,7 +727,7 @@ static int __devinit qpti_map_regs(struct qlogicpti *qpti) static int __devinit qpti_register_irq(struct qlogicpti *qpti) { - struct of_device *op = qpti->op; + struct platform_device *op = qpti->op; qpti->qhost->irq = qpti->irq = op->archdata.irqs[0]; @@ -752,7 +752,7 @@ fail: static void __devinit qpti_get_scsi_id(struct qlogicpti *qpti) { - struct of_device *op = qpti->op; + struct platform_device *op = qpti->op; struct device_node *dp; dp = op->dev.of_node; @@ -773,7 +773,7 @@ static void __devinit qpti_get_scsi_id(struct qlogicpti *qpti) static void qpti_get_bursts(struct qlogicpti *qpti) { - struct of_device *op = qpti->op; + struct platform_device *op = qpti->op; u8 bursts, bmask; bursts = of_getintprop_default(op->dev.of_node, "burst-sizes", 0xff); @@ -806,7 +806,7 @@ static void qpti_get_clock(struct qlogicpti *qpti) */ static int __devinit qpti_map_queues(struct qlogicpti *qpti) { - struct of_device *op = qpti->op; + struct platform_device *op = qpti->op; #define QSIZE(entries) (((entries) + 1) * QUEUE_ENTRY_LEN) qpti->res_cpu = dma_alloc_coherent(&op->dev, @@ -1290,7 +1290,7 @@ static struct scsi_host_template qpti_template = { .use_clustering = ENABLE_CLUSTERING, }; -static int __devinit qpti_sbus_probe(struct of_device *op, const struct of_device_id *match) +static int __devinit qpti_sbus_probe(struct platform_device *op, const struct of_device_id *match) { struct scsi_host_template *tpnt = match->data; struct device_node *dp = op->dev.of_node; @@ -1401,7 +1401,7 @@ fail_unlink: return -ENODEV; } -static int __devexit qpti_sbus_remove(struct of_device *op) +static int __devexit qpti_sbus_remove(struct platform_device *op) { struct qlogicpti *qpti = dev_get_drvdata(&op->dev); diff --git a/drivers/scsi/qlogicpti.h b/drivers/scsi/qlogicpti.h index e3c74d1ee2d..4377e87ee79 100644 --- a/drivers/scsi/qlogicpti.h +++ b/drivers/scsi/qlogicpti.h @@ -342,7 +342,7 @@ struct qlogicpti { u_int req_in_ptr; /* index of next request slot */ u_int res_out_ptr; /* index of next result slot */ long send_marker; /* must we send a marker? */ - struct of_device *op; + struct platform_device *op; unsigned long __pad; int cmd_count[MAX_TARGETS]; diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index b8de389636f..9ade720422c 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -1370,12 +1370,6 @@ static void scsi_kill_request(struct request *req, struct request_queue *q) blk_start_request(req); - if (unlikely(cmd == NULL)) { - printk(KERN_CRIT "impossible request in %s.\n", - __func__); - BUG(); - } - sdev = cmd->device; starget = scsi_target(sdev); shost = sdev->host; diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index edb6b362a8f..d7e470a0618 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -29,6 +29,7 @@ #include <linux/init.h> #include <linux/slab.h> #include <linux/delay.h> +#include <linux/kernel.h> #include <scsi/scsi_device.h> #include <scsi/scsi_host.h> #include <scsi/scsi_transport.h> @@ -1730,12 +1731,11 @@ fc_parse_wwn(const char *ns, u64 *nm) /* Validate and store the new name */ for (i=0, j=0; i < 16; i++) { - if ((*ns >= 'a') && (*ns <= 'f')) - j = ((j << 4) | ((*ns++ -'a') + 10)); - else if ((*ns >= 'A') && (*ns <= 'F')) - j = ((j << 4) | ((*ns++ -'A') + 10)); - else if ((*ns >= '0') && (*ns <= '9')) - j = ((j << 4) | (*ns++ -'0')); + int value; + + value = hex_to_bin(*ns++); + if (value >= 0) + j = (j << 4) | value; else return -EINVAL; if (i % 2) { diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 8e2e893db9e..2714becc2ea 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -119,8 +119,8 @@ static DEFINE_IDA(sd_index_ida); * object after last put) */ static DEFINE_MUTEX(sd_ref_mutex); -struct kmem_cache *sd_cdb_cache; -mempool_t *sd_cdb_pool; +static struct kmem_cache *sd_cdb_cache; +static mempool_t *sd_cdb_pool; static const char *sd_cache_types[] = { "write through", "none", "write back", @@ -147,7 +147,7 @@ sd_store_cache_type(struct device *dev, struct device_attribute *attr, return -EINVAL; for (i = 0; i < ARRAY_SIZE(sd_cache_types); i++) { - const int len = strlen(sd_cache_types[i]); + len = strlen(sd_cache_types[i]); if (strncmp(sd_cache_types[i], buf, len) == 0 && buf[len] == '\n') { ct = i; @@ -1423,7 +1423,7 @@ sd_spinup_disk(struct scsi_disk *sdkp) /* * Determine whether disk supports Data Integrity Field. */ -void sd_read_protection_type(struct scsi_disk *sdkp, unsigned char *buffer) +static void sd_read_protection_type(struct scsi_disk *sdkp, unsigned char *buffer) { struct scsi_device *sdp = sdkp->device; u8 type; @@ -1969,7 +1969,7 @@ defaults: * The ATO bit indicates whether the DIF application tag is available * for use by the operating system. */ -void sd_read_app_tag_own(struct scsi_disk *sdkp, unsigned char *buffer) +static void sd_read_app_tag_own(struct scsi_disk *sdkp, unsigned char *buffer) { int res, offset; struct scsi_device *sdp = sdkp->device; @@ -2315,7 +2315,7 @@ static int sd_probe(struct device *dev) struct scsi_device *sdp = to_scsi_device(dev); struct scsi_disk *sdkp; struct gendisk *gd; - u32 index; + int index; int error; error = -ENODEV; diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 2968c6b83dd..78d616315d8 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -1686,14 +1686,9 @@ static int sg_start_req(Sg_request *srp, unsigned char *cmd) int len, size = sizeof(struct sg_iovec) * iov_count; struct iovec *iov; - iov = kmalloc(size, GFP_ATOMIC); - if (!iov) - return -ENOMEM; - - if (copy_from_user(iov, hp->dxferp, size)) { - kfree(iov); - return -EFAULT; - } + iov = memdup_user(hp->dxferp, size); + if (IS_ERR(iov)) + return PTR_ERR(iov); len = iov_length(iov, iov_count); if (hp->dxfer_len < len) { diff --git a/drivers/scsi/sun_esp.c b/drivers/scsi/sun_esp.c index 89ba6fe02f8..193b37ba183 100644 --- a/drivers/scsi/sun_esp.c +++ b/drivers/scsi/sun_esp.c @@ -44,7 +44,7 @@ enum dvma_rev { }; static int __devinit esp_sbus_setup_dma(struct esp *esp, - struct of_device *dma_of) + struct platform_device *dma_of) { esp->dma = dma_of; @@ -81,7 +81,7 @@ static int __devinit esp_sbus_setup_dma(struct esp *esp, static int __devinit esp_sbus_map_regs(struct esp *esp, int hme) { - struct of_device *op = esp->dev; + struct platform_device *op = esp->dev; struct resource *res; /* On HME, two reg sets exist, first is DVMA, @@ -101,7 +101,7 @@ static int __devinit esp_sbus_map_regs(struct esp *esp, int hme) static int __devinit esp_sbus_map_command_block(struct esp *esp) { - struct of_device *op = esp->dev; + struct platform_device *op = esp->dev; esp->command_block = dma_alloc_coherent(&op->dev, 16, &esp->command_block_dma, @@ -114,15 +114,15 @@ static int __devinit esp_sbus_map_command_block(struct esp *esp) static int __devinit esp_sbus_register_irq(struct esp *esp) { struct Scsi_Host *host = esp->host; - struct of_device *op = esp->dev; + struct platform_device *op = esp->dev; host->irq = op->archdata.irqs[0]; return request_irq(host->irq, scsi_esp_intr, IRQF_SHARED, "ESP", esp); } -static void __devinit esp_get_scsi_id(struct esp *esp, struct of_device *espdma) +static void __devinit esp_get_scsi_id(struct esp *esp, struct platform_device *espdma) { - struct of_device *op = esp->dev; + struct platform_device *op = esp->dev; struct device_node *dp; dp = op->dev.of_node; @@ -144,7 +144,7 @@ done: static void __devinit esp_get_differential(struct esp *esp) { - struct of_device *op = esp->dev; + struct platform_device *op = esp->dev; struct device_node *dp; dp = op->dev.of_node; @@ -156,7 +156,7 @@ static void __devinit esp_get_differential(struct esp *esp) static void __devinit esp_get_clock_params(struct esp *esp) { - struct of_device *op = esp->dev; + struct platform_device *op = esp->dev; struct device_node *bus_dp, *dp; int fmhz; @@ -170,10 +170,10 @@ static void __devinit esp_get_clock_params(struct esp *esp) esp->cfreq = fmhz; } -static void __devinit esp_get_bursts(struct esp *esp, struct of_device *dma_of) +static void __devinit esp_get_bursts(struct esp *esp, struct platform_device *dma_of) { struct device_node *dma_dp = dma_of->dev.of_node; - struct of_device *op = esp->dev; + struct platform_device *op = esp->dev; struct device_node *dp; u8 bursts, val; @@ -195,7 +195,7 @@ static void __devinit esp_get_bursts(struct esp *esp, struct of_device *dma_of) esp->bursts = bursts; } -static void __devinit esp_sbus_get_props(struct esp *esp, struct of_device *espdma) +static void __devinit esp_sbus_get_props(struct esp *esp, struct platform_device *espdma) { esp_get_scsi_id(esp, espdma); esp_get_differential(esp); @@ -216,7 +216,7 @@ static u8 sbus_esp_read8(struct esp *esp, unsigned long reg) static dma_addr_t sbus_esp_map_single(struct esp *esp, void *buf, size_t sz, int dir) { - struct of_device *op = esp->dev; + struct platform_device *op = esp->dev; return dma_map_single(&op->dev, buf, sz, dir); } @@ -224,7 +224,7 @@ static dma_addr_t sbus_esp_map_single(struct esp *esp, void *buf, static int sbus_esp_map_sg(struct esp *esp, struct scatterlist *sg, int num_sg, int dir) { - struct of_device *op = esp->dev; + struct platform_device *op = esp->dev; return dma_map_sg(&op->dev, sg, num_sg, dir); } @@ -232,7 +232,7 @@ static int sbus_esp_map_sg(struct esp *esp, struct scatterlist *sg, static void sbus_esp_unmap_single(struct esp *esp, dma_addr_t addr, size_t sz, int dir) { - struct of_device *op = esp->dev; + struct platform_device *op = esp->dev; dma_unmap_single(&op->dev, addr, sz, dir); } @@ -240,7 +240,7 @@ static void sbus_esp_unmap_single(struct esp *esp, dma_addr_t addr, static void sbus_esp_unmap_sg(struct esp *esp, struct scatterlist *sg, int num_sg, int dir) { - struct of_device *op = esp->dev; + struct platform_device *op = esp->dev; dma_unmap_sg(&op->dev, sg, num_sg, dir); } @@ -256,7 +256,7 @@ static void sbus_esp_reset_dma(struct esp *esp) { int can_do_burst16, can_do_burst32, can_do_burst64; int can_do_sbus64, lim; - struct of_device *op; + struct platform_device *op; u32 val; can_do_burst16 = (esp->bursts & DMA_BURST16) != 0; @@ -487,8 +487,8 @@ static const struct esp_driver_ops sbus_esp_ops = { .dma_error = sbus_esp_dma_error, }; -static int __devinit esp_sbus_probe_one(struct of_device *op, - struct of_device *espdma, +static int __devinit esp_sbus_probe_one(struct platform_device *op, + struct platform_device *espdma, int hme) { struct scsi_host_template *tpnt = &scsi_esp_template; @@ -562,11 +562,11 @@ fail: return err; } -static int __devinit esp_sbus_probe(struct of_device *op, const struct of_device_id *match) +static int __devinit esp_sbus_probe(struct platform_device *op, const struct of_device_id *match) { struct device_node *dma_node = NULL; struct device_node *dp = op->dev.of_node; - struct of_device *dma_of = NULL; + struct platform_device *dma_of = NULL; int hme = 0; if (dp->parent && @@ -585,10 +585,10 @@ static int __devinit esp_sbus_probe(struct of_device *op, const struct of_device return esp_sbus_probe_one(op, dma_of, hme); } -static int __devexit esp_sbus_remove(struct of_device *op) +static int __devexit esp_sbus_remove(struct platform_device *op) { struct esp *esp = dev_get_drvdata(&op->dev); - struct of_device *dma_of = esp->dma; + struct platform_device *dma_of = esp->dma; unsigned int irq = esp->host->irq; bool is_hme; u32 val; diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index a22e60c06f4..12900f7083b 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -536,8 +536,8 @@ config SERIAL_S3C6400 config SERIAL_S5PV210 tristate "Samsung S5PV210 Serial port support" - depends on SERIAL_SAMSUNG && (CPU_S5PV210 || CPU_S5P6442) - select SERIAL_SAMSUNG_UARTS_4 if CPU_S5PV210 + depends on SERIAL_SAMSUNG && (CPU_S5PV210 || CPU_S5P6442 || CPU_S5PV310) + select SERIAL_SAMSUNG_UARTS_4 if (CPU_S5PV210 || CPU_S5PV310) default y help Serial port support for Samsung's S5P Family of SoC's diff --git a/drivers/serial/apbuart.c b/drivers/serial/apbuart.c index 0099b8692b6..cc01c650a14 100644 --- a/drivers/serial/apbuart.c +++ b/drivers/serial/apbuart.c @@ -551,7 +551,7 @@ static struct uart_driver grlib_apbuart_driver = { /* OF Platform Driver */ /* ======================================================================== */ -static int __devinit apbuart_probe(struct of_device *op, +static int __devinit apbuart_probe(struct platform_device *op, const struct of_device_id *match) { int i = -1; diff --git a/drivers/serial/cpm_uart/cpm_uart_core.c b/drivers/serial/cpm_uart/cpm_uart_core.c index 6016179db53..f2b8adcc6c9 100644 --- a/drivers/serial/cpm_uart/cpm_uart_core.c +++ b/drivers/serial/cpm_uart/cpm_uart_core.c @@ -1340,7 +1340,7 @@ static struct uart_driver cpm_reg = { static int probe_index; -static int __devinit cpm_uart_probe(struct of_device *ofdev, +static int __devinit cpm_uart_probe(struct platform_device *ofdev, const struct of_device_id *match) { int index = probe_index++; @@ -1364,7 +1364,7 @@ static int __devinit cpm_uart_probe(struct of_device *ofdev, return uart_add_one_port(&cpm_reg, &pinfo->port); } -static int __devexit cpm_uart_remove(struct of_device *ofdev) +static int __devexit cpm_uart_remove(struct platform_device *ofdev) { struct uart_cpm_port *pinfo = dev_get_drvdata(&ofdev->dev); return uart_remove_one_port(&cpm_reg, &pinfo->port); diff --git a/drivers/serial/mpc52xx_uart.c b/drivers/serial/mpc52xx_uart.c index 1a88b363005..8dedb266f14 100644 --- a/drivers/serial/mpc52xx_uart.c +++ b/drivers/serial/mpc52xx_uart.c @@ -1298,7 +1298,7 @@ static struct of_device_id mpc52xx_uart_of_match[] = { }; static int __devinit -mpc52xx_uart_of_probe(struct of_device *op, const struct of_device_id *match) +mpc52xx_uart_of_probe(struct platform_device *op, const struct of_device_id *match) { int idx = -1; unsigned int uartclk; @@ -1369,7 +1369,7 @@ mpc52xx_uart_of_probe(struct of_device *op, const struct of_device_id *match) } static int -mpc52xx_uart_of_remove(struct of_device *op) +mpc52xx_uart_of_remove(struct platform_device *op) { struct uart_port *port = dev_get_drvdata(&op->dev); dev_set_drvdata(&op->dev, NULL); @@ -1382,7 +1382,7 @@ mpc52xx_uart_of_remove(struct of_device *op) #ifdef CONFIG_PM static int -mpc52xx_uart_of_suspend(struct of_device *op, pm_message_t state) +mpc52xx_uart_of_suspend(struct platform_device *op, pm_message_t state) { struct uart_port *port = (struct uart_port *) dev_get_drvdata(&op->dev); @@ -1393,7 +1393,7 @@ mpc52xx_uart_of_suspend(struct of_device *op, pm_message_t state) } static int -mpc52xx_uart_of_resume(struct of_device *op) +mpc52xx_uart_of_resume(struct platform_device *op) { struct uart_port *port = (struct uart_port *) dev_get_drvdata(&op->dev); diff --git a/drivers/serial/nwpserial.c b/drivers/serial/nwpserial.c index e65b0d9202a..de173671e3d 100644 --- a/drivers/serial/nwpserial.c +++ b/drivers/serial/nwpserial.c @@ -344,7 +344,7 @@ int nwpserial_register_port(struct uart_port *port) mutex_lock(&nwpserial_mutex); - dn = to_of_device(port->dev)->dev.of_node; + dn = port->dev->of_node; if (dn == NULL) goto out; diff --git a/drivers/serial/of_serial.c b/drivers/serial/of_serial.c index a48d9080f55..659a695bdad 100644 --- a/drivers/serial/of_serial.c +++ b/drivers/serial/of_serial.c @@ -27,7 +27,7 @@ struct of_serial_info { /* * Fill a struct uart_port for a given device node */ -static int __devinit of_platform_serial_setup(struct of_device *ofdev, +static int __devinit of_platform_serial_setup(struct platform_device *ofdev, int type, struct uart_port *port) { struct resource resource; @@ -80,7 +80,7 @@ static int __devinit of_platform_serial_setup(struct of_device *ofdev, /* * Try to register a serial port */ -static int __devinit of_platform_serial_probe(struct of_device *ofdev, +static int __devinit of_platform_serial_probe(struct platform_device *ofdev, const struct of_device_id *id) { struct of_serial_info *info; @@ -134,7 +134,7 @@ out: /* * Release a line */ -static int of_platform_serial_remove(struct of_device *ofdev) +static int of_platform_serial_remove(struct platform_device *ofdev) { struct of_serial_info *info = dev_get_drvdata(&ofdev->dev); switch (info->type) { diff --git a/drivers/serial/s5pv210.c b/drivers/serial/s5pv210.c index 4a789e5361a..6ebccd70a70 100644 --- a/drivers/serial/s5pv210.c +++ b/drivers/serial/s5pv210.c @@ -28,8 +28,12 @@ static int s5pv210_serial_setsource(struct uart_port *port, struct s3c24xx_uart_clksrc *clk) { + struct s3c2410_uartcfg *cfg = port->dev->platform_data; unsigned long ucon = rd_regl(port, S3C2410_UCON); + if ((cfg->clocks_size) == 1) + return 0; + if (strcmp(clk->name, "pclk") == 0) ucon &= ~S5PV210_UCON_CLKMASK; else if (strcmp(clk->name, "uclk1") == 0) @@ -47,10 +51,14 @@ static int s5pv210_serial_setsource(struct uart_port *port, static int s5pv210_serial_getsource(struct uart_port *port, struct s3c24xx_uart_clksrc *clk) { + struct s3c2410_uartcfg *cfg = port->dev->platform_data; u32 ucon = rd_regl(port, S3C2410_UCON); clk->divisor = 1; + if ((cfg->clocks_size) == 1) + return 0; + switch (ucon & S5PV210_UCON_CLKMASK) { case S5PV210_UCON_PCLK: clk->name = "pclk"; diff --git a/drivers/serial/samsung.c b/drivers/serial/samsung.c index a9d6c5626a0..b1156ba8ad1 100644 --- a/drivers/serial/samsung.c +++ b/drivers/serial/samsung.c @@ -705,8 +705,13 @@ static void s3c24xx_serial_set_termios(struct uart_port *port, if (ourport->info->has_divslot) { unsigned int div = ourport->baudclk_rate / baud; - udivslot = udivslot_table[div & 15]; - dbg("udivslot = %04x (div %d)\n", udivslot, div & 15); + if (cfg->has_fracval) { + udivslot = (div & 15); + dbg("fracval = %04x\n", udivslot); + } else { + udivslot = udivslot_table[div & 15]; + dbg("udivslot = %04x (div %d)\n", udivslot, div & 15); + } } switch (termios->c_cflag & CSIZE) { diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c index 5f90fcd7d10..c291b3add1d 100644 --- a/drivers/serial/sh-sci.c +++ b/drivers/serial/sh-sci.c @@ -346,6 +346,27 @@ static int scif_rxfill(struct uart_port *port) return sci_in(port, SCFDR) & SCIF2_RFDC_MASK; } } +#elif defined(CONFIG_ARCH_SH7372) +static int scif_txfill(struct uart_port *port) +{ + if (port->type == PORT_SCIFA) + return sci_in(port, SCFDR) >> 8; + else + return sci_in(port, SCTFDR); +} + +static int scif_txroom(struct uart_port *port) +{ + return port->fifosize - scif_txfill(port); +} + +static int scif_rxfill(struct uart_port *port) +{ + if (port->type == PORT_SCIFA) + return sci_in(port, SCFDR) & SCIF_RFDC_MASK; + else + return sci_in(port, SCRFDR); +} #else static int scif_txfill(struct uart_port *port) { @@ -683,7 +704,7 @@ static irqreturn_t sci_rx_interrupt(int irq, void *ptr) u16 ssr = sci_in(port, SCxSR); /* Disable future Rx interrupts */ - if (port->type == PORT_SCIFA) { + if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { disable_irq_nosync(irq); scr |= 0x4000; } else { @@ -928,7 +949,7 @@ static void sci_dma_tx_complete(void *arg) if (!uart_circ_empty(xmit)) { schedule_work(&s->work_tx); - } else if (port->type == PORT_SCIFA) { + } else if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { u16 ctrl = sci_in(port, SCSCR); sci_out(port, SCSCR, ctrl & ~SCI_CTRL_FLAGS_TIE); } @@ -1184,7 +1205,7 @@ static void sci_start_tx(struct uart_port *port) unsigned short ctrl; #ifdef CONFIG_SERIAL_SH_SCI_DMA - if (port->type == PORT_SCIFA) { + if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { u16 new, scr = sci_in(port, SCSCR); if (s->chan_tx) new = scr | 0x8000; @@ -1197,7 +1218,7 @@ static void sci_start_tx(struct uart_port *port) s->cookie_tx < 0) schedule_work(&s->work_tx); #endif - if (!s->chan_tx || port->type == PORT_SCIFA) { + if (!s->chan_tx || port->type == PORT_SCIFA || port->type == PORT_SCIFB) { /* Set TIE (Transmit Interrupt Enable) bit in SCSCR */ ctrl = sci_in(port, SCSCR); sci_out(port, SCSCR, ctrl | SCI_CTRL_FLAGS_TIE); @@ -1210,7 +1231,7 @@ static void sci_stop_tx(struct uart_port *port) /* Clear TIE (Transmit Interrupt Enable) bit in SCSCR */ ctrl = sci_in(port, SCSCR); - if (port->type == PORT_SCIFA) + if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) ctrl &= ~0x8000; ctrl &= ~SCI_CTRL_FLAGS_TIE; sci_out(port, SCSCR, ctrl); @@ -1222,7 +1243,7 @@ static void sci_start_rx(struct uart_port *port) /* Set RIE (Receive Interrupt Enable) bit in SCSCR */ ctrl |= sci_in(port, SCSCR); - if (port->type == PORT_SCIFA) + if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) ctrl &= ~0x4000; sci_out(port, SCSCR, ctrl); } @@ -1233,7 +1254,7 @@ static void sci_stop_rx(struct uart_port *port) /* Clear RIE (Receive Interrupt Enable) bit in SCSCR */ ctrl = sci_in(port, SCSCR); - if (port->type == PORT_SCIFA) + if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) ctrl &= ~0x4000; ctrl &= ~(SCI_CTRL_FLAGS_RIE | SCI_CTRL_FLAGS_REIE); sci_out(port, SCSCR, ctrl); @@ -1271,7 +1292,7 @@ static void rx_timer_fn(unsigned long arg) struct uart_port *port = &s->port; u16 scr = sci_in(port, SCSCR); - if (port->type == PORT_SCIFA) { + if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { scr &= ~0x4000; enable_irq(s->irqs[1]); } @@ -1524,6 +1545,8 @@ static const char *sci_type(struct uart_port *port) return "scif"; case PORT_SCIFA: return "scifa"; + case PORT_SCIFB: + return "scifb"; } return NULL; @@ -1612,6 +1635,9 @@ static int __devinit sci_init_single(struct platform_device *dev, port->line = index; switch (p->type) { + case PORT_SCIFB: + port->fifosize = 256; + break; case PORT_SCIFA: port->fifosize = 64; break; diff --git a/drivers/serial/sh-sci.h b/drivers/serial/sh-sci.h index f70c49f915f..9b52f77a930 100644 --- a/drivers/serial/sh-sci.h +++ b/drivers/serial/sh-sci.h @@ -322,7 +322,7 @@ #define CPU_SCIx_FNS(name, sci_offset, sci_size, scif_offset, scif_size)\ static inline unsigned int sci_##name##_in(struct uart_port *port) \ { \ - if (port->type == PORT_SCIF) { \ + if (port->type == PORT_SCIF || port->type == PORT_SCIFB) { \ SCI_IN(scif_size, scif_offset) \ } else { /* PORT_SCI or PORT_SCIFA */ \ SCI_IN(sci_size, sci_offset); \ @@ -330,7 +330,7 @@ } \ static inline void sci_##name##_out(struct uart_port *port, unsigned int value) \ { \ - if (port->type == PORT_SCIF) { \ + if (port->type == PORT_SCIF || port->type == PORT_SCIFB) { \ SCI_OUT(scif_size, scif_offset, value) \ } else { /* PORT_SCI or PORT_SCIFA */ \ SCI_OUT(sci_size, sci_offset, value); \ @@ -384,8 +384,12 @@ defined(CONFIG_CPU_SUBTYPE_SH7720) || \ defined(CONFIG_CPU_SUBTYPE_SH7721) || \ defined(CONFIG_ARCH_SH7367) || \ - defined(CONFIG_ARCH_SH7377) || \ - defined(CONFIG_ARCH_SH7372) + defined(CONFIG_ARCH_SH7377) +#define SCIF_FNS(name, scif_offset, scif_size) \ + CPU_SCIF_FNS(name, scif_offset, scif_size) +#elif defined(CONFIG_ARCH_SH7372) +#define SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size, sh4_scifb_offset, sh4_scifb_size) \ + CPU_SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size, sh4_scifb_offset, sh4_scifb_size) #define SCIF_FNS(name, scif_offset, scif_size) \ CPU_SCIF_FNS(name, scif_offset, scif_size) #else @@ -422,8 +426,7 @@ defined(CONFIG_CPU_SUBTYPE_SH7720) || \ defined(CONFIG_CPU_SUBTYPE_SH7721) || \ defined(CONFIG_ARCH_SH7367) || \ - defined(CONFIG_ARCH_SH7377) || \ - defined(CONFIG_ARCH_SH7372) + defined(CONFIG_ARCH_SH7377) SCIF_FNS(SCSMR, 0x00, 16) SCIF_FNS(SCBRR, 0x04, 8) @@ -436,6 +439,20 @@ SCIF_FNS(SCFDR, 0x1c, 16) SCIF_FNS(SCxTDR, 0x20, 8) SCIF_FNS(SCxRDR, 0x24, 8) SCIF_FNS(SCLSR, 0x00, 0) +#elif defined(CONFIG_ARCH_SH7372) +SCIF_FNS(SCSMR, 0x00, 16) +SCIF_FNS(SCBRR, 0x04, 8) +SCIF_FNS(SCSCR, 0x08, 16) +SCIF_FNS(SCTDSR, 0x0c, 16) +SCIF_FNS(SCFER, 0x10, 16) +SCIF_FNS(SCxSR, 0x14, 16) +SCIF_FNS(SCFCR, 0x18, 16) +SCIF_FNS(SCFDR, 0x1c, 16) +SCIF_FNS(SCTFDR, 0x38, 16) +SCIF_FNS(SCRFDR, 0x3c, 16) +SCIx_FNS(SCxTDR, 0x20, 8, 0x40, 8) +SCIx_FNS(SCxRDR, 0x24, 8, 0x60, 8) +SCIF_FNS(SCLSR, 0x00, 0) #elif defined(CONFIG_CPU_SUBTYPE_SH7723) ||\ defined(CONFIG_CPU_SUBTYPE_SH7724) SCIx_FNS(SCSMR, 0x00, 16, 0x00, 16) diff --git a/drivers/serial/sunhv.c b/drivers/serial/sunhv.c index a779e22d213..c9014868297 100644 --- a/drivers/serial/sunhv.c +++ b/drivers/serial/sunhv.c @@ -519,7 +519,7 @@ static struct console sunhv_console = { .data = &sunhv_reg, }; -static int __devinit hv_probe(struct of_device *op, const struct of_device_id *match) +static int __devinit hv_probe(struct platform_device *op, const struct of_device_id *match) { struct uart_port *port; unsigned long minor; @@ -598,7 +598,7 @@ out_free_port: return err; } -static int __devexit hv_remove(struct of_device *dev) +static int __devexit hv_remove(struct platform_device *dev) { struct uart_port *port = dev_get_drvdata(&dev->dev); diff --git a/drivers/serial/sunsab.c b/drivers/serial/sunsab.c index 9845fb1cfb1..5b246b18f42 100644 --- a/drivers/serial/sunsab.c +++ b/drivers/serial/sunsab.c @@ -883,7 +883,7 @@ static int sunsab_console_setup(struct console *con, char *options) printk("Console: ttyS%d (SAB82532)\n", (sunsab_reg.minor - 64) + con->index); - sunserial_console_termios(con, to_of_device(up->port.dev)->dev.of_node); + sunserial_console_termios(con, up->port.dev->of_node); switch (con->cflag & CBAUD) { case B150: baud = 150; break; @@ -954,7 +954,7 @@ static inline struct console *SUNSAB_CONSOLE(void) #endif static int __devinit sunsab_init_one(struct uart_sunsab_port *up, - struct of_device *op, + struct platform_device *op, unsigned long offset, int line) { @@ -1006,7 +1006,7 @@ static int __devinit sunsab_init_one(struct uart_sunsab_port *up, return 0; } -static int __devinit sab_probe(struct of_device *op, const struct of_device_id *match) +static int __devinit sab_probe(struct platform_device *op, const struct of_device_id *match) { static int inst; struct uart_sunsab_port *up; @@ -1062,7 +1062,7 @@ out: return err; } -static int __devexit sab_remove(struct of_device *op) +static int __devexit sab_remove(struct platform_device *op) { struct uart_sunsab_port *up = dev_get_drvdata(&op->dev); diff --git a/drivers/serial/sunsu.c b/drivers/serial/sunsu.c index 3cdf74822db..551ebfe3ccb 100644 --- a/drivers/serial/sunsu.c +++ b/drivers/serial/sunsu.c @@ -1200,7 +1200,7 @@ static int __devinit sunsu_kbd_ms_init(struct uart_sunsu_port *up) return -ENODEV; printk("%s: %s port at %llx, irq %u\n", - to_of_device(up->port.dev)->dev.of_node->full_name, + up->port.dev->of_node->full_name, (up->su_type == SU_PORT_KBD) ? "Keyboard" : "Mouse", (unsigned long long) up->port.mapbase, up->port.irq); @@ -1352,7 +1352,7 @@ static int __init sunsu_console_setup(struct console *co, char *options) spin_lock_init(&port->lock); /* Get firmware console settings. */ - sunserial_console_termios(co, to_of_device(port->dev)->dev.of_node); + sunserial_console_termios(co, port->dev->of_node); memset(&termios, 0, sizeof(struct ktermios)); termios.c_cflag = co->cflag; @@ -1406,7 +1406,7 @@ static enum su_type __devinit su_get_type(struct device_node *dp) return SU_PORT_PORT; } -static int __devinit su_probe(struct of_device *op, const struct of_device_id *match) +static int __devinit su_probe(struct platform_device *op, const struct of_device_id *match) { static int inst; struct device_node *dp = op->dev.of_node; @@ -1497,7 +1497,7 @@ out_unmap: return err; } -static int __devexit su_remove(struct of_device *op) +static int __devexit su_remove(struct platform_device *op) { struct uart_sunsu_port *up = dev_get_drvdata(&op->dev); bool kbdms = false; diff --git a/drivers/serial/sunzilog.c b/drivers/serial/sunzilog.c index d1e6bcb5954..c1967ac1c07 100644 --- a/drivers/serial/sunzilog.c +++ b/drivers/serial/sunzilog.c @@ -1230,7 +1230,7 @@ static int __init sunzilog_console_setup(struct console *con, char *options) (sunzilog_reg.minor - 64) + con->index, con->index); /* Get firmware console settings. */ - sunserial_console_termios(con, to_of_device(up->port.dev)->dev.of_node); + sunserial_console_termios(con, up->port.dev->of_node); /* Firmware console speed is limited to 150-->38400 baud so * this hackish cflag thing is OK. @@ -1399,7 +1399,7 @@ static void __devinit sunzilog_init_hw(struct uart_sunzilog_port *up) static int zilog_irq = -1; -static int __devinit zs_probe(struct of_device *op, const struct of_device_id *match) +static int __devinit zs_probe(struct platform_device *op, const struct of_device_id *match) { static int kbm_inst, uart_inst; int inst; @@ -1516,7 +1516,7 @@ static void __devexit zs_remove_one(struct uart_sunzilog_port *up) uart_remove_one_port(&sunzilog_reg, &up->port); } -static int __devexit zs_remove(struct of_device *op) +static int __devexit zs_remove(struct platform_device *op) { struct uart_sunzilog_port *up = dev_get_drvdata(&op->dev); struct zilog_layout __iomem *regs; diff --git a/drivers/serial/uartlite.c b/drivers/serial/uartlite.c index caf085d3a76..9b03d7b3e45 100644 --- a/drivers/serial/uartlite.c +++ b/drivers/serial/uartlite.c @@ -584,7 +584,7 @@ static struct platform_driver ulite_platform_driver = { */ #if defined(CONFIG_OF) && (defined(CONFIG_PPC32) || defined(CONFIG_MICROBLAZE)) static int __devinit -ulite_of_probe(struct of_device *op, const struct of_device_id *match) +ulite_of_probe(struct platform_device *op, const struct of_device_id *match) { struct resource res; const unsigned int *id; @@ -605,7 +605,7 @@ ulite_of_probe(struct of_device *op, const struct of_device_id *match) return ulite_assign(&op->dev, id ? *id : -1, res.start, irq); } -static int __devexit ulite_of_remove(struct of_device *op) +static int __devexit ulite_of_remove(struct platform_device *op) { return ulite_release(&op->dev); } diff --git a/drivers/serial/ucc_uart.c b/drivers/serial/ucc_uart.c index 907b06f5c44..3f4848e2174 100644 --- a/drivers/serial/ucc_uart.c +++ b/drivers/serial/ucc_uart.c @@ -1194,7 +1194,7 @@ static void uart_firmware_cont(const struct firmware *fw, void *context) release_firmware(fw); } -static int ucc_uart_probe(struct of_device *ofdev, +static int ucc_uart_probe(struct platform_device *ofdev, const struct of_device_id *match) { struct device_node *np = ofdev->dev.of_node; @@ -1462,7 +1462,7 @@ static int ucc_uart_probe(struct of_device *ofdev, return 0; } -static int ucc_uart_remove(struct of_device *ofdev) +static int ucc_uart_remove(struct platform_device *ofdev) { struct uart_qe_port *qe_port = dev_get_drvdata(&ofdev->dev); diff --git a/drivers/sh/Makefile b/drivers/sh/Makefile index 78bb5127abd..08fc653a825 100644 --- a/drivers/sh/Makefile +++ b/drivers/sh/Makefile @@ -1,9 +1,10 @@ # # Makefile for the SuperH specific drivers. # +obj-y := clk.o intc.o + obj-$(CONFIG_SUPERHYWAY) += superhyway/ obj-$(CONFIG_MAPLE) += maple/ + obj-$(CONFIG_GENERIC_GPIO) += pfc.o -obj-$(CONFIG_SUPERH) += clk.o obj-$(CONFIG_SH_CLK_CPG) += clk-cpg.o -obj-y += intc.o diff --git a/drivers/sh/clk-cpg.c b/drivers/sh/clk-cpg.c index f5c80ba9ab1..8c024b984ed 100644 --- a/drivers/sh/clk-cpg.c +++ b/drivers/sh/clk-cpg.c @@ -68,6 +68,39 @@ static unsigned long sh_clk_div6_recalc(struct clk *clk) return clk->freq_table[idx].frequency; } +static int sh_clk_div6_set_parent(struct clk *clk, struct clk *parent) +{ + struct clk_div_mult_table *table = &sh_clk_div6_table; + u32 value; + int ret, i; + + if (!clk->parent_table || !clk->parent_num) + return -EINVAL; + + /* Search the parent */ + for (i = 0; i < clk->parent_num; i++) + if (clk->parent_table[i] == parent) + break; + + if (i == clk->parent_num) + return -ENODEV; + + ret = clk_reparent(clk, parent); + if (ret < 0) + return ret; + + value = __raw_readl(clk->enable_reg) & + ~(((1 << clk->src_width) - 1) << clk->src_shift); + + __raw_writel(value | (i << clk->src_shift), clk->enable_reg); + + /* Rebuild the frequency table */ + clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, + table, &clk->arch_flags); + + return 0; +} + static int sh_clk_div6_set_rate(struct clk *clk, unsigned long rate, int algo_id) { @@ -117,7 +150,17 @@ static struct clk_ops sh_clk_div6_clk_ops = { .disable = sh_clk_div6_disable, }; -int __init sh_clk_div6_register(struct clk *clks, int nr) +static struct clk_ops sh_clk_div6_reparent_clk_ops = { + .recalc = sh_clk_div6_recalc, + .round_rate = sh_clk_div_round_rate, + .set_rate = sh_clk_div6_set_rate, + .enable = sh_clk_div6_enable, + .disable = sh_clk_div6_disable, + .set_parent = sh_clk_div6_set_parent, +}; + +static int __init sh_clk_div6_register_ops(struct clk *clks, int nr, + struct clk_ops *ops) { struct clk *clkp; void *freq_table; @@ -136,7 +179,7 @@ int __init sh_clk_div6_register(struct clk *clks, int nr) for (k = 0; !ret && (k < nr); k++) { clkp = clks + k; - clkp->ops = &sh_clk_div6_clk_ops; + clkp->ops = ops; clkp->id = -1; clkp->freq_table = freq_table + (k * freq_table_size); clkp->freq_table[nr_divs].frequency = CPUFREQ_TABLE_END; @@ -147,6 +190,17 @@ int __init sh_clk_div6_register(struct clk *clks, int nr) return ret; } +int __init sh_clk_div6_register(struct clk *clks, int nr) +{ + return sh_clk_div6_register_ops(clks, nr, &sh_clk_div6_clk_ops); +} + +int __init sh_clk_div6_reparent_register(struct clk *clks, int nr) +{ + return sh_clk_div6_register_ops(clks, nr, + &sh_clk_div6_reparent_clk_ops); +} + static unsigned long sh_clk_div4_recalc(struct clk *clk) { struct clk_div4_table *d4t = clk->priv; diff --git a/drivers/spi/mpc512x_psc_spi.c b/drivers/spi/mpc512x_psc_spi.c index 10baac3f8ea..cddbfceb324 100644 --- a/drivers/spi/mpc512x_psc_spi.c +++ b/drivers/spi/mpc512x_psc_spi.c @@ -507,7 +507,7 @@ static int __exit mpc512x_psc_spi_do_remove(struct device *dev) return 0; } -static int __init mpc512x_psc_spi_of_probe(struct of_device *op, +static int __init mpc512x_psc_spi_of_probe(struct platform_device *op, const struct of_device_id *match) { const u32 *regaddr_p; @@ -539,7 +539,7 @@ static int __init mpc512x_psc_spi_of_probe(struct of_device *op, irq_of_parse_and_map(op->dev.of_node, 0), id); } -static int __exit mpc512x_psc_spi_of_remove(struct of_device *op) +static int __exit mpc512x_psc_spi_of_remove(struct platform_device *op) { return mpc512x_psc_spi_do_remove(&op->dev); } diff --git a/drivers/spi/mpc52xx_psc_spi.c b/drivers/spi/mpc52xx_psc_spi.c index 66d170147dc..983fbbfce76 100644 --- a/drivers/spi/mpc52xx_psc_spi.c +++ b/drivers/spi/mpc52xx_psc_spi.c @@ -465,7 +465,7 @@ static int __exit mpc52xx_psc_spi_do_remove(struct device *dev) return 0; } -static int __init mpc52xx_psc_spi_of_probe(struct of_device *op, +static int __init mpc52xx_psc_spi_of_probe(struct platform_device *op, const struct of_device_id *match) { const u32 *regaddr_p; @@ -495,7 +495,7 @@ static int __init mpc52xx_psc_spi_of_probe(struct of_device *op, irq_of_parse_and_map(op->dev.of_node, 0), id); } -static int __exit mpc52xx_psc_spi_of_remove(struct of_device *op) +static int __exit mpc52xx_psc_spi_of_remove(struct platform_device *op) { return mpc52xx_psc_spi_do_remove(&op->dev); } diff --git a/drivers/spi/mpc52xx_spi.c b/drivers/spi/mpc52xx_spi.c index 56136ff00e0..ec9f0b1bf86 100644 --- a/drivers/spi/mpc52xx_spi.c +++ b/drivers/spi/mpc52xx_spi.c @@ -390,7 +390,7 @@ static int mpc52xx_spi_transfer(struct spi_device *spi, struct spi_message *m) /* * OF Platform Bus Binding */ -static int __devinit mpc52xx_spi_probe(struct of_device *op, +static int __devinit mpc52xx_spi_probe(struct platform_device *op, const struct of_device_id *match) { struct spi_master *master; @@ -530,7 +530,7 @@ static int __devinit mpc52xx_spi_probe(struct of_device *op, return rc; } -static int __devexit mpc52xx_spi_remove(struct of_device *op) +static int __devexit mpc52xx_spi_remove(struct platform_device *op) { struct spi_master *master = dev_get_drvdata(&op->dev); struct mpc52xx_spi *ms = spi_master_get_devdata(master); diff --git a/drivers/spi/spi_mpc8xxx.c b/drivers/spi/spi_mpc8xxx.c index aad9ae1b9c6..d31b57f7baa 100644 --- a/drivers/spi/spi_mpc8xxx.c +++ b/drivers/spi/spi_mpc8xxx.c @@ -1236,7 +1236,7 @@ static int of_mpc8xxx_spi_free_chipselects(struct device *dev) return 0; } -static int __devinit of_mpc8xxx_spi_probe(struct of_device *ofdev, +static int __devinit of_mpc8xxx_spi_probe(struct platform_device *ofdev, const struct of_device_id *ofid) { struct device *dev = &ofdev->dev; @@ -1308,7 +1308,7 @@ err_clk: return ret; } -static int __devexit of_mpc8xxx_spi_remove(struct of_device *ofdev) +static int __devexit of_mpc8xxx_spi_remove(struct platform_device *ofdev) { int ret; diff --git a/drivers/spi/spi_ppc4xx.c b/drivers/spi/spi_ppc4xx.c index 0f5fa7e2a55..80e172d3e72 100644 --- a/drivers/spi/spi_ppc4xx.c +++ b/drivers/spi/spi_ppc4xx.c @@ -388,9 +388,9 @@ static void free_gpios(struct ppc4xx_spi *hw) } /* - * of_device layer stuff... + * platform_device layer stuff... */ -static int __init spi_ppc4xx_of_probe(struct of_device *op, +static int __init spi_ppc4xx_of_probe(struct platform_device *op, const struct of_device_id *match) { struct ppc4xx_spi *hw; @@ -565,7 +565,7 @@ free_master: return ret; } -static int __exit spi_ppc4xx_of_remove(struct of_device *op) +static int __exit spi_ppc4xx_of_remove(struct platform_device *op) { struct spi_master *master = dev_get_drvdata(&op->dev); struct ppc4xx_spi *hw = spi_master_get_devdata(master); diff --git a/drivers/spi/xilinx_spi_of.c b/drivers/spi/xilinx_spi_of.c index f53d3f6b9f6..b66c2dbf20a 100644 --- a/drivers/spi/xilinx_spi_of.c +++ b/drivers/spi/xilinx_spi_of.c @@ -38,7 +38,7 @@ #include "xilinx_spi.h" -static int __devinit xilinx_spi_of_probe(struct of_device *ofdev, +static int __devinit xilinx_spi_of_probe(struct platform_device *ofdev, const struct of_device_id *match) { struct spi_master *master; @@ -84,7 +84,7 @@ static int __devinit xilinx_spi_of_probe(struct of_device *ofdev, return 0; } -static int __devexit xilinx_spi_remove(struct of_device *ofdev) +static int __devexit xilinx_spi_remove(struct platform_device *ofdev) { xilinx_spi_deinit(dev_get_drvdata(&ofdev->dev)); dev_set_drvdata(&ofdev->dev, 0); @@ -93,7 +93,7 @@ static int __devexit xilinx_spi_remove(struct of_device *ofdev) return 0; } -static int __exit xilinx_spi_of_remove(struct of_device *op) +static int __exit xilinx_spi_of_remove(struct platform_device *op) { return xilinx_spi_remove(op); } diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c index 9648b75f028..a5ea2c1d8c9 100644 --- a/drivers/usb/gadget/fsl_qe_udc.c +++ b/drivers/usb/gadget/fsl_qe_udc.c @@ -2398,7 +2398,7 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) EXPORT_SYMBOL(usb_gadget_unregister_driver); /* udc structure's alloc and setup, include ep-param alloc */ -static struct qe_udc __devinit *qe_udc_config(struct of_device *ofdev) +static struct qe_udc __devinit *qe_udc_config(struct platform_device *ofdev) { struct qe_udc *udc; struct device_node *np = ofdev->dev.of_node; @@ -2523,7 +2523,7 @@ static void qe_udc_release(struct device *dev) } /* Driver probe functions */ -static int __devinit qe_udc_probe(struct of_device *ofdev, +static int __devinit qe_udc_probe(struct platform_device *ofdev, const struct of_device_id *match) { struct device_node *np = ofdev->dev.of_node; @@ -2679,18 +2679,18 @@ err1: } #ifdef CONFIG_PM -static int qe_udc_suspend(struct of_device *dev, pm_message_t state) +static int qe_udc_suspend(struct platform_device *dev, pm_message_t state) { return -ENOTSUPP; } -static int qe_udc_resume(struct of_device *dev) +static int qe_udc_resume(struct platform_device *dev) { return -ENOTSUPP; } #endif -static int __devexit qe_udc_remove(struct of_device *ofdev) +static int __devexit qe_udc_remove(struct platform_device *ofdev) { struct qe_ep *ep; unsigned int size; diff --git a/drivers/usb/host/ehci-ppc-of.c b/drivers/usb/host/ehci-ppc-of.c index 5aec92866ab..335ee699fd8 100644 --- a/drivers/usb/host/ehci-ppc-of.c +++ b/drivers/usb/host/ehci-ppc-of.c @@ -106,7 +106,7 @@ ppc44x_enable_bmt(struct device_node *dn) static int __devinit -ehci_hcd_ppc_of_probe(struct of_device *op, const struct of_device_id *match) +ehci_hcd_ppc_of_probe(struct platform_device *op, const struct of_device_id *match) { struct device_node *dn = op->dev.of_node; struct usb_hcd *hcd; @@ -210,7 +210,7 @@ err_rmr: } -static int ehci_hcd_ppc_of_remove(struct of_device *op) +static int ehci_hcd_ppc_of_remove(struct platform_device *op) { struct usb_hcd *hcd = dev_get_drvdata(&op->dev); struct ehci_hcd *ehci = hcd_to_ehci(hcd); @@ -253,7 +253,7 @@ static int ehci_hcd_ppc_of_remove(struct of_device *op) } -static int ehci_hcd_ppc_of_shutdown(struct of_device *op) +static int ehci_hcd_ppc_of_shutdown(struct platform_device *op) { struct usb_hcd *hcd = dev_get_drvdata(&op->dev); diff --git a/drivers/usb/host/ehci-xilinx-of.c b/drivers/usb/host/ehci-xilinx-of.c index 4899f451add..6c8076ad821 100644 --- a/drivers/usb/host/ehci-xilinx-of.c +++ b/drivers/usb/host/ehci-xilinx-of.c @@ -140,7 +140,7 @@ static const struct hc_driver ehci_xilinx_of_hc_driver = { /** * ehci_hcd_xilinx_of_probe - Probe method for the USB host controller - * @op: pointer to the of_device to which the host controller bound + * @op: pointer to the platform_device bound to the host controller * @match: pointer to of_device_id structure, not used * * This function requests resources and sets up appropriate properties for the @@ -149,7 +149,7 @@ static const struct hc_driver ehci_xilinx_of_hc_driver = { * entry, and sets an appropriate value for hcd->has_tt. */ static int __devinit -ehci_hcd_xilinx_of_probe(struct of_device *op, const struct of_device_id *match) +ehci_hcd_xilinx_of_probe(struct platform_device *op, const struct of_device_id *match) { struct device_node *dn = op->dev.of_node; struct usb_hcd *hcd; @@ -242,12 +242,12 @@ err_rmr: /** * ehci_hcd_xilinx_of_remove - shutdown hcd and release resources - * @op: pointer to of_device structure that is to be removed + * @op: pointer to platform_device structure that is to be removed * * Remove the hcd structure, and release resources that has been requested * during probe. */ -static int ehci_hcd_xilinx_of_remove(struct of_device *op) +static int ehci_hcd_xilinx_of_remove(struct platform_device *op) { struct usb_hcd *hcd = dev_get_drvdata(&op->dev); dev_set_drvdata(&op->dev, NULL); @@ -266,11 +266,11 @@ static int ehci_hcd_xilinx_of_remove(struct of_device *op) /** * ehci_hcd_xilinx_of_shutdown - shutdown the hcd - * @op: pointer to of_device structure that is to be removed + * @op: pointer to platform_device structure that is to be removed * * Properly shutdown the hcd, call driver's shutdown routine. */ -static int ehci_hcd_xilinx_of_shutdown(struct of_device *op) +static int ehci_hcd_xilinx_of_shutdown(struct platform_device *op) { struct usb_hcd *hcd = dev_get_drvdata(&op->dev); diff --git a/drivers/usb/host/fhci-hcd.c b/drivers/usb/host/fhci-hcd.c index c7c8392a88b..20092a27a1e 100644 --- a/drivers/usb/host/fhci-hcd.c +++ b/drivers/usb/host/fhci-hcd.c @@ -561,7 +561,7 @@ static const struct hc_driver fhci_driver = { .hub_control = fhci_hub_control, }; -static int __devinit of_fhci_probe(struct of_device *ofdev, +static int __devinit of_fhci_probe(struct platform_device *ofdev, const struct of_device_id *ofid) { struct device *dev = &ofdev->dev; @@ -801,7 +801,7 @@ static int __devexit fhci_remove(struct device *dev) return 0; } -static int __devexit of_fhci_remove(struct of_device *ofdev) +static int __devexit of_fhci_remove(struct platform_device *ofdev) { return fhci_remove(&ofdev->dev); } diff --git a/drivers/usb/host/isp1760-if.c b/drivers/usb/host/isp1760-if.c index ec85d0c3cc3..3b28dbfca05 100644 --- a/drivers/usb/host/isp1760-if.c +++ b/drivers/usb/host/isp1760-if.c @@ -27,7 +27,7 @@ #endif #ifdef CONFIG_PPC_OF -static int of_isp1760_probe(struct of_device *dev, +static int of_isp1760_probe(struct platform_device *dev, const struct of_device_id *match) { struct usb_hcd *hcd; @@ -95,7 +95,7 @@ release_reg: return ret; } -static int of_isp1760_remove(struct of_device *dev) +static int of_isp1760_remove(struct platform_device *dev) { struct usb_hcd *hcd = dev_get_drvdata(&dev->dev); diff --git a/drivers/usb/host/ohci-ppc-of.c b/drivers/usb/host/ohci-ppc-of.c index df165917412..b2c2dbf0876 100644 --- a/drivers/usb/host/ohci-ppc-of.c +++ b/drivers/usb/host/ohci-ppc-of.c @@ -81,7 +81,7 @@ static const struct hc_driver ohci_ppc_of_hc_driver = { static int __devinit -ohci_hcd_ppc_of_probe(struct of_device *op, const struct of_device_id *match) +ohci_hcd_ppc_of_probe(struct platform_device *op, const struct of_device_id *match) { struct device_node *dn = op->dev.of_node; struct usb_hcd *hcd; @@ -183,7 +183,7 @@ err_rmr: return rv; } -static int ohci_hcd_ppc_of_remove(struct of_device *op) +static int ohci_hcd_ppc_of_remove(struct platform_device *op) { struct usb_hcd *hcd = dev_get_drvdata(&op->dev); dev_set_drvdata(&op->dev, NULL); @@ -201,7 +201,7 @@ static int ohci_hcd_ppc_of_remove(struct of_device *op) return 0; } -static int ohci_hcd_ppc_of_shutdown(struct of_device *op) +static int ohci_hcd_ppc_of_shutdown(struct platform_device *op) { struct usb_hcd *hcd = dev_get_drvdata(&op->dev); diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index a9ca72f301b..8b31fdfefc9 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -1896,6 +1896,13 @@ config FB_W100 If unsure, say N. +config SH_MIPI_DSI + tristate + depends on (SUPERH || ARCH_SHMOBILE) && HAVE_CLK + +config SH_LCD_MIPI_DSI + bool + config FB_SH_MOBILE_LCDC tristate "SuperH Mobile LCDC framebuffer support" depends on FB && (SUPERH || ARCH_SHMOBILE) && HAVE_CLK @@ -1904,9 +1911,17 @@ config FB_SH_MOBILE_LCDC select FB_SYS_IMAGEBLIT select FB_SYS_FOPS select FB_DEFERRED_IO + select SH_MIPI_DSI if SH_LCD_MIPI_DSI ---help--- Frame buffer driver for the on-chip SH-Mobile LCD controller. +config FB_SH_MOBILE_HDMI + tristate "SuperH Mobile HDMI controller support" + depends on FB_SH_MOBILE_LCDC + select FB_MODE_HELPERS + ---help--- + Driver for the on-chip SH-Mobile HDMI controller. + config FB_TMIO tristate "Toshiba Mobile IO FrameBuffer support" depends on FB && MFD_CORE @@ -1931,7 +1946,7 @@ config FB_TMIO_ACCELL config FB_S3C tristate "Samsung S3C framebuffer support" - depends on FB && ARCH_S3C64XX + depends on FB && S3C_DEV_FB select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT diff --git a/drivers/video/Makefile b/drivers/video/Makefile index f56a9cae215..485e8ed1318 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -123,6 +123,8 @@ obj-$(CONFIG_FB_IBM_GXT4500) += gxt4500.o obj-$(CONFIG_FB_PS3) += ps3fb.o obj-$(CONFIG_FB_SM501) += sm501fb.o obj-$(CONFIG_FB_XILINX) += xilinxfb.o +obj-$(CONFIG_SH_MIPI_DSI) += sh_mipi_dsi.o +obj-$(CONFIG_FB_SH_MOBILE_HDMI) += sh_mobile_hdmi.o obj-$(CONFIG_FB_SH_MOBILE_LCDC) += sh_mobile_lcdcfb.o obj-$(CONFIG_FB_OMAP) += omap/ obj-y += omap2/ diff --git a/drivers/video/bw2.c b/drivers/video/bw2.c index c7796637baf..4dc13467281 100644 --- a/drivers/video/bw2.c +++ b/drivers/video/bw2.c @@ -273,7 +273,7 @@ static int __devinit bw2_do_default_mode(struct bw2_par *par, return 0; } -static int __devinit bw2_probe(struct of_device *op, const struct of_device_id *match) +static int __devinit bw2_probe(struct platform_device *op, const struct of_device_id *match) { struct device_node *dp = op->dev.of_node; struct fb_info *info; @@ -350,7 +350,7 @@ out_err: return err; } -static int __devexit bw2_remove(struct of_device *op) +static int __devexit bw2_remove(struct platform_device *op) { struct fb_info *info = dev_get_drvdata(&op->dev); struct bw2_par *par = info->par; diff --git a/drivers/video/cg14.c b/drivers/video/cg14.c index d09fde8beb6..24249535ac8 100644 --- a/drivers/video/cg14.c +++ b/drivers/video/cg14.c @@ -446,7 +446,7 @@ static struct sbus_mmap_map __cg14_mmap_map[CG14_MMAP_ENTRIES] __devinitdata = { { .size = 0 } }; -static void cg14_unmap_regs(struct of_device *op, struct fb_info *info, +static void cg14_unmap_regs(struct platform_device *op, struct fb_info *info, struct cg14_par *par) { if (par->regs) @@ -463,7 +463,7 @@ static void cg14_unmap_regs(struct of_device *op, struct fb_info *info, info->screen_base, info->fix.smem_len); } -static int __devinit cg14_probe(struct of_device *op, const struct of_device_id *match) +static int __devinit cg14_probe(struct platform_device *op, const struct of_device_id *match) { struct device_node *dp = op->dev.of_node; struct fb_info *info; @@ -570,7 +570,7 @@ out_err: return err; } -static int __devexit cg14_remove(struct of_device *op) +static int __devexit cg14_remove(struct platform_device *op) { struct fb_info *info = dev_get_drvdata(&op->dev); struct cg14_par *par = info->par; diff --git a/drivers/video/cg3.c b/drivers/video/cg3.c index 64aa29809fb..09c0c3c4248 100644 --- a/drivers/video/cg3.c +++ b/drivers/video/cg3.c @@ -346,7 +346,7 @@ static int __devinit cg3_do_default_mode(struct cg3_par *par) return 0; } -static int __devinit cg3_probe(struct of_device *op, +static int __devinit cg3_probe(struct platform_device *op, const struct of_device_id *match) { struct device_node *dp = op->dev.of_node; @@ -433,7 +433,7 @@ out_err: return err; } -static int __devexit cg3_remove(struct of_device *op) +static int __devexit cg3_remove(struct platform_device *op) { struct fb_info *info = dev_get_drvdata(&op->dev); struct cg3_par *par = info->par; diff --git a/drivers/video/cg6.c b/drivers/video/cg6.c index 2389a719dcc..2b5a97058b0 100644 --- a/drivers/video/cg6.c +++ b/drivers/video/cg6.c @@ -718,7 +718,7 @@ static void __devinit cg6_chip_init(struct fb_info *info) sbus_writel(info->var.yres - 1, &fbc->clipmaxy); } -static void cg6_unmap_regs(struct of_device *op, struct fb_info *info, +static void cg6_unmap_regs(struct platform_device *op, struct fb_info *info, struct cg6_par *par) { if (par->fbc) @@ -737,7 +737,7 @@ static void cg6_unmap_regs(struct of_device *op, struct fb_info *info, info->fix.smem_len); } -static int __devinit cg6_probe(struct of_device *op, +static int __devinit cg6_probe(struct platform_device *op, const struct of_device_id *match) { struct device_node *dp = op->dev.of_node; @@ -827,7 +827,7 @@ out_err: return err; } -static int __devexit cg6_remove(struct of_device *op) +static int __devexit cg6_remove(struct platform_device *op) { struct fb_info *info = dev_get_drvdata(&op->dev); struct cg6_par *par = info->par; diff --git a/drivers/video/console/bitblit.c b/drivers/video/console/bitblit.c index af88651b073..28b1a834906 100644 --- a/drivers/video/console/bitblit.c +++ b/drivers/video/console/bitblit.c @@ -22,7 +22,7 @@ /* * Accelerated handlers. */ -static inline void update_attr(u8 *dst, u8 *src, int attribute, +static void update_attr(u8 *dst, u8 *src, int attribute, struct vc_data *vc) { int i, offset = (vc->vc_font.height < 10) ? 1 : 2; diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index 26bf7cbfecc..84f842331df 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -287,7 +287,7 @@ static inline int fbcon_is_inactive(struct vc_data *vc, struct fb_info *info) !vt_force_oops_output(vc); } -static inline int get_color(struct vc_data *vc, struct fb_info *info, +static int get_color(struct vc_data *vc, struct fb_info *info, u16 c, int is_fg) { int depth = fb_get_color_depth(&info->var, &info->fix); diff --git a/drivers/video/console/fbcon_ccw.c b/drivers/video/console/fbcon_ccw.c index d135671d996..41b32ae23da 100644 --- a/drivers/video/console/fbcon_ccw.c +++ b/drivers/video/console/fbcon_ccw.c @@ -22,7 +22,7 @@ * Rotation 270 degrees */ -static inline void ccw_update_attr(u8 *dst, u8 *src, int attribute, +static void ccw_update_attr(u8 *dst, u8 *src, int attribute, struct vc_data *vc) { int i, j, offset = (vc->vc_font.height < 10) ? 1 : 2; diff --git a/drivers/video/console/fbcon_cw.c b/drivers/video/console/fbcon_cw.c index 126110f8454..6a737827beb 100644 --- a/drivers/video/console/fbcon_cw.c +++ b/drivers/video/console/fbcon_cw.c @@ -22,7 +22,7 @@ * Rotation 90 degrees */ -static inline void cw_update_attr(u8 *dst, u8 *src, int attribute, +static void cw_update_attr(u8 *dst, u8 *src, int attribute, struct vc_data *vc) { int i, j, offset = (vc->vc_font.height < 10) ? 1 : 2; diff --git a/drivers/video/console/fbcon_ud.c b/drivers/video/console/fbcon_ud.c index 93a3e7381b5..ff0872c0498 100644 --- a/drivers/video/console/fbcon_ud.c +++ b/drivers/video/console/fbcon_ud.c @@ -22,7 +22,7 @@ * Rotation 180 degrees */ -static inline void ud_update_attr(u8 *dst, u8 *src, int attribute, +static void ud_update_attr(u8 *dst, u8 *src, int attribute, struct vc_data *vc) { int i, offset = (vc->vc_font.height < 10) ? 1 : 2; diff --git a/drivers/video/efifb.c b/drivers/video/efifb.c index 4a56f46af40..815f84b0793 100644 --- a/drivers/video/efifb.c +++ b/drivers/video/efifb.c @@ -16,7 +16,7 @@ #include <video/vga.h> -static struct fb_var_screeninfo efifb_defined __initdata = { +static struct fb_var_screeninfo efifb_defined __devinitdata = { .activate = FB_ACTIVATE_NOW, .height = -1, .width = -1, @@ -27,7 +27,7 @@ static struct fb_var_screeninfo efifb_defined __initdata = { .vmode = FB_VMODE_NONINTERLACED, }; -static struct fb_fix_screeninfo efifb_fix __initdata = { +static struct fb_fix_screeninfo efifb_fix __devinitdata = { .id = "EFI VGA", .type = FB_TYPE_PACKED_PIXELS, .accel = FB_ACCEL_NONE, @@ -59,7 +59,7 @@ static struct efifb_dmi_info { int stride; int width; int height; -} dmi_list[] = { +} dmi_list[] __initdata = { [M_I17] = { "i17", 0x80010000, 1472 * 4, 1440, 900 }, [M_I20] = { "i20", 0x80010000, 1728 * 4, 1680, 1050 }, /* guess */ [M_I20_SR] = { "imac7", 0x40010000, 1728 * 4, 1680, 1050 }, @@ -83,7 +83,7 @@ static int set_system(const struct dmi_system_id *id); DMI_MATCH(DMI_PRODUCT_NAME, name) }, \ &dmi_list[enumid] } -static struct dmi_system_id __initdata dmi_system_table[] = { +static const struct dmi_system_id dmi_system_table[] __initconst = { EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "iMac4,1", M_I17), /* At least one of these two will be right; maybe both? */ EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "iMac5,1", M_I20), diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index 731fce64df9..b06647517c0 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -1362,6 +1362,7 @@ fb_mmap(struct file *file, struct vm_area_struct * vma) vma->vm_pgoff = off >> PAGE_SHIFT; /* This is an IO map - tell maydump to skip this VMA */ vma->vm_flags |= VM_IO | VM_RESERVED; + vma->vm_page_prot = vm_get_page_prot(vma->vm_flags); fb_pgprotect(file, vma, off); if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, vma->vm_end - vma->vm_start, vma->vm_page_prot)) @@ -1786,7 +1787,7 @@ static int ofonly __read_mostly; int fb_get_options(char *name, char **option) { char *opt, *options = NULL; - int opt_len, retval = 0; + int retval = 0; int name_len = strlen(name), i; if (name_len && ofonly && strncmp(name, "offb", 4)) @@ -1796,8 +1797,7 @@ int fb_get_options(char *name, char **option) for (i = 0; i < FB_MAX; i++) { if (video_options[i] == NULL) continue; - opt_len = strlen(video_options[i]); - if (!opt_len) + if (!video_options[i][0]) continue; opt = video_options[i]; if (!strncmp(name, opt, name_len) && diff --git a/drivers/video/ffb.c b/drivers/video/ffb.c index f6ecfab296d..6739b2af3bc 100644 --- a/drivers/video/ffb.c +++ b/drivers/video/ffb.c @@ -893,7 +893,7 @@ static void ffb_init_fix(struct fb_info *info) info->fix.accel = FB_ACCEL_SUN_CREATOR; } -static int __devinit ffb_probe(struct of_device *op, +static int __devinit ffb_probe(struct platform_device *op, const struct of_device_id *match) { struct device_node *dp = op->dev.of_node; @@ -1023,7 +1023,7 @@ out_err: return err; } -static int __devexit ffb_remove(struct of_device *op) +static int __devexit ffb_remove(struct platform_device *op) { struct fb_info *info = dev_get_drvdata(&op->dev); struct ffb_par *par = info->par; diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c index e38ad222454..8bbbf08fa3c 100644 --- a/drivers/video/fsl-diu-fb.c +++ b/drivers/video/fsl-diu-fb.c @@ -1393,7 +1393,7 @@ static void free_irq_local(int irq) * Power management hooks. Note that we won't be called from IRQ context, * unlike the blank functions above, so we may sleep. */ -static int fsl_diu_suspend(struct of_device *ofdev, pm_message_t state) +static int fsl_diu_suspend(struct platform_device *ofdev, pm_message_t state) { struct fsl_diu_data *machine_data; @@ -1403,7 +1403,7 @@ static int fsl_diu_suspend(struct of_device *ofdev, pm_message_t state) return 0; } -static int fsl_diu_resume(struct of_device *ofdev) +static int fsl_diu_resume(struct platform_device *ofdev) { struct fsl_diu_data *machine_data; @@ -1487,7 +1487,7 @@ static ssize_t show_monitor(struct device *device, return diu_ops.show_monitor_port(machine_data->monitor_port, buf); } -static int __devinit fsl_diu_probe(struct of_device *ofdev, +static int __devinit fsl_diu_probe(struct platform_device *ofdev, const struct of_device_id *match) { struct device_node *np = ofdev->dev.of_node; @@ -1667,7 +1667,7 @@ error2: } -static int fsl_diu_remove(struct of_device *ofdev) +static int fsl_diu_remove(struct platform_device *ofdev) { struct fsl_diu_data *machine_data; int i; diff --git a/drivers/video/igafb.c b/drivers/video/igafb.c index 15d20010944..d885c770eb8 100644 --- a/drivers/video/igafb.c +++ b/drivers/video/igafb.c @@ -368,7 +368,7 @@ static int __init iga_init(struct fb_info *info, struct iga_par *par) return 1; } -int __init igafb_init(void) +static int __init igafb_init(void) { struct fb_info *info; struct pci_dev *pdev; @@ -531,6 +531,7 @@ int __init igafb_init(void) iounmap(info->screen_base); kfree(par->mmap_map); kfree(info); + return -ENODEV; } #ifdef CONFIG_SPARC @@ -556,7 +557,7 @@ int __init igafb_init(void) return 0; } -int __init igafb_setup(char *options) +static int __init igafb_setup(char *options) { char *this_opt; diff --git a/drivers/video/imxfb.c b/drivers/video/imxfb.c index 43f0639b1c1..5c363d026f6 100644 --- a/drivers/video/imxfb.c +++ b/drivers/video/imxfb.c @@ -40,6 +40,12 @@ */ #define DEBUG_VAR 1 +#if defined(CONFIG_BACKLIGHT_CLASS_DEVICE) || \ + (defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE) && \ + defined(CONFIG_FB_IMX_MODULE)) +#define PWMR_BACKLIGHT_AVAILABLE +#endif + #define DRIVER_NAME "imx-fb" #define LCDC_SSA 0x00 @@ -175,7 +181,9 @@ struct imxfb_info { struct imx_fb_videomode *mode; int num_modes; +#ifdef PWMR_BACKLIGHT_AVAILABLE struct backlight_device *bl; +#endif void (*lcd_power)(int); void (*backlight_power)(int); @@ -450,8 +458,7 @@ static int imxfb_set_par(struct fb_info *info) return 0; } - - +#ifdef PWMR_BACKLIGHT_AVAILABLE static int imxfb_bl_get_brightness(struct backlight_device *bl) { struct imxfb_info *fbi = bl_get_data(bl); @@ -516,6 +523,7 @@ static void imxfb_exit_backlight(struct imxfb_info *fbi) if (fbi->bl) backlight_device_unregister(fbi->bl); } +#endif static void imxfb_enable_controller(struct imxfb_info *fbi) { @@ -647,6 +655,9 @@ static int imxfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *inf fbi->regs + LCDC_SIZE); writel(fbi->pcr, fbi->regs + LCDC_PCR); +#ifndef PWMR_BACKLIGHT_AVAILABLE + writel(fbi->pwmr, fbi->regs + LCDC_PWMR); +#endif writel(fbi->lscr1, fbi->regs + LCDC_LSCR1); writel(fbi->dmacr, fbi->regs + LCDC_DMACR); @@ -847,7 +858,9 @@ static int __init imxfb_probe(struct platform_device *pdev) imxfb_enable_controller(fbi); fbi->pdev = pdev; +#ifdef PWMR_BACKLIGHT_AVAILABLE imxfb_init_backlight(fbi); +#endif return 0; @@ -885,7 +898,9 @@ static int __devexit imxfb_remove(struct platform_device *pdev) imxfb_disable_controller(fbi); +#ifdef PWMR_BACKLIGHT_AVAILABLE imxfb_exit_backlight(fbi); +#endif unregister_framebuffer(info); pdata = pdev->dev.platform_data; diff --git a/drivers/video/leo.c b/drivers/video/leo.c index ad677637ffb..b599e5e36ce 100644 --- a/drivers/video/leo.c +++ b/drivers/video/leo.c @@ -529,7 +529,7 @@ static void leo_fixup_var_rgb(struct fb_var_screeninfo *var) var->transp.length = 0; } -static void leo_unmap_regs(struct of_device *op, struct fb_info *info, +static void leo_unmap_regs(struct platform_device *op, struct fb_info *info, struct leo_par *par) { if (par->lc_ss0_usr) @@ -547,7 +547,7 @@ static void leo_unmap_regs(struct of_device *op, struct fb_info *info, of_iounmap(&op->resource[0], info->screen_base, 0x800000); } -static int __devinit leo_probe(struct of_device *op, +static int __devinit leo_probe(struct platform_device *op, const struct of_device_id *match) { struct device_node *dp = op->dev.of_node; @@ -637,7 +637,7 @@ out_err: return err; } -static int __devexit leo_remove(struct of_device *op) +static int __devexit leo_remove(struct platform_device *op) { struct fb_info *info = dev_get_drvdata(&op->dev); struct leo_par *par = info->par; diff --git a/drivers/video/mb862xx/mb862xxfb.c b/drivers/video/mb862xx/mb862xxfb.c index 4e2b8cc3d46..b1c4374cf94 100644 --- a/drivers/video/mb862xx/mb862xxfb.c +++ b/drivers/video/mb862xx/mb862xxfb.c @@ -550,7 +550,7 @@ static int mb862xx_gdc_init(struct mb862xxfb_par *par) return 0; } -static int __devinit of_platform_mb862xx_probe(struct of_device *ofdev, +static int __devinit of_platform_mb862xx_probe(struct platform_device *ofdev, const struct of_device_id *id) { struct device_node *np = ofdev->dev.of_node; @@ -669,7 +669,7 @@ fbrel: return ret; } -static int __devexit of_platform_mb862xx_remove(struct of_device *ofdev) +static int __devexit of_platform_mb862xx_remove(struct platform_device *ofdev) { struct fb_info *fbi = dev_get_drvdata(&ofdev->dev); struct mb862xxfb_par *par = fbi->par; diff --git a/drivers/video/msm/mddi.c b/drivers/video/msm/mddi.c index c1ff271017a..7c316c34dfc 100644 --- a/drivers/video/msm/mddi.c +++ b/drivers/video/msm/mddi.c @@ -187,10 +187,8 @@ static void mddi_wait_interrupt(struct mddi_info *mddi, uint32_t intmask); static void mddi_handle_rev_data_avail(struct mddi_info *mddi) { - union mddi_rev *rev = mddi->rev_data; uint32_t rev_data_count; uint32_t rev_crc_err_count; - int i; struct reg_read_info *ri; size_t prev_offset; uint16_t length; @@ -670,7 +668,7 @@ static int __init mddi_rev_data_setup(struct mddi_info *mddi) return 0; } -static int __init mddi_probe(struct platform_device *pdev) +static int __devinit mddi_probe(struct platform_device *pdev) { struct msm_mddi_platform_data *pdata = pdev->dev.platform_data; struct mddi_info *mddi = &mddi_info[pdev->id]; diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c index 19c01c6208e..3c28db03ad3 100644 --- a/drivers/video/msm/mdp.c +++ b/drivers/video/msm/mdp.c @@ -258,7 +258,6 @@ int get_img(struct mdp_img *img, struct fb_info *info, { int put_needed, ret = 0; struct file *file; - unsigned long vstart; file = fget_light(img->memory_id, &put_needed); if (file == NULL) diff --git a/drivers/video/p9100.c b/drivers/video/p9100.c index 688b055abab..b6c3fc2db63 100644 --- a/drivers/video/p9100.c +++ b/drivers/video/p9100.c @@ -249,7 +249,7 @@ static void p9100_init_fix(struct fb_info *info, int linebytes, struct device_no info->fix.accel = FB_ACCEL_SUN_CGTHREE; } -static int __devinit p9100_probe(struct of_device *op, const struct of_device_id *match) +static int __devinit p9100_probe(struct platform_device *op, const struct of_device_id *match) { struct device_node *dp = op->dev.of_node; struct fb_info *info; @@ -326,7 +326,7 @@ out_err: return err; } -static int __devexit p9100_remove(struct of_device *op) +static int __devexit p9100_remove(struct platform_device *op) { struct fb_info *info = dev_get_drvdata(&op->dev); struct p9100_par *par = info->par; diff --git a/drivers/video/platinumfb.c b/drivers/video/platinumfb.c index 72a1f4c0473..a50e1977b31 100644 --- a/drivers/video/platinumfb.c +++ b/drivers/video/platinumfb.c @@ -533,7 +533,7 @@ static int __init platinumfb_setup(char *options) #define invalidate_cache(addr) #endif -static int __devinit platinumfb_probe(struct of_device* odev, +static int __devinit platinumfb_probe(struct platform_device* odev, const struct of_device_id *match) { struct device_node *dp = odev->dev.of_node; @@ -646,7 +646,7 @@ static int __devinit platinumfb_probe(struct of_device* odev, return rc; } -static int __devexit platinumfb_remove(struct of_device* odev) +static int __devexit platinumfb_remove(struct platform_device* odev) { struct fb_info *info = dev_get_drvdata(&odev->dev); struct fb_info_platinum *pinfo = info->par; diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c index 9682ecc60e1..f9aca9d13d1 100644 --- a/drivers/video/s3c-fb.c +++ b/drivers/video/s3c-fb.c @@ -1,7 +1,7 @@ /* linux/drivers/video/s3c-fb.c * * Copyright 2008 Openmoko Inc. - * Copyright 2008 Simtec Electronics + * Copyright 2008-2010 Simtec Electronics * Ben Dooks <ben@simtec.co.uk> * http://armlinux.simtec.co.uk/ * @@ -9,7 +9,7 @@ * * 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. + * published by the Free Software FoundatIon. */ #include <linux/kernel.h> @@ -21,9 +21,11 @@ #include <linux/clk.h> #include <linux/fb.h> #include <linux/io.h> +#include <linux/uaccess.h> +#include <linux/interrupt.h> #include <mach/map.h> -#include <mach/regs-fb.h> +#include <plat/regs-fb-v4.h> #include <plat/fb.h> /* This driver will export a number of framebuffer interfaces depending @@ -36,9 +38,9 @@ * output timings and as the control for the output power-down state. */ -/* note, some of the functions that get called are derived from including - * <mach/regs-fb.h> as they are specific to the architecture that the code - * is being built for. +/* note, the previous use of <mach/regs-fb.h> to get platform specific data + * has been replaced by using the platform device name to pick the correct + * configuration data for the system. */ #ifdef CONFIG_FB_S3C_DEBUG_REGWRITE @@ -48,13 +50,108 @@ __raw_writel(v, r); } while(0) #endif /* FB_S3C_DEBUG_REGWRITE */ +/* irq_flags bits */ +#define S3C_FB_VSYNC_IRQ_EN 0 + +#define VSYNC_TIMEOUT_MSEC 50 + struct s3c_fb; +#define VALID_BPP(x) (1 << ((x) - 1)) + +#define OSD_BASE(win, variant) ((variant).osd + ((win) * (variant).osd_stride)) +#define VIDOSD_A(win, variant) (OSD_BASE(win, variant) + 0x00) +#define VIDOSD_B(win, variant) (OSD_BASE(win, variant) + 0x04) +#define VIDOSD_C(win, variant) (OSD_BASE(win, variant) + 0x08) +#define VIDOSD_D(win, variant) (OSD_BASE(win, variant) + 0x0C) + +/** + * struct s3c_fb_variant - fb variant information + * @is_2443: Set if S3C2443/S3C2416 style hardware. + * @nr_windows: The number of windows. + * @vidtcon: The base for the VIDTCONx registers + * @wincon: The base for the WINxCON registers. + * @winmap: The base for the WINxMAP registers. + * @keycon: The abse for the WxKEYCON registers. + * @buf_start: Offset of buffer start registers. + * @buf_size: Offset of buffer size registers. + * @buf_end: Offset of buffer end registers. + * @osd: The base for the OSD registers. + * @palette: Address of palette memory, or 0 if none. + * @has_prtcon: Set if has PRTCON register. + * @has_shadowcon: Set if has SHADOWCON register. + */ +struct s3c_fb_variant { + unsigned int is_2443:1; + unsigned short nr_windows; + unsigned short vidtcon; + unsigned short wincon; + unsigned short winmap; + unsigned short keycon; + unsigned short buf_start; + unsigned short buf_end; + unsigned short buf_size; + unsigned short osd; + unsigned short osd_stride; + unsigned short palette[S3C_FB_MAX_WIN]; + + unsigned int has_prtcon:1; + unsigned int has_shadowcon:1; +}; + +/** + * struct s3c_fb_win_variant + * @has_osd_c: Set if has OSD C register. + * @has_osd_d: Set if has OSD D register. + * @has_osd_alpha: Set if can change alpha transparency for a window. + * @palette_sz: Size of palette in entries. + * @palette_16bpp: Set if palette is 16bits wide. + * @osd_size_off: If != 0, supports setting up OSD for a window; the appropriate + * register is located at the given offset from OSD_BASE. + * @valid_bpp: 1 bit per BPP setting to show valid bits-per-pixel. + * + * valid_bpp bit x is set if (x+1)BPP is supported. + */ +struct s3c_fb_win_variant { + unsigned int has_osd_c:1; + unsigned int has_osd_d:1; + unsigned int has_osd_alpha:1; + unsigned int palette_16bpp:1; + unsigned short osd_size_off; + unsigned short palette_sz; + u32 valid_bpp; +}; + +/** + * struct s3c_fb_driverdata - per-device type driver data for init time. + * @variant: The variant information for this driver. + * @win: The window information for each window. + */ +struct s3c_fb_driverdata { + struct s3c_fb_variant variant; + struct s3c_fb_win_variant *win[S3C_FB_MAX_WIN]; +}; + +/** + * struct s3c_fb_palette - palette information + * @r: Red bitfield. + * @g: Green bitfield. + * @b: Blue bitfield. + * @a: Alpha bitfield. + */ +struct s3c_fb_palette { + struct fb_bitfield r; + struct fb_bitfield g; + struct fb_bitfield b; + struct fb_bitfield a; +}; + /** * struct s3c_fb_win - per window private data for each framebuffer. * @windata: The platform data supplied for the window configuration. * @parent: The hardware that this window is part of. * @fbinfo: Pointer pack to the framebuffer info for this window. + * @varint: The variant information for this window. * @palette_buffer: Buffer/cache to hold palette entries. * @pseudo_palette: For use in TRUECOLOUR modes for entries 0..15/ * @index: The window number of this window. @@ -65,6 +162,7 @@ struct s3c_fb_win { struct s3c_fb *parent; struct fb_info *fbinfo; struct s3c_fb_palette palette; + struct s3c_fb_win_variant variant; u32 *palette_buffer; u32 pseudo_palette[16]; @@ -72,37 +170,54 @@ struct s3c_fb_win { }; /** + * struct s3c_fb_vsync - vsync information + * @wait: a queue for processes waiting for vsync + * @count: vsync interrupt count + */ +struct s3c_fb_vsync { + wait_queue_head_t wait; + unsigned int count; +}; + +/** * struct s3c_fb - overall hardware state of the hardware * @dev: The device that we bound to, for printing, etc. * @regs_res: The resource we claimed for the IO registers. * @bus_clk: The clk (hclk) feeding our interface and possibly pixclk. * @regs: The mapped hardware registers. + * @variant: Variant information for this hardware. * @enabled: A bitmask of enabled hardware windows. * @pdata: The platform configuration data passed with the device. * @windows: The hardware windows that have been claimed. + * @irq_no: IRQ line number + * @irq_flags: irq flags + * @vsync_info: VSYNC-related information (count, queues...) */ struct s3c_fb { struct device *dev; struct resource *regs_res; struct clk *bus_clk; void __iomem *regs; + struct s3c_fb_variant variant; unsigned char enabled; struct s3c_fb_platdata *pdata; struct s3c_fb_win *windows[S3C_FB_MAX_WIN]; + + int irq_no; + unsigned long irq_flags; + struct s3c_fb_vsync vsync_info; }; /** - * s3c_fb_win_has_palette() - determine if a mode has a palette - * @win: The window number being queried. - * @bpp: The number of bits per pixel to test. - * - * Work out if the given window supports palletised data at the specified bpp. + * s3c_fb_validate_win_bpp - validate the bits-per-pixel for this mode. + * @win: The device window. + * @bpp: The bit depth. */ -static int s3c_fb_win_has_palette(unsigned int win, unsigned int bpp) +static bool s3c_fb_validate_win_bpp(struct s3c_fb_win *win, unsigned int bpp) { - return s3c_fb_win_pal_size(win) <= (1 << bpp); + return win->variant.valid_bpp & VALID_BPP(bpp); } /** @@ -125,7 +240,7 @@ static int s3c_fb_check_var(struct fb_var_screeninfo *var, var->xres_virtual = max((unsigned int)windata->virtual_x, var->xres); var->yres_virtual = max((unsigned int)windata->virtual_y, var->yres); - if (!s3c_fb_validate_win_bpp(win->index, var->bits_per_pixel)) { + if (!s3c_fb_validate_win_bpp(win, var->bits_per_pixel)) { dev_dbg(sfb->dev, "win %d: unsupported bpp %d\n", win->index, var->bits_per_pixel); return -EINVAL; @@ -140,7 +255,7 @@ static int s3c_fb_check_var(struct fb_var_screeninfo *var, case 2: case 4: case 8: - if (!s3c_fb_win_has_palette(win->index, var->bits_per_pixel)) { + if (sfb->variant.palette[win->index] != 0) { /* non palletised, A:1,R:2,G:3,B:2 mode */ var->red.offset = 4; var->green.offset = 2; @@ -255,6 +370,66 @@ static int s3c_fb_align_word(unsigned int bpp, unsigned int pix) } /** + * vidosd_set_size() - set OSD size for a window + * + * @win: the window to set OSD size for + * @size: OSD size register value + */ +static void vidosd_set_size(struct s3c_fb_win *win, u32 size) +{ + struct s3c_fb *sfb = win->parent; + + /* OSD can be set up if osd_size_off != 0 for this window */ + if (win->variant.osd_size_off) + writel(size, sfb->regs + OSD_BASE(win->index, sfb->variant) + + win->variant.osd_size_off); +} + +/** + * vidosd_set_alpha() - set alpha transparency for a window + * + * @win: the window to set OSD size for + * @alpha: alpha register value + */ +static void vidosd_set_alpha(struct s3c_fb_win *win, u32 alpha) +{ + struct s3c_fb *sfb = win->parent; + + if (win->variant.has_osd_alpha) + writel(alpha, sfb->regs + VIDOSD_C(win->index, sfb->variant)); +} + +/** + * shadow_protect_win() - disable updating values from shadow registers at vsync + * + * @win: window to protect registers for + * @protect: 1 to protect (disable updates) + */ +static void shadow_protect_win(struct s3c_fb_win *win, bool protect) +{ + struct s3c_fb *sfb = win->parent; + u32 reg; + + if (protect) { + if (sfb->variant.has_prtcon) { + writel(PRTCON_PROTECT, sfb->regs + PRTCON); + } else if (sfb->variant.has_shadowcon) { + reg = readl(sfb->regs + SHADOWCON); + writel(reg | SHADOWCON_WINx_PROTECT(win->index), + sfb->regs + SHADOWCON); + } + } else { + if (sfb->variant.has_prtcon) { + writel(0, sfb->regs + PRTCON); + } else if (sfb->variant.has_shadowcon) { + reg = readl(sfb->regs + SHADOWCON); + writel(reg & ~SHADOWCON_WINx_PROTECT(win->index), + sfb->regs + SHADOWCON); + } + } +} + +/** * s3c_fb_set_par() - framebuffer request to set new framebuffer state. * @info: The framebuffer to change. * @@ -266,14 +441,17 @@ static int s3c_fb_set_par(struct fb_info *info) struct s3c_fb_win *win = info->par; struct s3c_fb *sfb = win->parent; void __iomem *regs = sfb->regs; + void __iomem *buf = regs; int win_no = win->index; - u32 osdc_data = 0; + u32 alpha = 0; u32 data; u32 pagewidth; int clkdiv; dev_dbg(sfb->dev, "setting framebuffer parameters\n"); + shadow_protect_win(win, 1); + switch (var->bits_per_pixel) { case 32: case 24: @@ -282,7 +460,7 @@ static int s3c_fb_set_par(struct fb_info *info) info->fix.visual = FB_VISUAL_TRUECOLOR; break; case 8: - if (s3c_fb_win_has_palette(win_no, 8)) + if (win->variant.palette_sz >= 256) info->fix.visual = FB_VISUAL_PSEUDOCOLOR; else info->fix.visual = FB_VISUAL_TRUECOLOR; @@ -297,12 +475,15 @@ static int s3c_fb_set_par(struct fb_info *info) info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8; + info->fix.xpanstep = info->var.xres_virtual > info->var.xres ? 1 : 0; + info->fix.ypanstep = info->var.yres_virtual > info->var.yres ? 1 : 0; + /* disable the window whilst we update it */ writel(0, regs + WINCON(win_no)); - /* use window 0 as the basis for the lcd output timings */ + /* use platform specified window as the basis for the lcd timings */ - if (win_no == 0) { + if (win_no == sfb->pdata->default_win) { clkdiv = s3c_fb_calc_pixclk(sfb, var->pixclock); data = sfb->pdata->vidcon0; @@ -315,6 +496,9 @@ static int s3c_fb_set_par(struct fb_info *info) /* write the timing data to the panel */ + if (sfb->variant.is_2443) + data |= (1 << 5); + data |= VIDCON0_ENVID | VIDCON0_ENVID_F; writel(data, regs + VIDCON0); @@ -322,53 +506,54 @@ static int s3c_fb_set_par(struct fb_info *info) VIDTCON0_VFPD(var->lower_margin - 1) | VIDTCON0_VSPW(var->vsync_len - 1); - writel(data, regs + VIDTCON0); + writel(data, regs + sfb->variant.vidtcon); data = VIDTCON1_HBPD(var->left_margin - 1) | VIDTCON1_HFPD(var->right_margin - 1) | VIDTCON1_HSPW(var->hsync_len - 1); - writel(data, regs + VIDTCON1); + /* VIDTCON1 */ + writel(data, regs + sfb->variant.vidtcon + 4); data = VIDTCON2_LINEVAL(var->yres - 1) | VIDTCON2_HOZVAL(var->xres - 1); - writel(data, regs + VIDTCON2); + writel(data, regs +sfb->variant.vidtcon + 8 ); } /* write the buffer address */ - writel(info->fix.smem_start, regs + VIDW_BUF_START(win_no)); + /* start and end registers stride is 8 */ + buf = regs + win_no * 8; + + writel(info->fix.smem_start, buf + sfb->variant.buf_start); data = info->fix.smem_start + info->fix.line_length * var->yres; - writel(data, regs + VIDW_BUF_END(win_no)); + writel(data, buf + sfb->variant.buf_end); pagewidth = (var->xres * var->bits_per_pixel) >> 3; data = VIDW_BUF_SIZE_OFFSET(info->fix.line_length - pagewidth) | VIDW_BUF_SIZE_PAGEWIDTH(pagewidth); - writel(data, regs + VIDW_BUF_SIZE(win_no)); + writel(data, regs + sfb->variant.buf_size + (win_no * 4)); /* write 'OSD' registers to control position of framebuffer */ data = VIDOSDxA_TOPLEFT_X(0) | VIDOSDxA_TOPLEFT_Y(0); - writel(data, regs + VIDOSD_A(win_no)); + writel(data, regs + VIDOSD_A(win_no, sfb->variant)); data = VIDOSDxB_BOTRIGHT_X(s3c_fb_align_word(var->bits_per_pixel, var->xres - 1)) | VIDOSDxB_BOTRIGHT_Y(var->yres - 1); - writel(data, regs + VIDOSD_B(win_no)); + writel(data, regs + VIDOSD_B(win_no, sfb->variant)); data = var->xres * var->yres; - osdc_data = VIDISD14C_ALPHA1_R(0xf) | + alpha = VIDISD14C_ALPHA1_R(0xf) | VIDISD14C_ALPHA1_G(0xf) | VIDISD14C_ALPHA1_B(0xf); - if (s3c_fb_has_osd_d(win_no)) { - writel(data, regs + VIDOSD_D(win_no)); - writel(osdc_data, regs + VIDOSD_C(win_no)); - } else - writel(data, regs + VIDOSD_C(win_no)); + vidosd_set_alpha(win, alpha); + vidosd_set_size(win, data); data = WINCONx_ENWIN; @@ -424,13 +609,15 @@ static int s3c_fb_set_par(struct fb_info *info) else data |= WINCON0_BPPMODE_24BPP_888; + data |= WINCONx_WSWP; data |= WINCONx_BURSTLEN_16WORD; break; } - /* It has no color key control register for window0 */ + /* Enable the colour keying for the window below this one */ if (win_no > 0) { u32 keycon0_data = 0, keycon1_data = 0; + void __iomem *keycon = regs + sfb->variant.keycon; keycon0_data = ~(WxKEYCON0_KEYBL_EN | WxKEYCON0_KEYEN_F | @@ -438,12 +625,23 @@ static int s3c_fb_set_par(struct fb_info *info) keycon1_data = WxKEYCON1_COLVAL(0xffffff); - writel(keycon0_data, regs + WxKEYCONy(win_no-1, 0)); - writel(keycon1_data, regs + WxKEYCONy(win_no-1, 1)); + keycon += (win_no - 1) * 8; + + writel(keycon0_data, keycon + WKEYCON0); + writel(keycon1_data, keycon + WKEYCON1); + } + + writel(data, regs + sfb->variant.wincon + (win_no * 4)); + writel(0x0, regs + sfb->variant.winmap + (win_no * 4)); + + /* Enable DMA channel for this window */ + if (sfb->variant.has_shadowcon) { + data = readl(sfb->regs + SHADOWCON); + data |= SHADOWCON_CHx_ENABLE(win_no); + writel(data, sfb->regs + SHADOWCON); } - writel(data, regs + WINCON(win_no)); - writel(0x0, regs + WINxMAP(win_no)); + shadow_protect_win(win, 0); return 0; } @@ -470,7 +668,7 @@ static void s3c_fb_update_palette(struct s3c_fb *sfb, void __iomem *palreg; u32 palcon; - palreg = sfb->regs + s3c_fb_pal_reg(win->index, reg); + palreg = sfb->regs + sfb->variant.palette[win->index]; dev_dbg(sfb->dev, "%s: win %d, reg %d (%p): %08x\n", __func__, win->index, reg, palreg, value); @@ -480,10 +678,10 @@ static void s3c_fb_update_palette(struct s3c_fb *sfb, palcon = readl(sfb->regs + WPALCON); writel(palcon | WPALCON_PAL_UPDATE, sfb->regs + WPALCON); - if (s3c_fb_pal_is16(win->index)) - writew(value, palreg); + if (win->variant.palette_16bpp) + writew(value, palreg + (reg * 2)); else - writel(value, palreg); + writel(value, palreg + (reg * 4)); writel(palcon, sfb->regs + WPALCON); } @@ -532,7 +730,7 @@ static int s3c_fb_setcolreg(unsigned regno, break; case FB_VISUAL_PSEUDOCOLOR: - if (regno < s3c_fb_win_pal_size(win->index)) { + if (regno < win->variant.palette_sz) { val = chan_to_field(red, &win->palette.r); val |= chan_to_field(green, &win->palette.g); val |= chan_to_field(blue, &win->palette.b); @@ -591,7 +789,7 @@ static int s3c_fb_blank(int blank_mode, struct fb_info *info) dev_dbg(sfb->dev, "blank mode %d\n", blank_mode); - wincon = readl(sfb->regs + WINCON(index)); + wincon = readl(sfb->regs + sfb->variant.wincon + (index * 4)); switch (blank_mode) { case FB_BLANK_POWERDOWN: @@ -602,11 +800,11 @@ static int s3c_fb_blank(int blank_mode, struct fb_info *info) case FB_BLANK_NORMAL: /* disable the DMA and display 0x0 (black) */ writel(WINxMAP_MAP | WINxMAP_MAP_COLOUR(0x0), - sfb->regs + WINxMAP(index)); + sfb->regs + sfb->variant.winmap + (index * 4)); break; case FB_BLANK_UNBLANK: - writel(0x0, sfb->regs + WINxMAP(index)); + writel(0x0, sfb->regs + sfb->variant.winmap + (index * 4)); wincon |= WINCONx_ENWIN; sfb->enabled |= (1 << index); break; @@ -617,7 +815,7 @@ static int s3c_fb_blank(int blank_mode, struct fb_info *info) return 1; } - writel(wincon, sfb->regs + WINCON(index)); + writel(wincon, sfb->regs + sfb->variant.wincon + (index * 4)); /* Check the enabled state to see if we need to be running the * main LCD interface, as if there are no active windows then @@ -636,12 +834,185 @@ static int s3c_fb_blank(int blank_mode, struct fb_info *info) /* we're stuck with this until we can do something about overriding * the power control using the blanking event for a single fb. */ - if (index == 0) + if (index == sfb->pdata->default_win) s3c_fb_enable(sfb, blank_mode != FB_BLANK_POWERDOWN ? 1 : 0); return 0; } +/** + * s3c_fb_pan_display() - Pan the display. + * + * Note that the offsets can be written to the device at any time, as their + * values are latched at each vsync automatically. This also means that only + * the last call to this function will have any effect on next vsync, but + * there is no need to sleep waiting for it to prevent tearing. + * + * @var: The screen information to verify. + * @info: The framebuffer device. + */ +static int s3c_fb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct s3c_fb_win *win = info->par; + struct s3c_fb *sfb = win->parent; + void __iomem *buf = sfb->regs + win->index * 8; + unsigned int start_boff, end_boff; + + /* Offset in bytes to the start of the displayed area */ + start_boff = var->yoffset * info->fix.line_length; + /* X offset depends on the current bpp */ + if (info->var.bits_per_pixel >= 8) { + start_boff += var->xoffset * (info->var.bits_per_pixel >> 3); + } else { + switch (info->var.bits_per_pixel) { + case 4: + start_boff += var->xoffset >> 1; + break; + case 2: + start_boff += var->xoffset >> 2; + break; + case 1: + start_boff += var->xoffset >> 3; + break; + default: + dev_err(sfb->dev, "invalid bpp\n"); + return -EINVAL; + } + } + /* Offset in bytes to the end of the displayed area */ + end_boff = start_boff + var->yres * info->fix.line_length; + + /* Temporarily turn off per-vsync update from shadow registers until + * both start and end addresses are updated to prevent corruption */ + shadow_protect_win(win, 1); + + writel(info->fix.smem_start + start_boff, buf + sfb->variant.buf_start); + writel(info->fix.smem_start + end_boff, buf + sfb->variant.buf_end); + + shadow_protect_win(win, 0); + + return 0; +} + +/** + * s3c_fb_enable_irq() - enable framebuffer interrupts + * @sfb: main hardware state + */ +static void s3c_fb_enable_irq(struct s3c_fb *sfb) +{ + void __iomem *regs = sfb->regs; + u32 irq_ctrl_reg; + + if (!test_and_set_bit(S3C_FB_VSYNC_IRQ_EN, &sfb->irq_flags)) { + /* IRQ disabled, enable it */ + irq_ctrl_reg = readl(regs + VIDINTCON0); + + irq_ctrl_reg |= VIDINTCON0_INT_ENABLE; + irq_ctrl_reg |= VIDINTCON0_INT_FRAME; + + irq_ctrl_reg &= ~VIDINTCON0_FRAMESEL0_MASK; + irq_ctrl_reg |= VIDINTCON0_FRAMESEL0_VSYNC; + irq_ctrl_reg &= ~VIDINTCON0_FRAMESEL1_MASK; + irq_ctrl_reg |= VIDINTCON0_FRAMESEL1_NONE; + + writel(irq_ctrl_reg, regs + VIDINTCON0); + } +} + +/** + * s3c_fb_disable_irq() - disable framebuffer interrupts + * @sfb: main hardware state + */ +static void s3c_fb_disable_irq(struct s3c_fb *sfb) +{ + void __iomem *regs = sfb->regs; + u32 irq_ctrl_reg; + + if (test_and_clear_bit(S3C_FB_VSYNC_IRQ_EN, &sfb->irq_flags)) { + /* IRQ enabled, disable it */ + irq_ctrl_reg = readl(regs + VIDINTCON0); + + irq_ctrl_reg &= ~VIDINTCON0_INT_FRAME; + irq_ctrl_reg &= ~VIDINTCON0_INT_ENABLE; + + writel(irq_ctrl_reg, regs + VIDINTCON0); + } +} + +static irqreturn_t s3c_fb_irq(int irq, void *dev_id) +{ + struct s3c_fb *sfb = dev_id; + void __iomem *regs = sfb->regs; + u32 irq_sts_reg; + + irq_sts_reg = readl(regs + VIDINTCON1); + + if (irq_sts_reg & VIDINTCON1_INT_FRAME) { + + /* VSYNC interrupt, accept it */ + writel(VIDINTCON1_INT_FRAME, regs + VIDINTCON1); + + sfb->vsync_info.count++; + wake_up_interruptible(&sfb->vsync_info.wait); + } + + /* We only support waiting for VSYNC for now, so it's safe + * to always disable irqs here. + */ + s3c_fb_disable_irq(sfb); + + return IRQ_HANDLED; +} + +/** + * s3c_fb_wait_for_vsync() - sleep until next VSYNC interrupt or timeout + * @sfb: main hardware state + * @crtc: head index. + */ +static int s3c_fb_wait_for_vsync(struct s3c_fb *sfb, u32 crtc) +{ + unsigned long count; + int ret; + + if (crtc != 0) + return -ENODEV; + + count = sfb->vsync_info.count; + s3c_fb_enable_irq(sfb); + ret = wait_event_interruptible_timeout(sfb->vsync_info.wait, + count != sfb->vsync_info.count, + msecs_to_jiffies(VSYNC_TIMEOUT_MSEC)); + if (ret == 0) + return -ETIMEDOUT; + + return 0; +} + +static int s3c_fb_ioctl(struct fb_info *info, unsigned int cmd, + unsigned long arg) +{ + struct s3c_fb_win *win = info->par; + struct s3c_fb *sfb = win->parent; + int ret; + u32 crtc; + + switch (cmd) { + case FBIO_WAITFORVSYNC: + if (get_user(crtc, (u32 __user *)arg)) { + ret = -EFAULT; + break; + } + + ret = s3c_fb_wait_for_vsync(sfb, crtc); + break; + default: + ret = -ENOTTY; + } + + return ret; +} + static struct fb_ops s3c_fb_ops = { .owner = THIS_MODULE, .fb_check_var = s3c_fb_check_var, @@ -651,9 +1022,33 @@ static struct fb_ops s3c_fb_ops = { .fb_fillrect = cfb_fillrect, .fb_copyarea = cfb_copyarea, .fb_imageblit = cfb_imageblit, + .fb_pan_display = s3c_fb_pan_display, + .fb_ioctl = s3c_fb_ioctl, }; /** + * s3c_fb_missing_pixclock() - calculates pixel clock + * @mode: The video mode to change. + * + * Calculate the pixel clock when none has been given through platform data. + */ +static void __devinit s3c_fb_missing_pixclock(struct fb_videomode *mode) +{ + u64 pixclk = 1000000000000ULL; + u32 div; + + div = mode->left_margin + mode->hsync_len + mode->right_margin + + mode->xres; + div *= mode->upper_margin + mode->vsync_len + mode->lower_margin + + mode->yres; + div *= mode->refresh ? : 60; + + do_div(pixclk, div); + + mode->pixclock = pixclk; +} + +/** * s3c_fb_alloc_memory() - allocate display memory for framebuffer window * @sfb: The base resources for the hardware. * @win: The window to initialise memory for. @@ -711,7 +1106,8 @@ static void s3c_fb_free_memory(struct s3c_fb *sfb, struct s3c_fb_win *win) { struct fb_info *fbi = win->fbinfo; - dma_free_writecombine(sfb->dev, PAGE_ALIGN(fbi->fix.smem_len), + if (fbi->screen_base) + dma_free_writecombine(sfb->dev, PAGE_ALIGN(fbi->fix.smem_len), fbi->screen_base, fbi->fix.smem_start); } @@ -724,9 +1120,18 @@ static void s3c_fb_free_memory(struct s3c_fb *sfb, struct s3c_fb_win *win) */ static void s3c_fb_release_win(struct s3c_fb *sfb, struct s3c_fb_win *win) { + u32 data; + if (win->fbinfo) { + if (sfb->variant.has_shadowcon) { + data = readl(sfb->regs + SHADOWCON); + data &= ~SHADOWCON_CHx_ENABLE(win->index); + data &= ~SHADOWCON_CHx_LOCAL_ENABLE(win->index); + writel(data, sfb->regs + SHADOWCON); + } unregister_framebuffer(win->fbinfo); - fb_dealloc_cmap(&win->fbinfo->cmap); + if (win->fbinfo->cmap.len) + fb_dealloc_cmap(&win->fbinfo->cmap); s3c_fb_free_memory(sfb, win); framebuffer_release(win->fbinfo); } @@ -735,12 +1140,14 @@ static void s3c_fb_release_win(struct s3c_fb *sfb, struct s3c_fb_win *win) /** * s3c_fb_probe_win() - register an hardware window * @sfb: The base resources for the hardware + * @variant: The variant information for this window. * @res: Pointer to where to place the resultant window. * * Allocate and do the basic initialisation for one of the hardware's graphics * windows. */ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, + struct s3c_fb_win_variant *variant, struct s3c_fb_win **res) { struct fb_var_screeninfo *var; @@ -751,9 +1158,11 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, int palette_size; int ret; - dev_dbg(sfb->dev, "probing window %d\n", win_no); + dev_dbg(sfb->dev, "probing window %d, variant %p\n", win_no, variant); + + init_waitqueue_head(&sfb->vsync_info.wait); - palette_size = s3c_fb_win_pal_size(win_no); + palette_size = variant->palette_sz * 4; fbinfo = framebuffer_alloc(sizeof(struct s3c_fb_win) + palette_size * sizeof(u32), sfb->dev); @@ -770,7 +1179,9 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, WARN_ON(windata->win_mode.yres == 0); win = fbinfo->par; + *res = win; var = &fbinfo->var; + win->variant = *variant; win->fbinfo = fbinfo; win->parent = sfb; win->windata = windata; @@ -784,7 +1195,24 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, } /* setup the r/b/g positions for the window's palette */ - s3c_fb_init_palette(win_no, &win->palette); + if (win->variant.palette_16bpp) { + /* Set RGB 5:6:5 as default */ + win->palette.r.offset = 11; + win->palette.r.length = 5; + win->palette.g.offset = 5; + win->palette.g.length = 6; + win->palette.b.offset = 0; + win->palette.b.length = 5; + + } else { + /* Set 8bpp or 8bpp and 1bit alpha */ + win->palette.r.offset = 16; + win->palette.r.length = 8; + win->palette.g.offset = 8; + win->palette.g.length = 8; + win->palette.b.offset = 0; + win->palette.b.length = 8; + } /* setup the initial video mode from the window */ fb_videomode_to_var(&fbinfo->var, initmode); @@ -808,7 +1236,7 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, /* create initial colour map */ - ret = fb_alloc_cmap(&fbinfo->cmap, s3c_fb_win_pal_size(win_no), 1); + ret = fb_alloc_cmap(&fbinfo->cmap, win->variant.palette_sz, 1); if (ret == 0) fb_set_cmap(&fbinfo->cmap, fbinfo); else @@ -826,7 +1254,6 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, return ret; } - *res = win; dev_info(sfb->dev, "window %d: fb %s\n", win_no, fbinfo->fix.id); return 0; @@ -842,18 +1269,19 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, static void s3c_fb_clear_win(struct s3c_fb *sfb, int win) { void __iomem *regs = sfb->regs; - - writel(0, regs + WINCON(win)); - writel(0xffffff, regs + WxKEYCONy(win, 0)); - writel(0xffffff, regs + WxKEYCONy(win, 1)); - - writel(0, regs + VIDOSD_A(win)); - writel(0, regs + VIDOSD_B(win)); - writel(0, regs + VIDOSD_C(win)); + u32 reg; + + writel(0, regs + sfb->variant.wincon + (win * 4)); + writel(0, regs + VIDOSD_A(win, sfb->variant)); + writel(0, regs + VIDOSD_B(win, sfb->variant)); + writel(0, regs + VIDOSD_C(win, sfb->variant)); + reg = readl(regs + SHADOWCON); + writel(reg & ~SHADOWCON_WINx_PROTECT(win), regs + SHADOWCON); } static int __devinit s3c_fb_probe(struct platform_device *pdev) { + struct s3c_fb_driverdata *fbdrv; struct device *dev = &pdev->dev; struct s3c_fb_platdata *pd; struct s3c_fb *sfb; @@ -861,6 +1289,13 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) int win; int ret = 0; + fbdrv = (struct s3c_fb_driverdata *)platform_get_device_id(pdev)->driver_data; + + if (fbdrv->variant.nr_windows > S3C_FB_MAX_WIN) { + dev_err(dev, "too many windows, cannot attach\n"); + return -EINVAL; + } + pd = pdev->dev.platform_data; if (!pd) { dev_err(dev, "no platform data specified\n"); @@ -873,8 +1308,11 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) return -ENOMEM; } + dev_dbg(dev, "allocate new framebuffer %p\n", sfb); + sfb->dev = dev; sfb->pdata = pd; + sfb->variant = fbdrv->variant; sfb->bus_clk = clk_get(dev, "lcd"); if (IS_ERR(sfb->bus_clk)) { @@ -906,6 +1344,20 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) goto err_req_region; } + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(dev, "failed to acquire irq resource\n"); + ret = -ENOENT; + goto err_ioremap; + } + sfb->irq_no = res->start; + ret = request_irq(sfb->irq_no, s3c_fb_irq, + 0, "s3c_fb", sfb); + if (ret) { + dev_err(dev, "irq request failed\n"); + goto err_ioremap; + } + dev_dbg(dev, "got resources (regs %p), probing windows\n", sfb->regs); /* setup gpio and output polarity controls */ @@ -916,21 +1368,34 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) /* zero all windows before we do anything */ - for (win = 0; win < S3C_FB_MAX_WIN; win++) + for (win = 0; win < fbdrv->variant.nr_windows; win++) s3c_fb_clear_win(sfb, win); + /* initialise colour key controls */ + for (win = 0; win < (fbdrv->variant.nr_windows - 1); win++) { + void __iomem *regs = sfb->regs + sfb->variant.keycon; + + regs += (win * 8); + writel(0xffffff, regs + WKEYCON0); + writel(0xffffff, regs + WKEYCON1); + } + /* we have the register setup, start allocating framebuffers */ - for (win = 0; win < S3C_FB_MAX_WIN; win++) { + for (win = 0; win < fbdrv->variant.nr_windows; win++) { if (!pd->win[win]) continue; - ret = s3c_fb_probe_win(sfb, win, &sfb->windows[win]); + if (!pd->win[win]->win_mode.pixclock) + s3c_fb_missing_pixclock(&pd->win[win]->win_mode); + + ret = s3c_fb_probe_win(sfb, win, fbdrv->win[win], + &sfb->windows[win]); if (ret < 0) { dev_err(dev, "failed to create window %d\n", win); for (; win >= 0; win--) s3c_fb_release_win(sfb, sfb->windows[win]); - goto err_ioremap; + goto err_irq; } } @@ -938,6 +1403,9 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) return 0; +err_irq: + free_irq(sfb->irq_no, sfb); + err_ioremap: iounmap(sfb->regs); @@ -970,6 +1438,8 @@ static int __devexit s3c_fb_remove(struct platform_device *pdev) if (sfb->windows[win]) s3c_fb_release_win(sfb, sfb->windows[win]); + free_irq(sfb->irq_no, sfb); + iounmap(sfb->regs); clk_disable(sfb->bus_clk); @@ -1016,9 +1486,17 @@ static int s3c_fb_resume(struct platform_device *pdev) writel(pd->vidcon1, sfb->regs + VIDCON1); /* zero all windows before we do anything */ - for (win_no = 0; win_no < S3C_FB_MAX_WIN; win_no++) + for (win_no = 0; win_no < sfb->variant.nr_windows; win_no++) s3c_fb_clear_win(sfb, win_no); + for (win_no = 0; win_no < sfb->variant.nr_windows - 1; win_no++) { + void __iomem *regs = sfb->regs + sfb->variant.keycon; + + regs += (win_no * 8); + writel(0xffffff, regs + WKEYCON0); + writel(0xffffff, regs + WKEYCON1); + } + /* restore framebuffers */ for (win_no = 0; win_no < S3C_FB_MAX_WIN; win_no++) { win = sfb->windows[win_no]; @@ -1036,11 +1514,208 @@ static int s3c_fb_resume(struct platform_device *pdev) #define s3c_fb_resume NULL #endif + +#define VALID_BPP124 (VALID_BPP(1) | VALID_BPP(2) | VALID_BPP(4)) +#define VALID_BPP1248 (VALID_BPP124 | VALID_BPP(8)) + +static struct s3c_fb_win_variant s3c_fb_data_64xx_wins[] = { + [0] = { + .has_osd_c = 1, + .osd_size_off = 0x8, + .palette_sz = 256, + .valid_bpp = VALID_BPP1248 | VALID_BPP(16) | VALID_BPP(24), + }, + [1] = { + .has_osd_c = 1, + .has_osd_d = 1, + .osd_size_off = 0x12, + .has_osd_alpha = 1, + .palette_sz = 256, + .valid_bpp = (VALID_BPP1248 | VALID_BPP(16) | + VALID_BPP(18) | VALID_BPP(19) | + VALID_BPP(24) | VALID_BPP(25)), + }, + [2] = { + .has_osd_c = 1, + .has_osd_d = 1, + .osd_size_off = 0x12, + .has_osd_alpha = 1, + .palette_sz = 16, + .palette_16bpp = 1, + .valid_bpp = (VALID_BPP1248 | VALID_BPP(16) | + VALID_BPP(18) | VALID_BPP(19) | + VALID_BPP(24) | VALID_BPP(25)), + }, + [3] = { + .has_osd_c = 1, + .has_osd_alpha = 1, + .palette_sz = 16, + .palette_16bpp = 1, + .valid_bpp = (VALID_BPP124 | VALID_BPP(16) | + VALID_BPP(18) | VALID_BPP(19) | + VALID_BPP(24) | VALID_BPP(25)), + }, + [4] = { + .has_osd_c = 1, + .has_osd_alpha = 1, + .palette_sz = 4, + .palette_16bpp = 1, + .valid_bpp = (VALID_BPP(1) | VALID_BPP(2) | + VALID_BPP(16) | VALID_BPP(18) | + VALID_BPP(24) | VALID_BPP(25)), + }, +}; + +static struct s3c_fb_driverdata s3c_fb_data_64xx = { + .variant = { + .nr_windows = 5, + .vidtcon = VIDTCON0, + .wincon = WINCON(0), + .winmap = WINxMAP(0), + .keycon = WKEYCON, + .osd = VIDOSD_BASE, + .osd_stride = 16, + .buf_start = VIDW_BUF_START(0), + .buf_size = VIDW_BUF_SIZE(0), + .buf_end = VIDW_BUF_END(0), + + .palette = { + [0] = 0x400, + [1] = 0x800, + [2] = 0x300, + [3] = 0x320, + [4] = 0x340, + }, + + .has_prtcon = 1, + }, + .win[0] = &s3c_fb_data_64xx_wins[0], + .win[1] = &s3c_fb_data_64xx_wins[1], + .win[2] = &s3c_fb_data_64xx_wins[2], + .win[3] = &s3c_fb_data_64xx_wins[3], + .win[4] = &s3c_fb_data_64xx_wins[4], +}; + +static struct s3c_fb_driverdata s3c_fb_data_s5pc100 = { + .variant = { + .nr_windows = 5, + .vidtcon = VIDTCON0, + .wincon = WINCON(0), + .winmap = WINxMAP(0), + .keycon = WKEYCON, + .osd = VIDOSD_BASE, + .osd_stride = 16, + .buf_start = VIDW_BUF_START(0), + .buf_size = VIDW_BUF_SIZE(0), + .buf_end = VIDW_BUF_END(0), + + .palette = { + [0] = 0x2400, + [1] = 0x2800, + [2] = 0x2c00, + [3] = 0x3000, + [4] = 0x3400, + }, + + .has_prtcon = 1, + }, + .win[0] = &s3c_fb_data_64xx_wins[0], + .win[1] = &s3c_fb_data_64xx_wins[1], + .win[2] = &s3c_fb_data_64xx_wins[2], + .win[3] = &s3c_fb_data_64xx_wins[3], + .win[4] = &s3c_fb_data_64xx_wins[4], +}; + +static struct s3c_fb_driverdata s3c_fb_data_s5pv210 = { + .variant = { + .nr_windows = 5, + .vidtcon = VIDTCON0, + .wincon = WINCON(0), + .winmap = WINxMAP(0), + .keycon = WKEYCON, + .osd = VIDOSD_BASE, + .osd_stride = 16, + .buf_start = VIDW_BUF_START(0), + .buf_size = VIDW_BUF_SIZE(0), + .buf_end = VIDW_BUF_END(0), + + .palette = { + [0] = 0x2400, + [1] = 0x2800, + [2] = 0x2c00, + [3] = 0x3000, + [4] = 0x3400, + }, + + .has_shadowcon = 1, + }, + .win[0] = &s3c_fb_data_64xx_wins[0], + .win[1] = &s3c_fb_data_64xx_wins[1], + .win[2] = &s3c_fb_data_64xx_wins[2], + .win[3] = &s3c_fb_data_64xx_wins[3], + .win[4] = &s3c_fb_data_64xx_wins[4], +}; + +/* S3C2443/S3C2416 style hardware */ +static struct s3c_fb_driverdata s3c_fb_data_s3c2443 = { + .variant = { + .nr_windows = 2, + .is_2443 = 1, + + .vidtcon = 0x08, + .wincon = 0x14, + .winmap = 0xd0, + .keycon = 0xb0, + .osd = 0x28, + .osd_stride = 12, + .buf_start = 0x64, + .buf_size = 0x94, + .buf_end = 0x7c, + + .palette = { + [0] = 0x400, + [1] = 0x800, + }, + }, + .win[0] = &(struct s3c_fb_win_variant) { + .palette_sz = 256, + .valid_bpp = VALID_BPP1248 | VALID_BPP(16) | VALID_BPP(24), + }, + .win[1] = &(struct s3c_fb_win_variant) { + .has_osd_c = 1, + .has_osd_alpha = 1, + .palette_sz = 256, + .valid_bpp = (VALID_BPP1248 | VALID_BPP(16) | + VALID_BPP(18) | VALID_BPP(19) | + VALID_BPP(24) | VALID_BPP(25) | + VALID_BPP(28)), + }, +}; + +static struct platform_device_id s3c_fb_driver_ids[] = { + { + .name = "s3c-fb", + .driver_data = (unsigned long)&s3c_fb_data_64xx, + }, { + .name = "s5pc100-fb", + .driver_data = (unsigned long)&s3c_fb_data_s5pc100, + }, { + .name = "s5pv210-fb", + .driver_data = (unsigned long)&s3c_fb_data_s5pv210, + }, { + .name = "s3c2443-fb", + .driver_data = (unsigned long)&s3c_fb_data_s3c2443, + }, + {}, +}; +MODULE_DEVICE_TABLE(platform, s3c_fb_driver_ids); + static struct platform_driver s3c_fb_driver = { .probe = s3c_fb_probe, .remove = __devexit_p(s3c_fb_remove), .suspend = s3c_fb_suspend, .resume = s3c_fb_resume, + .id_table = s3c_fb_driver_ids, .driver = { .name = "s3c-fb", .owner = THIS_MODULE, diff --git a/drivers/video/sh_mipi_dsi.c b/drivers/video/sh_mipi_dsi.c new file mode 100644 index 00000000000..5699ce0c178 --- /dev/null +++ b/drivers/video/sh_mipi_dsi.c @@ -0,0 +1,505 @@ +/* + * Renesas SH-mobile MIPI DSI support + * + * Copyright (C) 2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de> + * + * This is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/types.h> + +#include <video/mipi_display.h> +#include <video/sh_mipi_dsi.h> +#include <video/sh_mobile_lcdc.h> + +#define CMTSRTCTR 0x80d0 +#define CMTSRTREQ 0x8070 + +#define DSIINTE 0x0060 + +/* E.g., sh7372 has 2 MIPI-DSIs - one for each LCDC */ +#define MAX_SH_MIPI_DSI 2 + +struct sh_mipi { + void __iomem *base; + struct clk *dsit_clk; + struct clk *dsip_clk; +}; + +static struct sh_mipi *mipi_dsi[MAX_SH_MIPI_DSI]; + +/* Protect the above array */ +static DEFINE_MUTEX(array_lock); + +static struct sh_mipi *sh_mipi_by_handle(int handle) +{ + if (handle >= ARRAY_SIZE(mipi_dsi) || handle < 0) + return NULL; + + return mipi_dsi[handle]; +} + +static int sh_mipi_send_short(struct sh_mipi *mipi, u8 dsi_cmd, + u8 cmd, u8 param) +{ + u32 data = (dsi_cmd << 24) | (cmd << 16) | (param << 8); + int cnt = 100; + + /* transmit a short packet to LCD panel */ + iowrite32(1 | data, mipi->base + 0x80d0); /* CMTSRTCTR */ + iowrite32(1, mipi->base + 0x8070); /* CMTSRTREQ */ + + while ((ioread32(mipi->base + 0x8070) & 1) && --cnt) + udelay(1); + + return cnt ? 0 : -ETIMEDOUT; +} + +#define LCD_CHAN2MIPI(c) ((c) < LCDC_CHAN_MAINLCD || (c) > LCDC_CHAN_SUBLCD ? \ + -EINVAL : (c) - 1) + +static int sh_mipi_dcs(int handle, u8 cmd) +{ + struct sh_mipi *mipi = sh_mipi_by_handle(LCD_CHAN2MIPI(handle)); + if (!mipi) + return -ENODEV; + return sh_mipi_send_short(mipi, MIPI_DSI_DCS_SHORT_WRITE, cmd, 0); +} + +static int sh_mipi_dcs_param(int handle, u8 cmd, u8 param) +{ + struct sh_mipi *mipi = sh_mipi_by_handle(LCD_CHAN2MIPI(handle)); + if (!mipi) + return -ENODEV; + return sh_mipi_send_short(mipi, MIPI_DSI_DCS_SHORT_WRITE_PARAM, cmd, + param); +} + +static void sh_mipi_dsi_enable(struct sh_mipi *mipi, bool enable) +{ + /* + * enable LCDC data tx, transition to LPS after completion of each HS + * packet + */ + iowrite32(0x00000002 | enable, mipi->base + 0x8000); /* DTCTR */ +} + +static void sh_mipi_shutdown(struct platform_device *pdev) +{ + struct sh_mipi *mipi = platform_get_drvdata(pdev); + + sh_mipi_dsi_enable(mipi, false); +} + +static void mipi_display_on(void *arg, struct fb_info *info) +{ + struct sh_mipi *mipi = arg; + + sh_mipi_dsi_enable(mipi, true); +} + +static void mipi_display_off(void *arg) +{ + struct sh_mipi *mipi = arg; + + sh_mipi_dsi_enable(mipi, false); +} + +static int __init sh_mipi_setup(struct sh_mipi *mipi, + struct sh_mipi_dsi_info *pdata) +{ + void __iomem *base = mipi->base; + struct sh_mobile_lcdc_chan_cfg *ch = pdata->lcd_chan; + u32 pctype, datatype, pixfmt; + u32 linelength; + bool yuv; + + /* Select data format */ + switch (pdata->data_format) { + case MIPI_RGB888: + pctype = 0; + datatype = MIPI_DSI_PACKED_PIXEL_STREAM_24; + pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; + linelength = ch->lcd_cfg.xres * 3; + yuv = false; + break; + case MIPI_RGB565: + pctype = 1; + datatype = MIPI_DSI_PACKED_PIXEL_STREAM_16; + pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; + linelength = ch->lcd_cfg.xres * 2; + yuv = false; + break; + case MIPI_RGB666_LP: + pctype = 2; + datatype = MIPI_DSI_PIXEL_STREAM_3BYTE_18; + pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; + linelength = ch->lcd_cfg.xres * 3; + yuv = false; + break; + case MIPI_RGB666: + pctype = 3; + datatype = MIPI_DSI_PACKED_PIXEL_STREAM_18; + pixfmt = MIPI_DCS_PIXEL_FMT_18BIT; + linelength = (ch->lcd_cfg.xres * 18 + 7) / 8; + yuv = false; + break; + case MIPI_BGR888: + pctype = 8; + datatype = MIPI_DSI_PACKED_PIXEL_STREAM_24; + pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; + linelength = ch->lcd_cfg.xres * 3; + yuv = false; + break; + case MIPI_BGR565: + pctype = 9; + datatype = MIPI_DSI_PACKED_PIXEL_STREAM_16; + pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; + linelength = ch->lcd_cfg.xres * 2; + yuv = false; + break; + case MIPI_BGR666_LP: + pctype = 0xa; + datatype = MIPI_DSI_PIXEL_STREAM_3BYTE_18; + pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; + linelength = ch->lcd_cfg.xres * 3; + yuv = false; + break; + case MIPI_BGR666: + pctype = 0xb; + datatype = MIPI_DSI_PACKED_PIXEL_STREAM_18; + pixfmt = MIPI_DCS_PIXEL_FMT_18BIT; + linelength = (ch->lcd_cfg.xres * 18 + 7) / 8; + yuv = false; + break; + case MIPI_YUYV: + pctype = 4; + datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16; + pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; + linelength = ch->lcd_cfg.xres * 2; + yuv = true; + break; + case MIPI_UYVY: + pctype = 5; + datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16; + pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; + linelength = ch->lcd_cfg.xres * 2; + yuv = true; + break; + case MIPI_YUV420_L: + pctype = 6; + datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12; + pixfmt = MIPI_DCS_PIXEL_FMT_12BIT; + linelength = (ch->lcd_cfg.xres * 12 + 7) / 8; + yuv = true; + break; + case MIPI_YUV420: + pctype = 7; + datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12; + pixfmt = MIPI_DCS_PIXEL_FMT_12BIT; + /* Length of U/V line */ + linelength = (ch->lcd_cfg.xres + 1) / 2; + yuv = true; + break; + default: + return -EINVAL; + } + + if ((yuv && ch->interface_type != YUV422) || + (!yuv && ch->interface_type != RGB24)) + return -EINVAL; + + /* reset DSI link */ + iowrite32(0x00000001, base); /* SYSCTRL */ + /* Hold reset for 100 cycles of the slowest of bus, HS byte and LP clock */ + udelay(50); + iowrite32(0x00000000, base); /* SYSCTRL */ + + /* setup DSI link */ + + /* + * Default = ULPS enable | + * Contention detection enabled | + * EoT packet transmission enable | + * CRC check enable | + * ECC check enable + * additionally enable first two lanes + */ + iowrite32(0x00003703, base + 0x04); /* SYSCONF */ + /* + * T_wakeup = 0x7000 + * T_hs-trail = 3 + * T_hs-prepare = 3 + * T_clk-trail = 3 + * T_clk-prepare = 2 + */ + iowrite32(0x70003332, base + 0x08); /* TIMSET */ + /* no responses requested */ + iowrite32(0x00000000, base + 0x18); /* RESREQSET0 */ + /* request response to packets of type 0x28 */ + iowrite32(0x00000100, base + 0x1c); /* RESREQSET1 */ + /* High-speed transmission timeout, default 0xffffffff */ + iowrite32(0x0fffffff, base + 0x20); /* HSTTOVSET */ + /* LP reception timeout, default 0xffffffff */ + iowrite32(0x0fffffff, base + 0x24); /* LPRTOVSET */ + /* Turn-around timeout, default 0xffffffff */ + iowrite32(0x0fffffff, base + 0x28); /* TATOVSET */ + /* Peripheral reset timeout, default 0xffffffff */ + iowrite32(0x0fffffff, base + 0x2c); /* PRTOVSET */ + /* Enable timeout counters */ + iowrite32(0x00000f00, base + 0x30); /* DSICTRL */ + /* Interrupts not used, disable all */ + iowrite32(0, base + DSIINTE); + /* DSI-Tx bias on */ + iowrite32(0x00000001, base + 0x70); /* PHYCTRL */ + udelay(200); + /* Deassert resets, power on, set multiplier */ + iowrite32(0x03070b01, base + 0x70); /* PHYCTRL */ + + /* setup l-bridge */ + + /* + * Enable transmission of all packets, + * transmit LPS after each HS packet completion + */ + iowrite32(0x00000006, base + 0x8000); /* DTCTR */ + /* VSYNC width = 2 (<< 17) */ + iowrite32(0x00040000 | (pctype << 12) | datatype, base + 0x8020); /* VMCTR1 */ + /* + * Non-burst mode with sync pulses: VSE and HSE are output, + * HSA period allowed, no commands in LP + */ + iowrite32(0x00e00000, base + 0x8024); /* VMCTR2 */ + /* + * 0x660 = 1632 bytes per line (RGB24, 544 pixels: see + * sh_mobile_lcdc_info.ch[0].lcd_cfg.xres), HSALEN = 1 - default + * (unused, since VMCTR2[HSABM] = 0) + */ + iowrite32(1 | (linelength << 16), base + 0x8028); /* VMLEN1 */ + + msleep(5); + + /* setup LCD panel */ + + /* cf. drivers/video/omap/lcd_mipid.c */ + sh_mipi_dcs(ch->chan, MIPI_DCS_EXIT_SLEEP_MODE); + msleep(120); + /* + * [7] - Page Address Mode + * [6] - Column Address Mode + * [5] - Page / Column Address Mode + * [4] - Display Device Line Refresh Order + * [3] - RGB/BGR Order + * [2] - Display Data Latch Data Order + * [1] - Flip Horizontal + * [0] - Flip Vertical + */ + sh_mipi_dcs_param(ch->chan, MIPI_DCS_SET_ADDRESS_MODE, 0x00); + /* cf. set_data_lines() */ + sh_mipi_dcs_param(ch->chan, MIPI_DCS_SET_PIXEL_FORMAT, + pixfmt << 4); + sh_mipi_dcs(ch->chan, MIPI_DCS_SET_DISPLAY_ON); + + return 0; +} + +static int __init sh_mipi_probe(struct platform_device *pdev) +{ + struct sh_mipi *mipi; + struct sh_mipi_dsi_info *pdata = pdev->dev.platform_data; + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + unsigned long rate, f_current; + int idx = pdev->id, ret; + char dsip_clk[] = "dsi.p_clk"; + + if (!res || idx >= ARRAY_SIZE(mipi_dsi) || !pdata) + return -ENODEV; + + mutex_lock(&array_lock); + if (idx < 0) + for (idx = 0; idx < ARRAY_SIZE(mipi_dsi) && mipi_dsi[idx]; idx++) + ; + + if (idx == ARRAY_SIZE(mipi_dsi)) { + ret = -EBUSY; + goto efindslot; + } + + mipi = kzalloc(sizeof(*mipi), GFP_KERNEL); + if (!mipi) { + ret = -ENOMEM; + goto ealloc; + } + + if (!request_mem_region(res->start, resource_size(res), pdev->name)) { + dev_err(&pdev->dev, "MIPI register region already claimed\n"); + ret = -EBUSY; + goto ereqreg; + } + + mipi->base = ioremap(res->start, resource_size(res)); + if (!mipi->base) { + ret = -ENOMEM; + goto emap; + } + + mipi->dsit_clk = clk_get(&pdev->dev, "dsit_clk"); + if (IS_ERR(mipi->dsit_clk)) { + ret = PTR_ERR(mipi->dsit_clk); + goto eclktget; + } + + f_current = clk_get_rate(mipi->dsit_clk); + /* 80MHz required by the datasheet */ + rate = clk_round_rate(mipi->dsit_clk, 80000000); + if (rate > 0 && rate != f_current) + ret = clk_set_rate(mipi->dsit_clk, rate); + else + ret = rate; + if (ret < 0) + goto esettrate; + + dev_dbg(&pdev->dev, "DSI-T clk %lu -> %lu\n", f_current, rate); + + sprintf(dsip_clk, "dsi%1.1dp_clk", idx); + mipi->dsip_clk = clk_get(&pdev->dev, dsip_clk); + if (IS_ERR(mipi->dsip_clk)) { + ret = PTR_ERR(mipi->dsip_clk); + goto eclkpget; + } + + f_current = clk_get_rate(mipi->dsip_clk); + /* Between 10 and 50MHz */ + rate = clk_round_rate(mipi->dsip_clk, 24000000); + if (rate > 0 && rate != f_current) + ret = clk_set_rate(mipi->dsip_clk, rate); + else + ret = rate; + if (ret < 0) + goto esetprate; + + dev_dbg(&pdev->dev, "DSI-P clk %lu -> %lu\n", f_current, rate); + + msleep(10); + + ret = clk_enable(mipi->dsit_clk); + if (ret < 0) + goto eclkton; + + ret = clk_enable(mipi->dsip_clk); + if (ret < 0) + goto eclkpon; + + mipi_dsi[idx] = mipi; + + ret = sh_mipi_setup(mipi, pdata); + if (ret < 0) + goto emipisetup; + + mutex_unlock(&array_lock); + platform_set_drvdata(pdev, mipi); + + /* Set up LCDC callbacks */ + pdata->lcd_chan->board_cfg.board_data = mipi; + pdata->lcd_chan->board_cfg.display_on = mipi_display_on; + pdata->lcd_chan->board_cfg.display_off = mipi_display_off; + + return 0; + +emipisetup: + mipi_dsi[idx] = NULL; + clk_disable(mipi->dsip_clk); +eclkpon: + clk_disable(mipi->dsit_clk); +eclkton: +esetprate: + clk_put(mipi->dsip_clk); +eclkpget: +esettrate: + clk_put(mipi->dsit_clk); +eclktget: + iounmap(mipi->base); +emap: + release_mem_region(res->start, resource_size(res)); +ereqreg: + kfree(mipi); +ealloc: +efindslot: + mutex_unlock(&array_lock); + + return ret; +} + +static int __exit sh_mipi_remove(struct platform_device *pdev) +{ + struct sh_mipi_dsi_info *pdata = pdev->dev.platform_data; + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct sh_mipi *mipi = platform_get_drvdata(pdev); + int i, ret; + + mutex_lock(&array_lock); + + for (i = 0; i < ARRAY_SIZE(mipi_dsi) && mipi_dsi[i] != mipi; i++) + ; + + if (i == ARRAY_SIZE(mipi_dsi)) { + ret = -EINVAL; + } else { + ret = 0; + mipi_dsi[i] = NULL; + } + + mutex_unlock(&array_lock); + + if (ret < 0) + return ret; + + pdata->lcd_chan->board_cfg.display_on = NULL; + pdata->lcd_chan->board_cfg.display_off = NULL; + pdata->lcd_chan->board_cfg.board_data = NULL; + + clk_disable(mipi->dsip_clk); + clk_disable(mipi->dsit_clk); + clk_put(mipi->dsit_clk); + clk_put(mipi->dsip_clk); + iounmap(mipi->base); + if (res) + release_mem_region(res->start, resource_size(res)); + platform_set_drvdata(pdev, NULL); + kfree(mipi); + + return 0; +} + +static struct platform_driver sh_mipi_driver = { + .remove = __exit_p(sh_mipi_remove), + .shutdown = sh_mipi_shutdown, + .driver = { + .name = "sh-mipi-dsi", + }, +}; + +static int __init sh_mipi_init(void) +{ + return platform_driver_probe(&sh_mipi_driver, sh_mipi_probe); +} +module_init(sh_mipi_init); + +static void __exit sh_mipi_exit(void) +{ + platform_driver_unregister(&sh_mipi_driver); +} +module_exit(sh_mipi_exit); + +MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); +MODULE_DESCRIPTION("SuperH / ARM-shmobile MIPI DSI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/video/sh_mobile_hdmi.c b/drivers/video/sh_mobile_hdmi.c new file mode 100644 index 00000000000..2fde08cc66b --- /dev/null +++ b/drivers/video/sh_mobile_hdmi.c @@ -0,0 +1,1028 @@ +/* + * SH-Mobile High-Definition Multimedia Interface (HDMI) driver + * for SLISHDMI13T and SLIPHDMIT IP cores + * + * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de> + * + * 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/clk.h> +#include <linux/console.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/workqueue.h> + +#include <video/sh_mobile_hdmi.h> +#include <video/sh_mobile_lcdc.h> + +#define HDMI_SYSTEM_CTRL 0x00 /* System control */ +#define HDMI_L_R_DATA_SWAP_CTRL_RPKT 0x01 /* L/R data swap control, + bits 19..16 of 20-bit N for Audio Clock Regeneration packet */ +#define HDMI_20_BIT_N_FOR_AUDIO_RPKT_15_8 0x02 /* bits 15..8 of 20-bit N for Audio Clock Regeneration packet */ +#define HDMI_20_BIT_N_FOR_AUDIO_RPKT_7_0 0x03 /* bits 7..0 of 20-bit N for Audio Clock Regeneration packet */ +#define HDMI_SPDIF_AUDIO_SAMP_FREQ_CTS 0x04 /* SPDIF audio sampling frequency, + bits 19..16 of Internal CTS */ +#define HDMI_INTERNAL_CTS_15_8 0x05 /* bits 15..8 of Internal CTS */ +#define HDMI_INTERNAL_CTS_7_0 0x06 /* bits 7..0 of Internal CTS */ +#define HDMI_EXTERNAL_CTS_19_16 0x07 /* External CTS */ +#define HDMI_EXTERNAL_CTS_15_8 0x08 /* External CTS */ +#define HDMI_EXTERNAL_CTS_7_0 0x09 /* External CTS */ +#define HDMI_AUDIO_SETTING_1 0x0A /* Audio setting.1 */ +#define HDMI_AUDIO_SETTING_2 0x0B /* Audio setting.2 */ +#define HDMI_I2S_AUDIO_SET 0x0C /* I2S audio setting */ +#define HDMI_DSD_AUDIO_SET 0x0D /* DSD audio setting */ +#define HDMI_DEBUG_MONITOR_1 0x0E /* Debug monitor.1 */ +#define HDMI_DEBUG_MONITOR_2 0x0F /* Debug monitor.2 */ +#define HDMI_I2S_INPUT_PIN_SWAP 0x10 /* I2S input pin swap */ +#define HDMI_AUDIO_STATUS_BITS_SETTING_1 0x11 /* Audio status bits setting.1 */ +#define HDMI_AUDIO_STATUS_BITS_SETTING_2 0x12 /* Audio status bits setting.2 */ +#define HDMI_CATEGORY_CODE 0x13 /* Category code */ +#define HDMI_SOURCE_NUM_AUDIO_WORD_LEN 0x14 /* Source number/Audio word length */ +#define HDMI_AUDIO_VIDEO_SETTING_1 0x15 /* Audio/Video setting.1 */ +#define HDMI_VIDEO_SETTING_1 0x16 /* Video setting.1 */ +#define HDMI_DEEP_COLOR_MODES 0x17 /* Deep Color Modes */ + +/* 12 16- and 10-bit Color space conversion parameters: 0x18..0x2f */ +#define HDMI_COLOR_SPACE_CONVERSION_PARAMETERS 0x18 + +#define HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS 0x30 /* External video parameter settings */ +#define HDMI_EXTERNAL_H_TOTAL_7_0 0x31 /* External horizontal total (LSB) */ +#define HDMI_EXTERNAL_H_TOTAL_11_8 0x32 /* External horizontal total (MSB) */ +#define HDMI_EXTERNAL_H_BLANK_7_0 0x33 /* External horizontal blank (LSB) */ +#define HDMI_EXTERNAL_H_BLANK_9_8 0x34 /* External horizontal blank (MSB) */ +#define HDMI_EXTERNAL_H_DELAY_7_0 0x35 /* External horizontal delay (LSB) */ +#define HDMI_EXTERNAL_H_DELAY_9_8 0x36 /* External horizontal delay (MSB) */ +#define HDMI_EXTERNAL_H_DURATION_7_0 0x37 /* External horizontal duration (LSB) */ +#define HDMI_EXTERNAL_H_DURATION_9_8 0x38 /* External horizontal duration (MSB) */ +#define HDMI_EXTERNAL_V_TOTAL_7_0 0x39 /* External vertical total (LSB) */ +#define HDMI_EXTERNAL_V_TOTAL_9_8 0x3A /* External vertical total (MSB) */ +#define HDMI_AUDIO_VIDEO_SETTING_2 0x3B /* Audio/Video setting.2 */ +#define HDMI_EXTERNAL_V_BLANK 0x3D /* External vertical blank */ +#define HDMI_EXTERNAL_V_DELAY 0x3E /* External vertical delay */ +#define HDMI_EXTERNAL_V_DURATION 0x3F /* External vertical duration */ +#define HDMI_CTRL_PKT_MANUAL_SEND_CONTROL 0x40 /* Control packet manual send control */ +#define HDMI_CTRL_PKT_AUTO_SEND 0x41 /* Control packet auto send with VSYNC control */ +#define HDMI_AUTO_CHECKSUM_OPTION 0x42 /* Auto checksum option */ +#define HDMI_VIDEO_SETTING_2 0x45 /* Video setting.2 */ +#define HDMI_OUTPUT_OPTION 0x46 /* Output option */ +#define HDMI_SLIPHDMIT_PARAM_OPTION 0x51 /* SLIPHDMIT parameter option */ +#define HDMI_HSYNC_PMENT_AT_EMB_7_0 0x52 /* HSYNC placement at embedded sync (LSB) */ +#define HDMI_HSYNC_PMENT_AT_EMB_15_8 0x53 /* HSYNC placement at embedded sync (MSB) */ +#define HDMI_VSYNC_PMENT_AT_EMB_7_0 0x54 /* VSYNC placement at embedded sync (LSB) */ +#define HDMI_VSYNC_PMENT_AT_EMB_14_8 0x55 /* VSYNC placement at embedded sync (MSB) */ +#define HDMI_SLIPHDMIT_PARAM_SETTINGS_1 0x56 /* SLIPHDMIT parameter settings.1 */ +#define HDMI_SLIPHDMIT_PARAM_SETTINGS_2 0x57 /* SLIPHDMIT parameter settings.2 */ +#define HDMI_SLIPHDMIT_PARAM_SETTINGS_3 0x58 /* SLIPHDMIT parameter settings.3 */ +#define HDMI_SLIPHDMIT_PARAM_SETTINGS_5 0x59 /* SLIPHDMIT parameter settings.5 */ +#define HDMI_SLIPHDMIT_PARAM_SETTINGS_6 0x5A /* SLIPHDMIT parameter settings.6 */ +#define HDMI_SLIPHDMIT_PARAM_SETTINGS_7 0x5B /* SLIPHDMIT parameter settings.7 */ +#define HDMI_SLIPHDMIT_PARAM_SETTINGS_8 0x5C /* SLIPHDMIT parameter settings.8 */ +#define HDMI_SLIPHDMIT_PARAM_SETTINGS_9 0x5D /* SLIPHDMIT parameter settings.9 */ +#define HDMI_SLIPHDMIT_PARAM_SETTINGS_10 0x5E /* SLIPHDMIT parameter settings.10 */ +#define HDMI_CTRL_PKT_BUF_INDEX 0x5F /* Control packet buffer index */ +#define HDMI_CTRL_PKT_BUF_ACCESS_HB0 0x60 /* Control packet data buffer access window - HB0 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_HB1 0x61 /* Control packet data buffer access window - HB1 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_HB2 0x62 /* Control packet data buffer access window - HB2 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB0 0x63 /* Control packet data buffer access window - PB0 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB1 0x64 /* Control packet data buffer access window - PB1 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB2 0x65 /* Control packet data buffer access window - PB2 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB3 0x66 /* Control packet data buffer access window - PB3 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB4 0x67 /* Control packet data buffer access window - PB4 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB5 0x68 /* Control packet data buffer access window - PB5 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB6 0x69 /* Control packet data buffer access window - PB6 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB7 0x6A /* Control packet data buffer access window - PB7 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB8 0x6B /* Control packet data buffer access window - PB8 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB9 0x6C /* Control packet data buffer access window - PB9 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB10 0x6D /* Control packet data buffer access window - PB10 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB11 0x6E /* Control packet data buffer access window - PB11 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB12 0x6F /* Control packet data buffer access window - PB12 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB13 0x70 /* Control packet data buffer access window - PB13 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB14 0x71 /* Control packet data buffer access window - PB14 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB15 0x72 /* Control packet data buffer access window - PB15 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB16 0x73 /* Control packet data buffer access window - PB16 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB17 0x74 /* Control packet data buffer access window - PB17 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB18 0x75 /* Control packet data buffer access window - PB18 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB19 0x76 /* Control packet data buffer access window - PB19 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB20 0x77 /* Control packet data buffer access window - PB20 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB21 0x78 /* Control packet data buffer access window - PB21 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB22 0x79 /* Control packet data buffer access window - PB22 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB23 0x7A /* Control packet data buffer access window - PB23 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB24 0x7B /* Control packet data buffer access window - PB24 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB25 0x7C /* Control packet data buffer access window - PB25 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB26 0x7D /* Control packet data buffer access window - PB26 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB27 0x7E /* Control packet data buffer access window - PB27 */ +#define HDMI_EDID_KSV_FIFO_ACCESS_WINDOW 0x80 /* EDID/KSV FIFO access window */ +#define HDMI_DDC_BUS_ACCESS_FREQ_CTRL_7_0 0x81 /* DDC bus access frequency control (LSB) */ +#define HDMI_DDC_BUS_ACCESS_FREQ_CTRL_15_8 0x82 /* DDC bus access frequency control (MSB) */ +#define HDMI_INTERRUPT_MASK_1 0x92 /* Interrupt mask.1 */ +#define HDMI_INTERRUPT_MASK_2 0x93 /* Interrupt mask.2 */ +#define HDMI_INTERRUPT_STATUS_1 0x94 /* Interrupt status.1 */ +#define HDMI_INTERRUPT_STATUS_2 0x95 /* Interrupt status.2 */ +#define HDMI_INTERRUPT_MASK_3 0x96 /* Interrupt mask.3 */ +#define HDMI_INTERRUPT_MASK_4 0x97 /* Interrupt mask.4 */ +#define HDMI_INTERRUPT_STATUS_3 0x98 /* Interrupt status.3 */ +#define HDMI_INTERRUPT_STATUS_4 0x99 /* Interrupt status.4 */ +#define HDMI_SOFTWARE_HDCP_CONTROL_1 0x9A /* Software HDCP control.1 */ +#define HDMI_FRAME_COUNTER 0x9C /* Frame counter */ +#define HDMI_FRAME_COUNTER_FOR_RI_CHECK 0x9D /* Frame counter for Ri check */ +#define HDMI_HDCP_CONTROL 0xAF /* HDCP control */ +#define HDMI_RI_FRAME_COUNT_REGISTER 0xB2 /* Ri frame count register */ +#define HDMI_DDC_BUS_CONTROL 0xB7 /* DDC bus control */ +#define HDMI_HDCP_STATUS 0xB8 /* HDCP status */ +#define HDMI_SHA0 0xB9 /* sha0 */ +#define HDMI_SHA1 0xBA /* sha1 */ +#define HDMI_SHA2 0xBB /* sha2 */ +#define HDMI_SHA3 0xBC /* sha3 */ +#define HDMI_SHA4 0xBD /* sha4 */ +#define HDMI_BCAPS_READ 0xBE /* BCAPS read / debug */ +#define HDMI_AKSV_BKSV_7_0_MONITOR 0xBF /* AKSV/BKSV[7:0] monitor */ +#define HDMI_AKSV_BKSV_15_8_MONITOR 0xC0 /* AKSV/BKSV[15:8] monitor */ +#define HDMI_AKSV_BKSV_23_16_MONITOR 0xC1 /* AKSV/BKSV[23:16] monitor */ +#define HDMI_AKSV_BKSV_31_24_MONITOR 0xC2 /* AKSV/BKSV[31:24] monitor */ +#define HDMI_AKSV_BKSV_39_32_MONITOR 0xC3 /* AKSV/BKSV[39:32] monitor */ +#define HDMI_EDID_SEGMENT_POINTER 0xC4 /* EDID segment pointer */ +#define HDMI_EDID_WORD_ADDRESS 0xC5 /* EDID word address */ +#define HDMI_EDID_DATA_FIFO_ADDRESS 0xC6 /* EDID data FIFO address */ +#define HDMI_NUM_OF_HDMI_DEVICES 0xC7 /* Number of HDMI devices */ +#define HDMI_HDCP_ERROR_CODE 0xC8 /* HDCP error code */ +#define HDMI_100MS_TIMER_SET 0xC9 /* 100ms timer setting */ +#define HDMI_5SEC_TIMER_SET 0xCA /* 5sec timer setting */ +#define HDMI_RI_READ_COUNT 0xCB /* Ri read count */ +#define HDMI_AN_SEED 0xCC /* An seed */ +#define HDMI_MAX_NUM_OF_RCIVRS_ALLOWED 0xCD /* Maximum number of receivers allowed */ +#define HDMI_HDCP_MEMORY_ACCESS_CONTROL_1 0xCE /* HDCP memory access control.1 */ +#define HDMI_HDCP_MEMORY_ACCESS_CONTROL_2 0xCF /* HDCP memory access control.2 */ +#define HDMI_HDCP_CONTROL_2 0xD0 /* HDCP Control 2 */ +#define HDMI_HDCP_KEY_MEMORY_CONTROL 0xD2 /* HDCP Key Memory Control */ +#define HDMI_COLOR_SPACE_CONV_CONFIG_1 0xD3 /* Color space conversion configuration.1 */ +#define HDMI_VIDEO_SETTING_3 0xD4 /* Video setting.3 */ +#define HDMI_RI_7_0 0xD5 /* Ri[7:0] */ +#define HDMI_RI_15_8 0xD6 /* Ri[15:8] */ +#define HDMI_PJ 0xD7 /* Pj */ +#define HDMI_SHA_RD 0xD8 /* sha_rd */ +#define HDMI_RI_7_0_SAVED 0xD9 /* Ri[7:0] saved */ +#define HDMI_RI_15_8_SAVED 0xDA /* Ri[15:8] saved */ +#define HDMI_PJ_SAVED 0xDB /* Pj saved */ +#define HDMI_NUM_OF_DEVICES 0xDC /* Number of devices */ +#define HDMI_HOT_PLUG_MSENS_STATUS 0xDF /* Hot plug/MSENS status */ +#define HDMI_BCAPS_WRITE 0xE0 /* bcaps */ +#define HDMI_BSTAT_7_0 0xE1 /* bstat[7:0] */ +#define HDMI_BSTAT_15_8 0xE2 /* bstat[15:8] */ +#define HDMI_BKSV_7_0 0xE3 /* bksv[7:0] */ +#define HDMI_BKSV_15_8 0xE4 /* bksv[15:8] */ +#define HDMI_BKSV_23_16 0xE5 /* bksv[23:16] */ +#define HDMI_BKSV_31_24 0xE6 /* bksv[31:24] */ +#define HDMI_BKSV_39_32 0xE7 /* bksv[39:32] */ +#define HDMI_AN_7_0 0xE8 /* An[7:0] */ +#define HDMI_AN_15_8 0xE9 /* An [15:8] */ +#define HDMI_AN_23_16 0xEA /* An [23:16] */ +#define HDMI_AN_31_24 0xEB /* An [31:24] */ +#define HDMI_AN_39_32 0xEC /* An [39:32] */ +#define HDMI_AN_47_40 0xED /* An [47:40] */ +#define HDMI_AN_55_48 0xEE /* An [55:48] */ +#define HDMI_AN_63_56 0xEF /* An [63:56] */ +#define HDMI_PRODUCT_ID 0xF0 /* Product ID */ +#define HDMI_REVISION_ID 0xF1 /* Revision ID */ +#define HDMI_TEST_MODE 0xFE /* Test mode */ + +enum hotplug_state { + HDMI_HOTPLUG_DISCONNECTED, + HDMI_HOTPLUG_CONNECTED, + HDMI_HOTPLUG_EDID_DONE, +}; + +struct sh_hdmi { + void __iomem *base; + enum hotplug_state hp_state; + struct clk *hdmi_clk; + struct device *dev; + struct fb_info *info; + struct delayed_work edid_work; + struct fb_var_screeninfo var; +}; + +static void hdmi_write(struct sh_hdmi *hdmi, u8 data, u8 reg) +{ + iowrite8(data, hdmi->base + reg); +} + +static u8 hdmi_read(struct sh_hdmi *hdmi, u8 reg) +{ + return ioread8(hdmi->base + reg); +} + +/* External video parameter settings */ +static void hdmi_external_video_param(struct sh_hdmi *hdmi) +{ + struct fb_var_screeninfo *var = &hdmi->var; + u16 htotal, hblank, hdelay, vtotal, vblank, vdelay, voffset; + u8 sync = 0; + + htotal = var->xres + var->right_margin + var->left_margin + var->hsync_len; + + hdelay = var->hsync_len + var->left_margin; + hblank = var->right_margin + hdelay; + + /* + * Vertical timing looks a bit different in Figure 18, + * but let's try the same first by setting offset = 0 + */ + vtotal = var->yres + var->upper_margin + var->lower_margin + var->vsync_len; + + vdelay = var->vsync_len + var->upper_margin; + vblank = var->lower_margin + vdelay; + voffset = min(var->upper_margin / 2, 6U); + + /* + * [3]: VSYNC polarity: Positive + * [2]: HSYNC polarity: Positive + * [1]: Interlace/Progressive: Progressive + * [0]: External video settings enable: used. + */ + if (var->sync & FB_SYNC_HOR_HIGH_ACT) + sync |= 4; + if (var->sync & FB_SYNC_VERT_HIGH_ACT) + sync |= 8; + + pr_debug("H: %u, %u, %u, %u; V: %u, %u, %u, %u; sync 0x%x\n", + htotal, hblank, hdelay, var->hsync_len, + vtotal, vblank, vdelay, var->vsync_len, sync); + + hdmi_write(hdmi, sync | (voffset << 4), HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS); + + hdmi_write(hdmi, htotal, HDMI_EXTERNAL_H_TOTAL_7_0); + hdmi_write(hdmi, htotal >> 8, HDMI_EXTERNAL_H_TOTAL_11_8); + + hdmi_write(hdmi, hblank, HDMI_EXTERNAL_H_BLANK_7_0); + hdmi_write(hdmi, hblank >> 8, HDMI_EXTERNAL_H_BLANK_9_8); + + hdmi_write(hdmi, hdelay, HDMI_EXTERNAL_H_DELAY_7_0); + hdmi_write(hdmi, hdelay >> 8, HDMI_EXTERNAL_H_DELAY_9_8); + + hdmi_write(hdmi, var->hsync_len, HDMI_EXTERNAL_H_DURATION_7_0); + hdmi_write(hdmi, var->hsync_len >> 8, HDMI_EXTERNAL_H_DURATION_9_8); + + hdmi_write(hdmi, vtotal, HDMI_EXTERNAL_V_TOTAL_7_0); + hdmi_write(hdmi, vtotal >> 8, HDMI_EXTERNAL_V_TOTAL_9_8); + + hdmi_write(hdmi, vblank, HDMI_EXTERNAL_V_BLANK); + + hdmi_write(hdmi, vdelay, HDMI_EXTERNAL_V_DELAY); + + hdmi_write(hdmi, var->vsync_len, HDMI_EXTERNAL_V_DURATION); + + /* Set bit 0 of HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS here for manual mode */ +} + +/** + * sh_hdmi_video_config() + */ +static void sh_hdmi_video_config(struct sh_hdmi *hdmi) +{ + /* + * [7:4]: Audio sampling frequency: 48kHz + * [3:1]: Input video format: RGB and YCbCr 4:4:4 (Y on Green) + * [0]: Internal/External DE select: internal + */ + hdmi_write(hdmi, 0x20, HDMI_AUDIO_VIDEO_SETTING_1); + + /* + * [7:6]: Video output format: RGB 4:4:4 + * [5:4]: Input video data width: 8 bit + * [3:1]: EAV/SAV location: channel 1 + * [0]: Video input color space: RGB + */ + hdmi_write(hdmi, 0x34, HDMI_VIDEO_SETTING_1); + + /* + * [7:6]: Together with bit [6] of HDMI_AUDIO_VIDEO_SETTING_2, which is + * left at 0 by default, this configures 24bpp and sets the Color Depth + * (CD) field in the General Control Packet + */ + hdmi_write(hdmi, 0x20, HDMI_DEEP_COLOR_MODES); +} + +/** + * sh_hdmi_audio_config() + */ +static void sh_hdmi_audio_config(struct sh_hdmi *hdmi) +{ + /* + * [7:4] L/R data swap control + * [3:0] appropriate N[19:16] + */ + hdmi_write(hdmi, 0x00, HDMI_L_R_DATA_SWAP_CTRL_RPKT); + /* appropriate N[15:8] */ + hdmi_write(hdmi, 0x18, HDMI_20_BIT_N_FOR_AUDIO_RPKT_15_8); + /* appropriate N[7:0] */ + hdmi_write(hdmi, 0x00, HDMI_20_BIT_N_FOR_AUDIO_RPKT_7_0); + + /* [7:4] 48 kHz SPDIF not used */ + hdmi_write(hdmi, 0x20, HDMI_SPDIF_AUDIO_SAMP_FREQ_CTS); + + /* + * [6:5] set required down sampling rate if required + * [4:3] set required audio source + */ + hdmi_write(hdmi, 0x00, HDMI_AUDIO_SETTING_1); + + /* [3:0] set sending channel number for channel status */ + hdmi_write(hdmi, 0x40, HDMI_AUDIO_SETTING_2); + + /* + * [5:2] set valid I2S source input pin + * [1:0] set input I2S source mode + */ + hdmi_write(hdmi, 0x04, HDMI_I2S_AUDIO_SET); + + /* [7:4] set valid DSD source input pin */ + hdmi_write(hdmi, 0x00, HDMI_DSD_AUDIO_SET); + + /* [7:0] set appropriate I2S input pin swap settings if required */ + hdmi_write(hdmi, 0x00, HDMI_I2S_INPUT_PIN_SWAP); + + /* + * [7] set validity bit for channel status + * [3:0] set original sample frequency for channel status + */ + hdmi_write(hdmi, 0x00, HDMI_AUDIO_STATUS_BITS_SETTING_1); + + /* + * [7] set value for channel status + * [6] set value for channel status + * [5] set copyright bit for channel status + * [4:2] set additional information for channel status + * [1:0] set clock accuracy for channel status + */ + hdmi_write(hdmi, 0x00, HDMI_AUDIO_STATUS_BITS_SETTING_2); + + /* [7:0] set category code for channel status */ + hdmi_write(hdmi, 0x00, HDMI_CATEGORY_CODE); + + /* + * [7:4] set source number for channel status + * [3:0] set word length for channel status + */ + hdmi_write(hdmi, 0x00, HDMI_SOURCE_NUM_AUDIO_WORD_LEN); + + /* [7:4] set sample frequency for channel status */ + hdmi_write(hdmi, 0x20, HDMI_AUDIO_VIDEO_SETTING_1); +} + +/** + * sh_hdmi_phy_config() + */ +static void sh_hdmi_phy_config(struct sh_hdmi *hdmi) +{ + /* 720p, 8bit, 74.25MHz. Might need to be adjusted for other formats */ + hdmi_write(hdmi, 0x19, HDMI_SLIPHDMIT_PARAM_SETTINGS_1); + hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_2); + hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_3); + /* PLLA_CONFIG[7:0]: VCO gain, VCO offset, LPF resistance[0] */ + hdmi_write(hdmi, 0x44, HDMI_SLIPHDMIT_PARAM_SETTINGS_5); + hdmi_write(hdmi, 0x32, HDMI_SLIPHDMIT_PARAM_SETTINGS_6); + hdmi_write(hdmi, 0x4A, HDMI_SLIPHDMIT_PARAM_SETTINGS_7); + hdmi_write(hdmi, 0x0E, HDMI_SLIPHDMIT_PARAM_SETTINGS_8); + hdmi_write(hdmi, 0x25, HDMI_SLIPHDMIT_PARAM_SETTINGS_9); + hdmi_write(hdmi, 0x04, HDMI_SLIPHDMIT_PARAM_SETTINGS_10); +} + +/** + * sh_hdmi_avi_infoframe_setup() - Auxiliary Video Information InfoFrame CONTROL PACKET + */ +static void sh_hdmi_avi_infoframe_setup(struct sh_hdmi *hdmi) +{ + /* AVI InfoFrame */ + hdmi_write(hdmi, 0x06, HDMI_CTRL_PKT_BUF_INDEX); + + /* Packet Type = 0x82 */ + hdmi_write(hdmi, 0x82, HDMI_CTRL_PKT_BUF_ACCESS_HB0); + + /* Version = 0x02 */ + hdmi_write(hdmi, 0x02, HDMI_CTRL_PKT_BUF_ACCESS_HB1); + + /* Length = 13 (0x0D) */ + hdmi_write(hdmi, 0x0D, HDMI_CTRL_PKT_BUF_ACCESS_HB2); + + /* N. A. Checksum */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0); + + /* + * Y = RGB + * A0 = No Data + * B = Bar Data not valid + * S = No Data + */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB1); + + /* + * C = No Data + * M = 16:9 Picture Aspect Ratio + * R = Same as picture aspect ratio + */ + hdmi_write(hdmi, 0x28, HDMI_CTRL_PKT_BUF_ACCESS_PB2); + + /* + * ITC = No Data + * EC = xvYCC601 + * Q = Default (depends on video format) + * SC = No Known non_uniform Scaling + */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB3); + + /* + * VIC = 1280 x 720p: ignored if external config is used + * Send 2 for 720 x 480p, 16 for 1080p + */ + hdmi_write(hdmi, 4, HDMI_CTRL_PKT_BUF_ACCESS_PB4); + + /* PR = No Repetition */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB5); + + /* Line Number of End of Top Bar (lower 8 bits) */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB6); + + /* Line Number of End of Top Bar (upper 8 bits) */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB7); + + /* Line Number of Start of Bottom Bar (lower 8 bits) */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB8); + + /* Line Number of Start of Bottom Bar (upper 8 bits) */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB9); + + /* Pixel Number of End of Left Bar (lower 8 bits) */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB10); + + /* Pixel Number of End of Left Bar (upper 8 bits) */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB11); + + /* Pixel Number of Start of Right Bar (lower 8 bits) */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB12); + + /* Pixel Number of Start of Right Bar (upper 8 bits) */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB13); +} + +/** + * sh_hdmi_audio_infoframe_setup() - Audio InfoFrame of CONTROL PACKET + */ +static void sh_hdmi_audio_infoframe_setup(struct sh_hdmi *hdmi) +{ + /* Audio InfoFrame */ + hdmi_write(hdmi, 0x08, HDMI_CTRL_PKT_BUF_INDEX); + + /* Packet Type = 0x84 */ + hdmi_write(hdmi, 0x84, HDMI_CTRL_PKT_BUF_ACCESS_HB0); + + /* Version Number = 0x01 */ + hdmi_write(hdmi, 0x01, HDMI_CTRL_PKT_BUF_ACCESS_HB1); + + /* 0 Length = 10 (0x0A) */ + hdmi_write(hdmi, 0x0A, HDMI_CTRL_PKT_BUF_ACCESS_HB2); + + /* n. a. Checksum */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0); + + /* Audio Channel Count = Refer to Stream Header */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB1); + + /* Refer to Stream Header */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB2); + + /* Format depends on coding type (i.e. CT0...CT3) */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB3); + + /* Speaker Channel Allocation = Front Right + Front Left */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB4); + + /* Level Shift Value = 0 dB, Down - mix is permitted or no information */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB5); + + /* Reserved (0) */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB6); + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB7); + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB8); + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB9); + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB10); +} + +/** + * sh_hdmi_gamut_metadata_setup() - Gamut Metadata Packet of CONTROL PACKET + */ +static void sh_hdmi_gamut_metadata_setup(struct sh_hdmi *hdmi) +{ + int i; + + /* Gamut Metadata Packet */ + hdmi_write(hdmi, 0x04, HDMI_CTRL_PKT_BUF_INDEX); + + /* Packet Type = 0x0A */ + hdmi_write(hdmi, 0x0A, HDMI_CTRL_PKT_BUF_ACCESS_HB0); + /* Gamut Packet is not used, so default value */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB1); + /* Gamut Packet is not used, so default value */ + hdmi_write(hdmi, 0x10, HDMI_CTRL_PKT_BUF_ACCESS_HB2); + + /* GBD bytes 0 through 27 */ + for (i = 0; i <= 27; i++) + /* HDMI_CTRL_PKT_BUF_ACCESS_PB0_63H - PB27_7EH */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0 + i); +} + +/** + * sh_hdmi_acp_setup() - Audio Content Protection Packet (ACP) + */ +static void sh_hdmi_acp_setup(struct sh_hdmi *hdmi) +{ + int i; + + /* Audio Content Protection Packet (ACP) */ + hdmi_write(hdmi, 0x01, HDMI_CTRL_PKT_BUF_INDEX); + + /* Packet Type = 0x04 */ + hdmi_write(hdmi, 0x04, HDMI_CTRL_PKT_BUF_ACCESS_HB0); + /* ACP_Type */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB1); + /* Reserved (0) */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB2); + + /* GBD bytes 0 through 27 */ + for (i = 0; i <= 27; i++) + /* HDMI_CTRL_PKT_BUF_ACCESS_PB0 - PB27 */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0 + i); +} + +/** + * sh_hdmi_isrc1_setup() - ISRC1 Packet + */ +static void sh_hdmi_isrc1_setup(struct sh_hdmi *hdmi) +{ + int i; + + /* ISRC1 Packet */ + hdmi_write(hdmi, 0x02, HDMI_CTRL_PKT_BUF_INDEX); + + /* Packet Type = 0x05 */ + hdmi_write(hdmi, 0x05, HDMI_CTRL_PKT_BUF_ACCESS_HB0); + /* ISRC_Cont, ISRC_Valid, Reserved (0), ISRC_Status */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB1); + /* Reserved (0) */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB2); + + /* PB0 UPC_EAN_ISRC_0-15 */ + /* Bytes PB16-PB27 shall be set to a value of 0. */ + for (i = 0; i <= 27; i++) + /* HDMI_CTRL_PKT_BUF_ACCESS_PB0 - PB27 */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0 + i); +} + +/** + * sh_hdmi_isrc2_setup() - ISRC2 Packet + */ +static void sh_hdmi_isrc2_setup(struct sh_hdmi *hdmi) +{ + int i; + + /* ISRC2 Packet */ + hdmi_write(hdmi, 0x03, HDMI_CTRL_PKT_BUF_INDEX); + + /* HB0 Packet Type = 0x06 */ + hdmi_write(hdmi, 0x06, HDMI_CTRL_PKT_BUF_ACCESS_HB0); + /* Reserved (0) */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB1); + /* Reserved (0) */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB2); + + /* PB0 UPC_EAN_ISRC_16-31 */ + /* Bytes PB16-PB27 shall be set to a value of 0. */ + for (i = 0; i <= 27; i++) + /* HDMI_CTRL_PKT_BUF_ACCESS_PB0 - PB27 */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0 + i); +} + +/** + * sh_hdmi_configure() - Initialise HDMI for output + */ +static void sh_hdmi_configure(struct sh_hdmi *hdmi) +{ + /* Configure video format */ + sh_hdmi_video_config(hdmi); + + /* Configure audio format */ + sh_hdmi_audio_config(hdmi); + + /* Configure PHY */ + sh_hdmi_phy_config(hdmi); + + /* Auxiliary Video Information (AVI) InfoFrame */ + sh_hdmi_avi_infoframe_setup(hdmi); + + /* Audio InfoFrame */ + sh_hdmi_audio_infoframe_setup(hdmi); + + /* Gamut Metadata packet */ + sh_hdmi_gamut_metadata_setup(hdmi); + + /* Audio Content Protection (ACP) Packet */ + sh_hdmi_acp_setup(hdmi); + + /* ISRC1 Packet */ + sh_hdmi_isrc1_setup(hdmi); + + /* ISRC2 Packet */ + sh_hdmi_isrc2_setup(hdmi); + + /* + * Control packet auto send with VSYNC control: auto send + * General control, Gamut metadata, ISRC, and ACP packets + */ + hdmi_write(hdmi, 0x8E, HDMI_CTRL_PKT_AUTO_SEND); + + /* FIXME */ + msleep(10); + + /* PS mode b->d, reset PLLA and PLLB */ + hdmi_write(hdmi, 0x4C, HDMI_SYSTEM_CTRL); + + udelay(10); + + hdmi_write(hdmi, 0x40, HDMI_SYSTEM_CTRL); +} + +static void sh_hdmi_read_edid(struct sh_hdmi *hdmi) +{ + struct fb_var_screeninfo *var = &hdmi->var; + struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; + struct fb_videomode *lcd_cfg = &pdata->lcd_chan->lcd_cfg; + unsigned long height = var->height, width = var->width; + int i; + u8 edid[128]; + + /* Read EDID */ + pr_debug("Read back EDID code:"); + for (i = 0; i < 128; i++) { + edid[i] = hdmi_read(hdmi, HDMI_EDID_KSV_FIFO_ACCESS_WINDOW); +#ifdef DEBUG + if ((i % 16) == 0) { + printk(KERN_CONT "\n"); + printk(KERN_DEBUG "%02X | %02X", i, edid[i]); + } else { + printk(KERN_CONT " %02X", edid[i]); + } +#endif + } +#ifdef DEBUG + printk(KERN_CONT "\n"); +#endif + fb_parse_edid(edid, var); + pr_debug("%u-%u-%u-%u x %u-%u-%u-%u @ %lu kHz monitor detected\n", + var->left_margin, var->xres, var->right_margin, var->hsync_len, + var->upper_margin, var->yres, var->lower_margin, var->vsync_len, + PICOS2KHZ(var->pixclock)); + + /* FIXME: Use user-provided configuration instead of EDID */ + var->width = width; + var->xres = lcd_cfg->xres; + var->xres_virtual = lcd_cfg->xres; + var->left_margin = lcd_cfg->left_margin; + var->right_margin = lcd_cfg->right_margin; + var->hsync_len = lcd_cfg->hsync_len; + var->height = height; + var->yres = lcd_cfg->yres; + var->yres_virtual = lcd_cfg->yres * 2; + var->upper_margin = lcd_cfg->upper_margin; + var->lower_margin = lcd_cfg->lower_margin; + var->vsync_len = lcd_cfg->vsync_len; + var->sync = lcd_cfg->sync; + var->pixclock = lcd_cfg->pixclock; + + hdmi_external_video_param(hdmi); +} + +static irqreturn_t sh_hdmi_hotplug(int irq, void *dev_id) +{ + struct sh_hdmi *hdmi = dev_id; + u8 status1, status2, mask1, mask2; + + /* mode_b and PLLA and PLLB reset */ + hdmi_write(hdmi, 0x2C, HDMI_SYSTEM_CTRL); + + /* How long shall reset be held? */ + udelay(10); + + /* mode_b and PLLA and PLLB reset release */ + hdmi_write(hdmi, 0x20, HDMI_SYSTEM_CTRL); + + status1 = hdmi_read(hdmi, HDMI_INTERRUPT_STATUS_1); + status2 = hdmi_read(hdmi, HDMI_INTERRUPT_STATUS_2); + + mask1 = hdmi_read(hdmi, HDMI_INTERRUPT_MASK_1); + mask2 = hdmi_read(hdmi, HDMI_INTERRUPT_MASK_2); + + /* Correct would be to ack only set bits, but the datasheet requires 0xff */ + hdmi_write(hdmi, 0xFF, HDMI_INTERRUPT_STATUS_1); + hdmi_write(hdmi, 0xFF, HDMI_INTERRUPT_STATUS_2); + + if (printk_ratelimit()) + pr_debug("IRQ #%d: Status #1: 0x%x & 0x%x, #2: 0x%x & 0x%x\n", + irq, status1, mask1, status2, mask2); + + if (!((status1 & mask1) | (status2 & mask2))) { + return IRQ_NONE; + } else if (status1 & 0xc0) { + u8 msens; + + /* Datasheet specifies 10ms... */ + udelay(500); + + msens = hdmi_read(hdmi, HDMI_HOT_PLUG_MSENS_STATUS); + pr_debug("MSENS 0x%x\n", msens); + /* Check, if hot plug & MSENS pin status are both high */ + if ((msens & 0xC0) == 0xC0) { + /* Display plug in */ + hdmi->hp_state = HDMI_HOTPLUG_CONNECTED; + + /* Set EDID word address */ + hdmi_write(hdmi, 0x00, HDMI_EDID_WORD_ADDRESS); + /* Set EDID segment pointer */ + hdmi_write(hdmi, 0x00, HDMI_EDID_SEGMENT_POINTER); + /* Enable EDID interrupt */ + hdmi_write(hdmi, 0xC6, HDMI_INTERRUPT_MASK_1); + } else if (!(status1 & 0x80)) { + /* Display unplug, beware multiple interrupts */ + if (hdmi->hp_state != HDMI_HOTPLUG_DISCONNECTED) + schedule_delayed_work(&hdmi->edid_work, 0); + + hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED; + /* display_off will switch back to mode_a */ + } + } else if (status1 & 2) { + /* EDID error interrupt: retry */ + /* Set EDID word address */ + hdmi_write(hdmi, 0x00, HDMI_EDID_WORD_ADDRESS); + /* Set EDID segment pointer */ + hdmi_write(hdmi, 0x00, HDMI_EDID_SEGMENT_POINTER); + } else if (status1 & 4) { + /* Disable EDID interrupt */ + hdmi_write(hdmi, 0xC0, HDMI_INTERRUPT_MASK_1); + hdmi->hp_state = HDMI_HOTPLUG_EDID_DONE; + schedule_delayed_work(&hdmi->edid_work, msecs_to_jiffies(10)); + } + + return IRQ_HANDLED; +} + +static void hdmi_display_on(void *arg, struct fb_info *info) +{ + struct sh_hdmi *hdmi = arg; + struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; + + if (info->var.xres != 1280 || info->var.yres != 720) { + dev_warn(info->device, "Unsupported framebuffer geometry %ux%u\n", + info->var.xres, info->var.yres); + return; + } + + pr_debug("%s(%p): state %x\n", __func__, pdata->lcd_dev, info->state); + /* + * FIXME: not a good place to store fb_info. And we cannot nullify it + * even on monitor disconnect. What should the lifecycle be? + */ + hdmi->info = info; + switch (hdmi->hp_state) { + case HDMI_HOTPLUG_EDID_DONE: + /* PS mode d->e. All functions are active */ + hdmi_write(hdmi, 0x80, HDMI_SYSTEM_CTRL); + pr_debug("HDMI running\n"); + break; + case HDMI_HOTPLUG_DISCONNECTED: + info->state = FBINFO_STATE_SUSPENDED; + default: + hdmi->var = info->var; + } +} + +static void hdmi_display_off(void *arg) +{ + struct sh_hdmi *hdmi = arg; + struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; + + pr_debug("%s(%p)\n", __func__, pdata->lcd_dev); + /* PS mode e->a */ + hdmi_write(hdmi, 0x10, HDMI_SYSTEM_CTRL); +} + +/* Hotplug interrupt occurred, read EDID */ +static void edid_work_fn(struct work_struct *work) +{ + struct sh_hdmi *hdmi = container_of(work, struct sh_hdmi, edid_work.work); + struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; + + pr_debug("%s(%p): begin, hotplug status %d\n", __func__, + pdata->lcd_dev, hdmi->hp_state); + + if (!pdata->lcd_dev) + return; + + if (hdmi->hp_state == HDMI_HOTPLUG_EDID_DONE) { + pm_runtime_get_sync(hdmi->dev); + /* A device has been plugged in */ + sh_hdmi_read_edid(hdmi); + msleep(10); + sh_hdmi_configure(hdmi); + /* Switched to another (d) power-save mode */ + msleep(10); + + if (!hdmi->info) + return; + + acquire_console_sem(); + + /* HDMI plug in */ + hdmi->info->var = hdmi->var; + if (hdmi->info->state != FBINFO_STATE_RUNNING) + fb_set_suspend(hdmi->info, 0); + else + hdmi_display_on(hdmi, hdmi->info); + + release_console_sem(); + } else { + if (!hdmi->info) + return; + + acquire_console_sem(); + + /* HDMI disconnect */ + fb_set_suspend(hdmi->info, 1); + + release_console_sem(); + pm_runtime_put(hdmi->dev); + } + + pr_debug("%s(%p): end\n", __func__, pdata->lcd_dev); +} + +static int __init sh_hdmi_probe(struct platform_device *pdev) +{ + struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data; + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + int irq = platform_get_irq(pdev, 0), ret; + struct sh_hdmi *hdmi; + long rate; + + if (!res || !pdata || irq < 0) + return -ENODEV; + + hdmi = kzalloc(sizeof(*hdmi), GFP_KERNEL); + if (!hdmi) { + dev_err(&pdev->dev, "Cannot allocate device data\n"); + return -ENOMEM; + } + + hdmi->dev = &pdev->dev; + + hdmi->hdmi_clk = clk_get(&pdev->dev, "ick"); + if (IS_ERR(hdmi->hdmi_clk)) { + ret = PTR_ERR(hdmi->hdmi_clk); + dev_err(&pdev->dev, "Unable to get clock: %d\n", ret); + goto egetclk; + } + + rate = PICOS2KHZ(pdata->lcd_chan->lcd_cfg.pixclock) * 1000; + + rate = clk_round_rate(hdmi->hdmi_clk, rate); + if (rate < 0) { + ret = rate; + dev_err(&pdev->dev, "Cannot get suitable rate: %ld\n", rate); + goto erate; + } + + ret = clk_set_rate(hdmi->hdmi_clk, rate); + if (ret < 0) { + dev_err(&pdev->dev, "Cannot set rate %ld: %d\n", rate, ret); + goto erate; + } + + pr_debug("HDMI set frequency %lu\n", rate); + + ret = clk_enable(hdmi->hdmi_clk); + if (ret < 0) { + dev_err(&pdev->dev, "Cannot enable clock: %d\n", ret); + goto eclkenable; + } + + dev_info(&pdev->dev, "Enabled HDMI clock at %luHz\n", rate); + + if (!request_mem_region(res->start, resource_size(res), dev_name(&pdev->dev))) { + dev_err(&pdev->dev, "HDMI register region already claimed\n"); + ret = -EBUSY; + goto ereqreg; + } + + hdmi->base = ioremap(res->start, resource_size(res)); + if (!hdmi->base) { + dev_err(&pdev->dev, "HDMI register region already claimed\n"); + ret = -ENOMEM; + goto emap; + } + + platform_set_drvdata(pdev, hdmi); + +#if 1 + /* Product and revision IDs are 0 in sh-mobile version */ + dev_info(&pdev->dev, "Detected HDMI controller 0x%x:0x%x\n", + hdmi_read(hdmi, HDMI_PRODUCT_ID), hdmi_read(hdmi, HDMI_REVISION_ID)); +#endif + + /* Set up LCDC callbacks */ + pdata->lcd_chan->board_cfg.board_data = hdmi; + pdata->lcd_chan->board_cfg.display_on = hdmi_display_on; + pdata->lcd_chan->board_cfg.display_off = hdmi_display_off; + + INIT_DELAYED_WORK(&hdmi->edid_work, edid_work_fn); + + pm_runtime_enable(&pdev->dev); + pm_runtime_resume(&pdev->dev); + + ret = request_irq(irq, sh_hdmi_hotplug, 0, + dev_name(&pdev->dev), hdmi); + if (ret < 0) { + dev_err(&pdev->dev, "Unable to request irq: %d\n", ret); + goto ereqirq; + } + + return 0; + +ereqirq: + pm_runtime_disable(&pdev->dev); + iounmap(hdmi->base); +emap: + release_mem_region(res->start, resource_size(res)); +ereqreg: + clk_disable(hdmi->hdmi_clk); +eclkenable: +erate: + clk_put(hdmi->hdmi_clk); +egetclk: + kfree(hdmi); + + return ret; +} + +static int __exit sh_hdmi_remove(struct platform_device *pdev) +{ + struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data; + struct sh_hdmi *hdmi = platform_get_drvdata(pdev); + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + int irq = platform_get_irq(pdev, 0); + + pdata->lcd_chan->board_cfg.display_on = NULL; + pdata->lcd_chan->board_cfg.display_off = NULL; + pdata->lcd_chan->board_cfg.board_data = NULL; + + free_irq(irq, hdmi); + pm_runtime_disable(&pdev->dev); + cancel_delayed_work_sync(&hdmi->edid_work); + clk_disable(hdmi->hdmi_clk); + clk_put(hdmi->hdmi_clk); + iounmap(hdmi->base); + release_mem_region(res->start, resource_size(res)); + kfree(hdmi); + + return 0; +} + +static struct platform_driver sh_hdmi_driver = { + .remove = __exit_p(sh_hdmi_remove), + .driver = { + .name = "sh-mobile-hdmi", + }, +}; + +static int __init sh_hdmi_init(void) +{ + return platform_driver_probe(&sh_hdmi_driver, sh_hdmi_probe); +} +module_init(sh_hdmi_init); + +static void __exit sh_hdmi_exit(void) +{ + platform_driver_unregister(&sh_hdmi_driver); +} +module_exit(sh_hdmi_exit); + +MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); +MODULE_DESCRIPTION("SuperH / ARM-shmobile HDMI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c index 12c451a711e..d72075a9f01 100644 --- a/drivers/video/sh_mobile_lcdcfb.c +++ b/drivers/video/sh_mobile_lcdcfb.c @@ -56,6 +56,7 @@ static int lcdc_shared_regs[] = { /* per-channel registers */ enum { LDDCKPAT1R, LDDCKPAT2R, LDMT1R, LDMT2R, LDMT3R, LDDFR, LDSM1R, LDSM2R, LDSA1R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR, + LDHAJR, NR_CH_REGS }; static unsigned long lcdc_offs_mainlcd[NR_CH_REGS] = { @@ -74,6 +75,7 @@ static unsigned long lcdc_offs_mainlcd[NR_CH_REGS] = { [LDVLNR] = 0x450, [LDVSYNR] = 0x454, [LDPMR] = 0x460, + [LDHAJR] = 0x4a0, }; static unsigned long lcdc_offs_sublcd[NR_CH_REGS] = { @@ -137,6 +139,7 @@ struct sh_mobile_lcdc_priv { struct clk *dot_clk; unsigned long lddckr; struct sh_mobile_lcdc_chan ch[2]; + struct notifier_block notifier; unsigned long saved_shared_regs[NR_SHARED_REGS]; int started; }; @@ -404,6 +407,56 @@ static void sh_mobile_lcdc_start_stop(struct sh_mobile_lcdc_priv *priv, lcdc_write(priv, _LDDCKSTPR, 1); /* stop dotclock */ } +static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch) +{ + struct fb_var_screeninfo *var = &ch->info->var; + unsigned long h_total, hsync_pos; + u32 tmp; + + tmp = ch->ldmt1r_value; + tmp |= (var->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : 1 << 28; + tmp |= (var->sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : 1 << 27; + tmp |= (ch->cfg.flags & LCDC_FLAGS_DWPOL) ? 1 << 26 : 0; + tmp |= (ch->cfg.flags & LCDC_FLAGS_DIPOL) ? 1 << 25 : 0; + tmp |= (ch->cfg.flags & LCDC_FLAGS_DAPOL) ? 1 << 24 : 0; + tmp |= (ch->cfg.flags & LCDC_FLAGS_HSCNT) ? 1 << 17 : 0; + tmp |= (ch->cfg.flags & LCDC_FLAGS_DWCNT) ? 1 << 16 : 0; + lcdc_write_chan(ch, LDMT1R, tmp); + + /* setup SYS bus */ + lcdc_write_chan(ch, LDMT2R, ch->cfg.sys_bus_cfg.ldmt2r); + lcdc_write_chan(ch, LDMT3R, ch->cfg.sys_bus_cfg.ldmt3r); + + /* horizontal configuration */ + h_total = var->xres + var->hsync_len + + var->left_margin + var->right_margin; + tmp = h_total / 8; /* HTCN */ + tmp |= (var->xres / 8) << 16; /* HDCN */ + lcdc_write_chan(ch, LDHCNR, tmp); + + hsync_pos = var->xres + var->right_margin; + tmp = hsync_pos / 8; /* HSYNP */ + tmp |= (var->hsync_len / 8) << 16; /* HSYNW */ + lcdc_write_chan(ch, LDHSYNR, tmp); + + /* vertical configuration */ + tmp = var->yres + var->vsync_len + + var->upper_margin + var->lower_margin; /* VTLN */ + tmp |= var->yres << 16; /* VDLN */ + lcdc_write_chan(ch, LDVLNR, tmp); + + tmp = var->yres + var->lower_margin; /* VSYNP */ + tmp |= var->vsync_len << 16; /* VSYNW */ + lcdc_write_chan(ch, LDVSYNR, tmp); + + /* Adjust horizontal synchronisation for HDMI */ + tmp = ((var->xres & 7) << 24) | + ((h_total & 7) << 16) | + ((var->hsync_len & 7) << 8) | + hsync_pos; + lcdc_write_chan(ch, LDHAJR, tmp); +} + static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) { struct sh_mobile_lcdc_chan *ch; @@ -470,49 +523,11 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) if (!ch->enabled) continue; - tmp = ch->ldmt1r_value; - tmp |= (lcd_cfg->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : 1 << 28; - tmp |= (lcd_cfg->sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : 1 << 27; - tmp |= (ch->cfg.flags & LCDC_FLAGS_DWPOL) ? 1 << 26 : 0; - tmp |= (ch->cfg.flags & LCDC_FLAGS_DIPOL) ? 1 << 25 : 0; - tmp |= (ch->cfg.flags & LCDC_FLAGS_DAPOL) ? 1 << 24 : 0; - tmp |= (ch->cfg.flags & LCDC_FLAGS_HSCNT) ? 1 << 17 : 0; - tmp |= (ch->cfg.flags & LCDC_FLAGS_DWCNT) ? 1 << 16 : 0; - lcdc_write_chan(ch, LDMT1R, tmp); - - /* setup SYS bus */ - lcdc_write_chan(ch, LDMT2R, ch->cfg.sys_bus_cfg.ldmt2r); - lcdc_write_chan(ch, LDMT3R, ch->cfg.sys_bus_cfg.ldmt3r); - - /* horizontal configuration */ - tmp = lcd_cfg->xres + lcd_cfg->hsync_len; - tmp += lcd_cfg->left_margin; - tmp += lcd_cfg->right_margin; - tmp /= 8; /* HTCN */ - tmp |= (lcd_cfg->xres / 8) << 16; /* HDCN */ - lcdc_write_chan(ch, LDHCNR, tmp); - - tmp = lcd_cfg->xres; - tmp += lcd_cfg->right_margin; - tmp /= 8; /* HSYNP */ - tmp |= (lcd_cfg->hsync_len / 8) << 16; /* HSYNW */ - lcdc_write_chan(ch, LDHSYNR, tmp); + sh_mobile_lcdc_geometry(ch); /* power supply */ lcdc_write_chan(ch, LDPMR, 0); - /* vertical configuration */ - tmp = lcd_cfg->yres + lcd_cfg->vsync_len; - tmp += lcd_cfg->upper_margin; - tmp += lcd_cfg->lower_margin; /* VTLN */ - tmp |= lcd_cfg->yres << 16; /* VDLN */ - lcdc_write_chan(ch, LDVLNR, tmp); - - tmp = lcd_cfg->yres; - tmp += lcd_cfg->lower_margin; /* VSYNP */ - tmp |= lcd_cfg->vsync_len << 16; /* VSYNW */ - lcdc_write_chan(ch, LDVSYNR, tmp); - board_cfg = &ch->cfg.board_cfg; if (board_cfg->setup_sys) ret = board_cfg->setup_sys(board_cfg->board_data, ch, @@ -577,7 +592,7 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) board_cfg = &ch->cfg.board_cfg; if (board_cfg->display_on) - board_cfg->display_on(board_cfg->board_data); + board_cfg->display_on(board_cfg->board_data, ch->info); } return 0; @@ -943,6 +958,62 @@ static const struct dev_pm_ops sh_mobile_lcdc_dev_pm_ops = { .runtime_resume = sh_mobile_lcdc_runtime_resume, }; +static int sh_mobile_lcdc_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct fb_event *event = data; + struct fb_info *info = event->info; + struct sh_mobile_lcdc_chan *ch = info->par; + struct sh_mobile_lcdc_board_cfg *board_cfg = &ch->cfg.board_cfg; + struct fb_var_screeninfo *var; + + if (&ch->lcdc->notifier != nb) + return 0; + + dev_dbg(info->dev, "%s(): action = %lu, data = %p\n", + __func__, action, event->data); + + switch(action) { + case FB_EVENT_SUSPEND: + if (board_cfg->display_off) + board_cfg->display_off(board_cfg->board_data); + pm_runtime_put(info->device); + break; + case FB_EVENT_RESUME: + var = &info->var; + + /* HDMI must be enabled before LCDC configuration */ + if (board_cfg->display_on) + board_cfg->display_on(board_cfg->board_data, ch->info); + + /* Check if the new display is not in our modelist */ + if (ch->info->modelist.next && + !fb_match_mode(var, &ch->info->modelist)) { + struct fb_videomode mode; + int ret; + + /* Can we handle this display? */ + if (var->xres > ch->cfg.lcd_cfg.xres || + var->yres > ch->cfg.lcd_cfg.yres) + return -ENOMEM; + + /* Add to the modelist */ + fb_var_to_videomode(&mode, var); + ret = fb_add_videomode(&mode, &ch->info->modelist); + if (ret < 0) + return ret; + } + + pm_runtime_get_sync(info->device); + + sh_mobile_lcdc_geometry(ch); + + break; + } + + return 0; +} + static int sh_mobile_lcdc_remove(struct platform_device *pdev); static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) @@ -1020,15 +1091,19 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) goto err1; } + priv->base = ioremap_nocache(res->start, resource_size(res)); + if (!priv->base) + goto err1; + error = sh_mobile_lcdc_setup_clocks(pdev, pdata->clock_source, priv); if (error) { dev_err(&pdev->dev, "unable to setup clocks\n"); goto err1; } - priv->base = ioremap_nocache(res->start, (res->end - res->start) + 1); - for (i = 0; i < j; i++) { + struct fb_var_screeninfo *var; + struct fb_videomode *lcd_cfg; cfg = &priv->ch[i].cfg; priv->ch[i].info = framebuffer_alloc(0, &pdev->dev); @@ -1039,22 +1114,33 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) } info = priv->ch[i].info; + var = &info->var; + lcd_cfg = &cfg->lcd_cfg; info->fbops = &sh_mobile_lcdc_ops; - info->var.xres = info->var.xres_virtual = cfg->lcd_cfg.xres; - info->var.yres = cfg->lcd_cfg.yres; + var->xres = var->xres_virtual = lcd_cfg->xres; + var->yres = lcd_cfg->yres; /* Default Y virtual resolution is 2x panel size */ - info->var.yres_virtual = info->var.yres * 2; - info->var.width = cfg->lcd_size_cfg.width; - info->var.height = cfg->lcd_size_cfg.height; - info->var.activate = FB_ACTIVATE_NOW; - error = sh_mobile_lcdc_set_bpp(&info->var, cfg->bpp); + var->yres_virtual = var->yres * 2; + var->width = cfg->lcd_size_cfg.width; + var->height = cfg->lcd_size_cfg.height; + var->activate = FB_ACTIVATE_NOW; + var->left_margin = lcd_cfg->left_margin; + var->right_margin = lcd_cfg->right_margin; + var->upper_margin = lcd_cfg->upper_margin; + var->lower_margin = lcd_cfg->lower_margin; + var->hsync_len = lcd_cfg->hsync_len; + var->vsync_len = lcd_cfg->vsync_len; + var->sync = lcd_cfg->sync; + var->pixclock = lcd_cfg->pixclock; + + error = sh_mobile_lcdc_set_bpp(var, cfg->bpp); if (error) break; info->fix = sh_mobile_lcdc_fix; - info->fix.line_length = cfg->lcd_cfg.xres * (cfg->bpp / 8); + info->fix.line_length = lcd_cfg->xres * (cfg->bpp / 8); info->fix.smem_len = info->fix.line_length * - info->var.yres_virtual; + var->yres_virtual; buf = dma_alloc_coherent(&pdev->dev, info->fix.smem_len, &priv->ch[i].dma_handle, GFP_KERNEL); @@ -1119,10 +1205,14 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) ch->cfg.bpp); /* deferred io mode: disable clock to save power */ - if (info->fbdefio) + if (info->fbdefio || info->state == FBINFO_STATE_SUSPENDED) sh_mobile_lcdc_clk_off(priv); } + /* Failure ignored */ + priv->notifier.notifier_call = sh_mobile_lcdc_notify; + fb_register_client(&priv->notifier); + return 0; err1: sh_mobile_lcdc_remove(pdev); @@ -1136,6 +1226,8 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev) struct fb_info *info; int i; + fb_unregister_client(&priv->notifier); + for (i = 0; i < ARRAY_SIZE(priv->ch); i++) if (priv->ch[i].info && priv->ch[i].info->dev) unregister_framebuffer(priv->ch[i].info); diff --git a/drivers/video/sunxvr1000.c b/drivers/video/sunxvr1000.c index 7288934c0d4..5dbe06af222 100644 --- a/drivers/video/sunxvr1000.c +++ b/drivers/video/sunxvr1000.c @@ -111,7 +111,7 @@ static int __devinit gfb_set_fbinfo(struct gfb_info *gp) return 0; } -static int __devinit gfb_probe(struct of_device *op, +static int __devinit gfb_probe(struct platform_device *op, const struct of_device_id *match) { struct device_node *dp = op->dev.of_node; @@ -172,7 +172,7 @@ err_out: return err; } -static int __devexit gfb_remove(struct of_device *op) +static int __devexit gfb_remove(struct platform_device *op) { struct fb_info *info = dev_get_drvdata(&op->dev); struct gfb_info *gp = info->par; diff --git a/drivers/video/tcx.c b/drivers/video/tcx.c index f375e0db677..77ad27955cf 100644 --- a/drivers/video/tcx.c +++ b/drivers/video/tcx.c @@ -342,7 +342,7 @@ tcx_init_fix(struct fb_info *info, int linebytes) info->fix.accel = FB_ACCEL_SUN_TCX; } -static void tcx_unmap_regs(struct of_device *op, struct fb_info *info, +static void tcx_unmap_regs(struct platform_device *op, struct fb_info *info, struct tcx_par *par) { if (par->tec) @@ -362,7 +362,7 @@ static void tcx_unmap_regs(struct of_device *op, struct fb_info *info, info->screen_base, info->fix.smem_len); } -static int __devinit tcx_probe(struct of_device *op, +static int __devinit tcx_probe(struct platform_device *op, const struct of_device_id *match) { struct device_node *dp = op->dev.of_node; @@ -486,7 +486,7 @@ out_err: return err; } -static int __devexit tcx_remove(struct of_device *op) +static int __devexit tcx_remove(struct platform_device *op) { struct fb_info *info = dev_get_drvdata(&op->dev); struct tcx_par *par = info->par; diff --git a/drivers/video/xilinxfb.c b/drivers/video/xilinxfb.c index 29b5daacc21..0c9ce88e95e 100644 --- a/drivers/video/xilinxfb.c +++ b/drivers/video/xilinxfb.c @@ -397,7 +397,7 @@ static int xilinxfb_release(struct device *dev) */ static int __devinit -xilinxfb_of_probe(struct of_device *op, const struct of_device_id *match) +xilinxfb_of_probe(struct platform_device *op, const struct of_device_id *match) { const u32 *prop; u32 *p; @@ -477,7 +477,7 @@ xilinxfb_of_probe(struct of_device *op, const struct of_device_id *match) return -ENODEV; } -static int __devexit xilinxfb_of_remove(struct of_device *op) +static int __devexit xilinxfb_of_remove(struct platform_device *op) { return xilinxfb_release(&op->dev); } diff --git a/drivers/watchdog/cpwd.c b/drivers/watchdog/cpwd.c index 30a2512fd52..566343b3c13 100644 --- a/drivers/watchdog/cpwd.c +++ b/drivers/watchdog/cpwd.c @@ -526,7 +526,7 @@ static const struct file_operations cpwd_fops = { .release = cpwd_release, }; -static int __devinit cpwd_probe(struct of_device *op, +static int __devinit cpwd_probe(struct platform_device *op, const struct of_device_id *match) { struct device_node *options; @@ -639,7 +639,7 @@ out_free: goto out; } -static int __devexit cpwd_remove(struct of_device *op) +static int __devexit cpwd_remove(struct platform_device *op) { struct cpwd *p = dev_get_drvdata(&op->dev); int i; diff --git a/drivers/watchdog/gef_wdt.c b/drivers/watchdog/gef_wdt.c index 1df284f9c2a..9c21d19043a 100644 --- a/drivers/watchdog/gef_wdt.c +++ b/drivers/watchdog/gef_wdt.c @@ -260,7 +260,7 @@ static struct miscdevice gef_wdt_miscdev = { }; -static int __devinit gef_wdt_probe(struct of_device *dev, +static int __devinit gef_wdt_probe(struct platform_device *dev, const struct of_device_id *match) { int timeout = 10; diff --git a/drivers/watchdog/mpc8xxx_wdt.c b/drivers/watchdog/mpc8xxx_wdt.c index 4cda64dd309..8fa213cdb49 100644 --- a/drivers/watchdog/mpc8xxx_wdt.c +++ b/drivers/watchdog/mpc8xxx_wdt.c @@ -185,7 +185,7 @@ static struct miscdevice mpc8xxx_wdt_miscdev = { .fops = &mpc8xxx_wdt_fops, }; -static int __devinit mpc8xxx_wdt_probe(struct of_device *ofdev, +static int __devinit mpc8xxx_wdt_probe(struct platform_device *ofdev, const struct of_device_id *match) { int ret; @@ -238,7 +238,7 @@ err_unmap: return ret; } -static int __devexit mpc8xxx_wdt_remove(struct of_device *ofdev) +static int __devexit mpc8xxx_wdt_remove(struct platform_device *ofdev) { mpc8xxx_wdt_pr_warn("watchdog removed"); del_timer_sync(&wdt_timer); diff --git a/drivers/watchdog/riowd.c b/drivers/watchdog/riowd.c index 4082b4ace1f..3faee1ae64b 100644 --- a/drivers/watchdog/riowd.c +++ b/drivers/watchdog/riowd.c @@ -172,7 +172,7 @@ static struct miscdevice riowd_miscdev = { .fops = &riowd_fops }; -static int __devinit riowd_probe(struct of_device *op, +static int __devinit riowd_probe(struct platform_device *op, const struct of_device_id *match) { struct riowd *p; @@ -219,7 +219,7 @@ out: return err; } -static int __devexit riowd_remove(struct of_device *op) +static int __devexit riowd_remove(struct platform_device *op) { struct riowd *p = dev_get_drvdata(&op->dev); diff --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig index 0a882693663..60d71e9abe9 100644 --- a/drivers/xen/Kconfig +++ b/drivers/xen/Kconfig @@ -71,4 +71,9 @@ config XEN_PLATFORM_PCI initializing xenbus and grant_table when running in a Xen HVM domain. As a consequence this driver is required to run any Xen PV frontend on Xen HVM. + +config SWIOTLB_XEN + def_bool y + depends on SWIOTLB + endmenu diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile index e392fb776af..fcaf838f54b 100644 --- a/drivers/xen/Makefile +++ b/drivers/xen/Makefile @@ -11,3 +11,4 @@ obj-$(CONFIG_XEN_DEV_EVTCHN) += evtchn.o obj-$(CONFIG_XENFS) += xenfs/ obj-$(CONFIG_XEN_SYS_HYPERVISOR) += sys-hypervisor.o obj-$(CONFIG_XEN_PLATFORM_PCI) += platform-pci.o +obj-$(CONFIG_SWIOTLB_XEN) += swiotlb-xen.o diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c index 1a0d8c2a035..500290b150b 100644 --- a/drivers/xen/balloon.c +++ b/drivers/xen/balloon.c @@ -85,13 +85,6 @@ static struct sys_device balloon_sysdev; static int register_balloon(struct sys_device *sysdev); -/* - * Protects atomic reservation decrease/increase against concurrent increases. - * Also protects non-atomic updates of current_pages and driver_pages, and - * balloon lists. - */ -static DEFINE_SPINLOCK(balloon_lock); - static struct balloon_stats balloon_stats; /* We increase/decrease in batches which fit in a page */ @@ -210,7 +203,7 @@ static int increase_reservation(unsigned long nr_pages) if (nr_pages > ARRAY_SIZE(frame_list)) nr_pages = ARRAY_SIZE(frame_list); - spin_lock_irqsave(&balloon_lock, flags); + spin_lock_irqsave(&xen_reservation_lock, flags); page = balloon_first_page(); for (i = 0; i < nr_pages; i++) { @@ -254,7 +247,7 @@ static int increase_reservation(unsigned long nr_pages) balloon_stats.current_pages += rc; out: - spin_unlock_irqrestore(&balloon_lock, flags); + spin_unlock_irqrestore(&xen_reservation_lock, flags); return rc < 0 ? rc : rc != nr_pages; } @@ -299,7 +292,7 @@ static int decrease_reservation(unsigned long nr_pages) kmap_flush_unused(); flush_tlb_all(); - spin_lock_irqsave(&balloon_lock, flags); + spin_lock_irqsave(&xen_reservation_lock, flags); /* No more mappings: invalidate P2M and add to balloon. */ for (i = 0; i < nr_pages; i++) { @@ -315,7 +308,7 @@ static int decrease_reservation(unsigned long nr_pages) balloon_stats.current_pages -= nr_pages; - spin_unlock_irqrestore(&balloon_lock, flags); + spin_unlock_irqrestore(&xen_reservation_lock, flags); return need_sleep; } diff --git a/drivers/xen/swiotlb-xen.c b/drivers/xen/swiotlb-xen.c new file mode 100644 index 00000000000..54469c3eeac --- /dev/null +++ b/drivers/xen/swiotlb-xen.c @@ -0,0 +1,515 @@ +/* + * Copyright 2010 + * by Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> + * + * This code provides a IOMMU for Xen PV guests with PCI passthrough. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License v2.0 as published by + * the Free Software Foundation + * + * 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. + * + * PV guests under Xen are running in an non-contiguous memory architecture. + * + * When PCI pass-through is utilized, this necessitates an IOMMU for + * translating bus (DMA) to virtual and vice-versa and also providing a + * mechanism to have contiguous pages for device drivers operations (say DMA + * operations). + * + * Specifically, under Xen the Linux idea of pages is an illusion. It + * assumes that pages start at zero and go up to the available memory. To + * help with that, the Linux Xen MMU provides a lookup mechanism to + * translate the page frame numbers (PFN) to machine frame numbers (MFN) + * and vice-versa. The MFN are the "real" frame numbers. Furthermore + * memory is not contiguous. Xen hypervisor stitches memory for guests + * from different pools, which means there is no guarantee that PFN==MFN + * and PFN+1==MFN+1. Lastly with Xen 4.0, pages (in debug mode) are + * allocated in descending order (high to low), meaning the guest might + * never get any MFN's under the 4GB mark. + * + */ + +#include <linux/bootmem.h> +#include <linux/dma-mapping.h> +#include <xen/swiotlb-xen.h> +#include <xen/page.h> +#include <xen/xen-ops.h> +/* + * Used to do a quick range check in swiotlb_tbl_unmap_single and + * swiotlb_tbl_sync_single_*, to see if the memory was in fact allocated by this + * API. + */ + +static char *xen_io_tlb_start, *xen_io_tlb_end; +static unsigned long xen_io_tlb_nslabs; +/* + * Quick lookup value of the bus address of the IOTLB. + */ + +u64 start_dma_addr; + +static dma_addr_t xen_phys_to_bus(phys_addr_t paddr) +{ + return phys_to_machine(XPADDR(paddr)).maddr;; +} + +static phys_addr_t xen_bus_to_phys(dma_addr_t baddr) +{ + return machine_to_phys(XMADDR(baddr)).paddr; +} + +static dma_addr_t xen_virt_to_bus(void *address) +{ + return xen_phys_to_bus(virt_to_phys(address)); +} + +static int check_pages_physically_contiguous(unsigned long pfn, + unsigned int offset, + size_t length) +{ + unsigned long next_mfn; + int i; + int nr_pages; + + next_mfn = pfn_to_mfn(pfn); + nr_pages = (offset + length + PAGE_SIZE-1) >> PAGE_SHIFT; + + for (i = 1; i < nr_pages; i++) { + if (pfn_to_mfn(++pfn) != ++next_mfn) + return 0; + } + return 1; +} + +static int range_straddles_page_boundary(phys_addr_t p, size_t size) +{ + unsigned long pfn = PFN_DOWN(p); + unsigned int offset = p & ~PAGE_MASK; + + if (offset + size <= PAGE_SIZE) + return 0; + if (check_pages_physically_contiguous(pfn, offset, size)) + return 0; + return 1; +} + +static int is_xen_swiotlb_buffer(dma_addr_t dma_addr) +{ + unsigned long mfn = PFN_DOWN(dma_addr); + unsigned long pfn = mfn_to_local_pfn(mfn); + phys_addr_t paddr; + + /* If the address is outside our domain, it CAN + * have the same virtual address as another address + * in our domain. Therefore _only_ check address within our domain. + */ + if (pfn_valid(pfn)) { + paddr = PFN_PHYS(pfn); + return paddr >= virt_to_phys(xen_io_tlb_start) && + paddr < virt_to_phys(xen_io_tlb_end); + } + return 0; +} + +static int max_dma_bits = 32; + +static int +xen_swiotlb_fixup(void *buf, size_t size, unsigned long nslabs) +{ + int i, rc; + int dma_bits; + + dma_bits = get_order(IO_TLB_SEGSIZE << IO_TLB_SHIFT) + PAGE_SHIFT; + + i = 0; + do { + int slabs = min(nslabs - i, (unsigned long)IO_TLB_SEGSIZE); + + do { + rc = xen_create_contiguous_region( + (unsigned long)buf + (i << IO_TLB_SHIFT), + get_order(slabs << IO_TLB_SHIFT), + dma_bits); + } while (rc && dma_bits++ < max_dma_bits); + if (rc) + return rc; + + i += slabs; + } while (i < nslabs); + return 0; +} + +void __init xen_swiotlb_init(int verbose) +{ + unsigned long bytes; + int rc; + + xen_io_tlb_nslabs = (64 * 1024 * 1024 >> IO_TLB_SHIFT); + xen_io_tlb_nslabs = ALIGN(xen_io_tlb_nslabs, IO_TLB_SEGSIZE); + + bytes = xen_io_tlb_nslabs << IO_TLB_SHIFT; + + /* + * Get IO TLB memory from any location. + */ + xen_io_tlb_start = alloc_bootmem(bytes); + if (!xen_io_tlb_start) + panic("Cannot allocate SWIOTLB buffer"); + + xen_io_tlb_end = xen_io_tlb_start + bytes; + /* + * And replace that memory with pages under 4GB. + */ + rc = xen_swiotlb_fixup(xen_io_tlb_start, + bytes, + xen_io_tlb_nslabs); + if (rc) + goto error; + + start_dma_addr = xen_virt_to_bus(xen_io_tlb_start); + swiotlb_init_with_tbl(xen_io_tlb_start, xen_io_tlb_nslabs, verbose); + + return; +error: + panic("DMA(%d): Failed to exchange pages allocated for DMA with Xen! "\ + "We either don't have the permission or you do not have enough"\ + "free memory under 4GB!\n", rc); +} + +void * +xen_swiotlb_alloc_coherent(struct device *hwdev, size_t size, + dma_addr_t *dma_handle, gfp_t flags) +{ + void *ret; + int order = get_order(size); + u64 dma_mask = DMA_BIT_MASK(32); + unsigned long vstart; + + /* + * Ignore region specifiers - the kernel's ideas of + * pseudo-phys memory layout has nothing to do with the + * machine physical layout. We can't allocate highmem + * because we can't return a pointer to it. + */ + flags &= ~(__GFP_DMA | __GFP_HIGHMEM); + + if (dma_alloc_from_coherent(hwdev, size, dma_handle, &ret)) + return ret; + + vstart = __get_free_pages(flags, order); + ret = (void *)vstart; + + if (hwdev && hwdev->coherent_dma_mask) + dma_mask = dma_alloc_coherent_mask(hwdev, flags); + + if (ret) { + if (xen_create_contiguous_region(vstart, order, + fls64(dma_mask)) != 0) { + free_pages(vstart, order); + return NULL; + } + memset(ret, 0, size); + *dma_handle = virt_to_machine(ret).maddr; + } + return ret; +} +EXPORT_SYMBOL_GPL(xen_swiotlb_alloc_coherent); + +void +xen_swiotlb_free_coherent(struct device *hwdev, size_t size, void *vaddr, + dma_addr_t dev_addr) +{ + int order = get_order(size); + + if (dma_release_from_coherent(hwdev, order, vaddr)) + return; + + xen_destroy_contiguous_region((unsigned long)vaddr, order); + free_pages((unsigned long)vaddr, order); +} +EXPORT_SYMBOL_GPL(xen_swiotlb_free_coherent); + + +/* + * Map a single buffer of the indicated size for DMA in streaming mode. The + * physical address to use is returned. + * + * Once the device is given the dma address, the device owns this memory until + * either xen_swiotlb_unmap_page or xen_swiotlb_dma_sync_single is performed. + */ +dma_addr_t xen_swiotlb_map_page(struct device *dev, struct page *page, + unsigned long offset, size_t size, + enum dma_data_direction dir, + struct dma_attrs *attrs) +{ + phys_addr_t phys = page_to_phys(page) + offset; + dma_addr_t dev_addr = xen_phys_to_bus(phys); + void *map; + + BUG_ON(dir == DMA_NONE); + /* + * If the address happens to be in the device's DMA window, + * we can safely return the device addr and not worry about bounce + * buffering it. + */ + if (dma_capable(dev, dev_addr, size) && + !range_straddles_page_boundary(phys, size) && !swiotlb_force) + return dev_addr; + + /* + * Oh well, have to allocate and map a bounce buffer. + */ + map = swiotlb_tbl_map_single(dev, start_dma_addr, phys, size, dir); + if (!map) + return DMA_ERROR_CODE; + + dev_addr = xen_virt_to_bus(map); + + /* + * Ensure that the address returned is DMA'ble + */ + if (!dma_capable(dev, dev_addr, size)) + panic("map_single: bounce buffer is not DMA'ble"); + + return dev_addr; +} +EXPORT_SYMBOL_GPL(xen_swiotlb_map_page); + +/* + * Unmap a single streaming mode DMA translation. The dma_addr and size must + * match what was provided for in a previous xen_swiotlb_map_page call. All + * other usages are undefined. + * + * After this call, reads by the cpu to the buffer are guaranteed to see + * whatever the device wrote there. + */ +static void xen_unmap_single(struct device *hwdev, dma_addr_t dev_addr, + size_t size, enum dma_data_direction dir) +{ + phys_addr_t paddr = xen_bus_to_phys(dev_addr); + + BUG_ON(dir == DMA_NONE); + + /* NOTE: We use dev_addr here, not paddr! */ + if (is_xen_swiotlb_buffer(dev_addr)) { + swiotlb_tbl_unmap_single(hwdev, phys_to_virt(paddr), size, dir); + return; + } + + if (dir != DMA_FROM_DEVICE) + return; + + /* + * phys_to_virt doesn't work with hihgmem page but we could + * call dma_mark_clean() with hihgmem page here. However, we + * are fine since dma_mark_clean() is null on POWERPC. We can + * make dma_mark_clean() take a physical address if necessary. + */ + dma_mark_clean(phys_to_virt(paddr), size); +} + +void xen_swiotlb_unmap_page(struct device *hwdev, dma_addr_t dev_addr, + size_t size, enum dma_data_direction dir, + struct dma_attrs *attrs) +{ + xen_unmap_single(hwdev, dev_addr, size, dir); +} +EXPORT_SYMBOL_GPL(xen_swiotlb_unmap_page); + +/* + * Make physical memory consistent for a single streaming mode DMA translation + * after a transfer. + * + * If you perform a xen_swiotlb_map_page() but wish to interrogate the buffer + * using the cpu, yet do not wish to teardown the dma mapping, you must + * call this function before doing so. At the next point you give the dma + * address back to the card, you must first perform a + * xen_swiotlb_dma_sync_for_device, and then the device again owns the buffer + */ +static void +xen_swiotlb_sync_single(struct device *hwdev, dma_addr_t dev_addr, + size_t size, enum dma_data_direction dir, + enum dma_sync_target target) +{ + phys_addr_t paddr = xen_bus_to_phys(dev_addr); + + BUG_ON(dir == DMA_NONE); + + /* NOTE: We use dev_addr here, not paddr! */ + if (is_xen_swiotlb_buffer(dev_addr)) { + swiotlb_tbl_sync_single(hwdev, phys_to_virt(paddr), size, dir, + target); + return; + } + + if (dir != DMA_FROM_DEVICE) + return; + + dma_mark_clean(phys_to_virt(paddr), size); +} + +void +xen_swiotlb_sync_single_for_cpu(struct device *hwdev, dma_addr_t dev_addr, + size_t size, enum dma_data_direction dir) +{ + xen_swiotlb_sync_single(hwdev, dev_addr, size, dir, SYNC_FOR_CPU); +} +EXPORT_SYMBOL_GPL(xen_swiotlb_sync_single_for_cpu); + +void +xen_swiotlb_sync_single_for_device(struct device *hwdev, dma_addr_t dev_addr, + size_t size, enum dma_data_direction dir) +{ + xen_swiotlb_sync_single(hwdev, dev_addr, size, dir, SYNC_FOR_DEVICE); +} +EXPORT_SYMBOL_GPL(xen_swiotlb_sync_single_for_device); + +/* + * Map a set of buffers described by scatterlist in streaming mode for DMA. + * This is the scatter-gather version of the above xen_swiotlb_map_page + * interface. Here the scatter gather list elements are each tagged with the + * appropriate dma address and length. They are obtained via + * sg_dma_{address,length}(SG). + * + * NOTE: An implementation may be able to use a smaller number of + * DMA address/length pairs than there are SG table elements. + * (for example via virtual mapping capabilities) + * The routine returns the number of addr/length pairs actually + * used, at most nents. + * + * Device ownership issues as mentioned above for xen_swiotlb_map_page are the + * same here. + */ +int +xen_swiotlb_map_sg_attrs(struct device *hwdev, struct scatterlist *sgl, + int nelems, enum dma_data_direction dir, + struct dma_attrs *attrs) +{ + struct scatterlist *sg; + int i; + + BUG_ON(dir == DMA_NONE); + + for_each_sg(sgl, sg, nelems, i) { + phys_addr_t paddr = sg_phys(sg); + dma_addr_t dev_addr = xen_phys_to_bus(paddr); + + if (swiotlb_force || + !dma_capable(hwdev, dev_addr, sg->length) || + range_straddles_page_boundary(paddr, sg->length)) { + void *map = swiotlb_tbl_map_single(hwdev, + start_dma_addr, + sg_phys(sg), + sg->length, dir); + if (!map) { + /* Don't panic here, we expect map_sg users + to do proper error handling. */ + xen_swiotlb_unmap_sg_attrs(hwdev, sgl, i, dir, + attrs); + sgl[0].dma_length = 0; + return DMA_ERROR_CODE; + } + sg->dma_address = xen_virt_to_bus(map); + } else + sg->dma_address = dev_addr; + sg->dma_length = sg->length; + } + return nelems; +} +EXPORT_SYMBOL_GPL(xen_swiotlb_map_sg_attrs); + +int +xen_swiotlb_map_sg(struct device *hwdev, struct scatterlist *sgl, int nelems, + enum dma_data_direction dir) +{ + return xen_swiotlb_map_sg_attrs(hwdev, sgl, nelems, dir, NULL); +} +EXPORT_SYMBOL_GPL(xen_swiotlb_map_sg); + +/* + * Unmap a set of streaming mode DMA translations. Again, cpu read rules + * concerning calls here are the same as for swiotlb_unmap_page() above. + */ +void +xen_swiotlb_unmap_sg_attrs(struct device *hwdev, struct scatterlist *sgl, + int nelems, enum dma_data_direction dir, + struct dma_attrs *attrs) +{ + struct scatterlist *sg; + int i; + + BUG_ON(dir == DMA_NONE); + + for_each_sg(sgl, sg, nelems, i) + xen_unmap_single(hwdev, sg->dma_address, sg->dma_length, dir); + +} +EXPORT_SYMBOL_GPL(xen_swiotlb_unmap_sg_attrs); + +void +xen_swiotlb_unmap_sg(struct device *hwdev, struct scatterlist *sgl, int nelems, + enum dma_data_direction dir) +{ + return xen_swiotlb_unmap_sg_attrs(hwdev, sgl, nelems, dir, NULL); +} +EXPORT_SYMBOL_GPL(xen_swiotlb_unmap_sg); + +/* + * Make physical memory consistent for a set of streaming mode DMA translations + * after a transfer. + * + * The same as swiotlb_sync_single_* but for a scatter-gather list, same rules + * and usage. + */ +static void +xen_swiotlb_sync_sg(struct device *hwdev, struct scatterlist *sgl, + int nelems, enum dma_data_direction dir, + enum dma_sync_target target) +{ + struct scatterlist *sg; + int i; + + for_each_sg(sgl, sg, nelems, i) + xen_swiotlb_sync_single(hwdev, sg->dma_address, + sg->dma_length, dir, target); +} + +void +xen_swiotlb_sync_sg_for_cpu(struct device *hwdev, struct scatterlist *sg, + int nelems, enum dma_data_direction dir) +{ + xen_swiotlb_sync_sg(hwdev, sg, nelems, dir, SYNC_FOR_CPU); +} +EXPORT_SYMBOL_GPL(xen_swiotlb_sync_sg_for_cpu); + +void +xen_swiotlb_sync_sg_for_device(struct device *hwdev, struct scatterlist *sg, + int nelems, enum dma_data_direction dir) +{ + xen_swiotlb_sync_sg(hwdev, sg, nelems, dir, SYNC_FOR_DEVICE); +} +EXPORT_SYMBOL_GPL(xen_swiotlb_sync_sg_for_device); + +int +xen_swiotlb_dma_mapping_error(struct device *hwdev, dma_addr_t dma_addr) +{ + return !dma_addr; +} +EXPORT_SYMBOL_GPL(xen_swiotlb_dma_mapping_error); + +/* + * Return whether the given device DMA address mask can be supported + * properly. For example, if your device can only drive the low 24-bits + * during bus mastering, then you would pass 0x00ffffff as the mask to + * this function. + */ +int +xen_swiotlb_dma_supported(struct device *hwdev, u64 mask) +{ + return xen_virt_to_bus(xen_io_tlb_end - 1) <= mask; +} +EXPORT_SYMBOL_GPL(xen_swiotlb_dma_supported); |