From 54d5102fac3dd4034104e1b38a44a873d5f3a8d3 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 13 Oct 2009 12:30:40 +0900 Subject: cdrom: gdrom: Kill off PHYSADDR use. PHYSADDR() is gone, and completely unecessary in all of the cases the gdrom driver was using it. Kill off all references to it, and change the one legitimate use over to virt_to_phys() instead. Signed-off-by: Paul Mundt --- drivers/cdrom/gdrom.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/cdrom/gdrom.c b/drivers/cdrom/gdrom.c index a762283d2a2..e789e6c9a42 100644 --- a/drivers/cdrom/gdrom.c +++ b/drivers/cdrom/gdrom.c @@ -214,7 +214,7 @@ static void gdrom_spicommand(void *spi_string, int buflen) gdrom_getsense(NULL); return; } - outsw(PHYSADDR(GDROM_DATA_REG), cmd, 6); + outsw(GDROM_DATA_REG, cmd, 6); } @@ -298,7 +298,7 @@ static int gdrom_readtoc_cmd(struct gdromtoc *toc, int session) err = -EINVAL; goto cleanup_readtoc; } - insw(PHYSADDR(GDROM_DATA_REG), toc, tocsize/2); + insw(GDROM_DATA_REG, toc, tocsize/2); if (gd.status & 0x01) err = -EINVAL; @@ -449,7 +449,7 @@ static int gdrom_getsense(short *bufstring) GDROM_DEFAULT_TIMEOUT); if (gd.pending) goto cleanup_sense; - insw(PHYSADDR(GDROM_DATA_REG), &sense, sense_command->buflen/2); + insw(GDROM_DATA_REG, &sense, sense_command->buflen/2); if (sense[1] & 40) { printk(KERN_INFO "GDROM: Drive not ready - command aborted\n"); goto cleanup_sense; @@ -586,7 +586,7 @@ static void gdrom_readdisk_dma(struct work_struct *work) spin_unlock(&gdrom_lock); block = blk_rq_pos(req)/GD_TO_BLK + GD_SESSION_OFFSET; block_cnt = blk_rq_sectors(req)/GD_TO_BLK; - ctrl_outl(PHYSADDR(req->buffer), GDROM_DMA_STARTADDR_REG); + ctrl_outl(virt_to_phys(req->buffer), GDROM_DMA_STARTADDR_REG); ctrl_outl(block_cnt * GDROM_HARD_SECTOR, GDROM_DMA_LENGTH_REG); ctrl_outl(1, GDROM_DMA_DIRECTION_REG); ctrl_outl(1, GDROM_DMA_ENABLE_REG); @@ -615,7 +615,7 @@ static void gdrom_readdisk_dma(struct work_struct *work) cpu_relax(); gd.pending = 1; gd.transfer = 1; - outsw(PHYSADDR(GDROM_DATA_REG), &read_command->cmd, 6); + outsw(GDROM_DATA_REG, &read_command->cmd, 6); timeout = jiffies + HZ / 2; /* Wait for any pending DMA to finish */ while (ctrl_inb(GDROM_DMA_STATUS_REG) && -- cgit v1.2.3 From 913df4453f85f1fe79b35ecf3c9a0c0b707d22a2 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 13 Oct 2009 12:35:30 +0900 Subject: sh: maple: PHYSADDR() -> virt_to_phys() conversion. Maple's abuse of PHYSADDR() likewise can be converted to virt_to_phys() for its cases, although in practice this really wants explicit remapping. Signed-off-by: Paul Mundt --- drivers/sh/maple/maple.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/sh/maple/maple.c b/drivers/sh/maple/maple.c index 93c20e135ee..4e8f57d4131 100644 --- a/drivers/sh/maple/maple.c +++ b/drivers/sh/maple/maple.c @@ -106,7 +106,7 @@ static void maple_dma_reset(void) * max delay is 11 */ ctrl_outl(MAPLE_2MBPS | MAPLE_TIMEOUT(0xFFFF), MAPLE_SPEED); - ctrl_outl(PHYSADDR(maple_sendbuf), MAPLE_DMAADDR); + ctrl_outl(virt_to_phys(maple_sendbuf), MAPLE_DMAADDR); ctrl_outl(1, MAPLE_ENABLE); } @@ -258,7 +258,7 @@ static void maple_build_block(struct mapleq *mq) maple_lastptr = maple_sendptr; *maple_sendptr++ = (port << 16) | len | 0x80000000; - *maple_sendptr++ = PHYSADDR(mq->recvbuf->buf); + *maple_sendptr++ = virt_to_phys(mq->recvbuf->buf); *maple_sendptr++ = mq->command | (to << 8) | (from << 16) | (len << 24); while (len-- > 0) -- cgit v1.2.3 From a87d563873a6f1ee98233b57af665f2d0fc90ebb Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Fri, 2 Oct 2009 02:22:09 +0000 Subject: mfd: Add SuperH Mobile SDHI platform driver This patch adds an MFD driver for the SuperH Mobile SDHI hardware block. At this point the driver simply wraps the tmio-mmc driver with some clock code. In the future this driver is the place to put SDHI specific hotplug code. Signed-off-by: Magnus Damm Signed-off-by: Paul Mundt --- drivers/mfd/Kconfig | 8 +++ drivers/mfd/Makefile | 1 + drivers/mfd/sh_mobile_sdhi.c | 145 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 154 insertions(+) create mode 100644 drivers/mfd/sh_mobile_sdhi.c (limited to 'drivers') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 570be139f9d..96956b3cc17 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -35,6 +35,14 @@ config MFD_ASIC3 This driver supports the ASIC3 multifunction chip found on many PDAs (mainly iPAQ and HTC based ones) +config MFD_SH_MOBILE_SDHI + bool "Support for SuperH Mobile SDHI" + depends on SUPERH + select MFD_CORE + ---help--- + This driver supports the SDHI hardware block found in many + SuperH Mobile SoCs. + config MFD_DM355EVM_MSP bool "DaVinci DM355 EVM microcontroller" depends on I2C && MACH_DAVINCI_DM355_EVM diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index f3b277b90d4..d9522943d2f 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_MFD_SM501) += sm501.o obj-$(CONFIG_MFD_ASIC3) += asic3.o +obj-$(CONFIG_MFD_SH_MOBILE_SDHI) += sh_mobile_sdhi.o obj-$(CONFIG_HTC_EGPIO) += htc-egpio.o obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o diff --git a/drivers/mfd/sh_mobile_sdhi.c b/drivers/mfd/sh_mobile_sdhi.c new file mode 100644 index 00000000000..56f72cc1d56 --- /dev/null +++ b/drivers/mfd/sh_mobile_sdhi.c @@ -0,0 +1,145 @@ +/* + * SuperH Mobile SDHI + * + * Copyright (C) 2009 Magnus Damm + * + * 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. + * + * Based on "Compaq ASIC3 support": + * + * Copyright 2001 Compaq Computer Corporation. + * Copyright 2004-2005 Phil Blundell + * Copyright 2007-2008 OpenedHand Ltd. + * + * Authors: Phil Blundell , + * Samuel Ortiz + * + */ + +#include +#include +#include + +#include +#include + +struct sh_mobile_sdhi { + struct clk *clk; + struct tmio_mmc_data mmc_data; + struct mfd_cell cell_mmc; +}; + +static struct resource sh_mobile_sdhi_resources[] = { + { + .start = 0x000, + .end = 0x1ff, + .flags = IORESOURCE_MEM, + }, + { + .start = 0, + .end = 0, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct mfd_cell sh_mobile_sdhi_cell = { + .name = "tmio-mmc", + .num_resources = ARRAY_SIZE(sh_mobile_sdhi_resources), + .resources = sh_mobile_sdhi_resources, +}; + +static int __init sh_mobile_sdhi_probe(struct platform_device *pdev) +{ + struct sh_mobile_sdhi *priv; + struct resource *mem; + char clk_name[8]; + int ret, irq; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) + dev_err(&pdev->dev, "missing MEM resource\n"); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + dev_err(&pdev->dev, "missing IRQ resource\n"); + + if (!mem || (irq < 0)) + return -EINVAL; + + priv = kzalloc(sizeof(struct sh_mobile_sdhi), GFP_KERNEL); + if (priv == NULL) { + dev_err(&pdev->dev, "kzalloc failed\n"); + return -ENOMEM; + } + + snprintf(clk_name, sizeof(clk_name), "sdhi%d", pdev->id); + priv->clk = clk_get(&pdev->dev, clk_name); + if (IS_ERR(priv->clk)) { + dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name); + ret = PTR_ERR(priv->clk); + kfree(priv); + return ret; + } + + clk_enable(priv->clk); + + /* FIXME: silly const unsigned int hclk */ + *(unsigned int *)&priv->mmc_data.hclk = clk_get_rate(priv->clk); + + memcpy(&priv->cell_mmc, &sh_mobile_sdhi_cell, sizeof(priv->cell_mmc)); + priv->cell_mmc.driver_data = &priv->mmc_data; + priv->cell_mmc.platform_data = &priv->cell_mmc; + priv->cell_mmc.data_size = sizeof(priv->cell_mmc); + + platform_set_drvdata(pdev, priv); + + ret = mfd_add_devices(&pdev->dev, pdev->id, + &priv->cell_mmc, 1, mem, irq); + if (ret) { + clk_disable(priv->clk); + clk_put(priv->clk); + kfree(priv); + } + + return ret; +} + +static int sh_mobile_sdhi_remove(struct platform_device *pdev) +{ + struct sh_mobile_sdhi *priv = platform_get_drvdata(pdev); + + mfd_remove_devices(&pdev->dev); + clk_disable(priv->clk); + clk_put(priv->clk); + kfree(priv); + + return 0; +} + +static struct platform_driver sh_mobile_sdhi_driver = { + .driver = { + .name = "sh_mobile_sdhi", + .owner = THIS_MODULE, + }, + .probe = sh_mobile_sdhi_probe, + .remove = __devexit_p(sh_mobile_sdhi_remove), +}; + +static int __init sh_mobile_sdhi_init(void) +{ + return platform_driver_register(&sh_mobile_sdhi_driver); +} + +static void __exit sh_mobile_sdhi_exit(void) +{ + platform_driver_unregister(&sh_mobile_sdhi_driver); +} + +module_init(sh_mobile_sdhi_init); +module_exit(sh_mobile_sdhi_exit); + +MODULE_DESCRIPTION("SuperH Mobile SDHI driver"); +MODULE_AUTHOR("Magnus Damm"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 6d522b05984404d6c22cc5dfd2c989bbcf3df8c9 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Fri, 2 Oct 2009 02:22:21 +0000 Subject: mmc: Add SuperH to the tmio-mmc Kconfig Add SUPERH to the Kconfig dependencies for tmio_mmc. This change allows us to drive the SDHI hardware blocks found in SuperH Mobile with tmio_mmc. Signed-off-by: Magnus Damm Signed-off-by: Paul Mundt --- drivers/mmc/host/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 432ae8358c8..e04b751680d 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -329,7 +329,7 @@ config MMC_SDRICOH_CS config MMC_TMIO tristate "Toshiba Mobile IO Controller (TMIO) MMC/SD function support" - depends on MFD_TMIO || MFD_ASIC3 + depends on MFD_TMIO || MFD_ASIC3 || SUPERH help This provides support for the SD/MMC cell found in TC6393XB, T7L66XB and also HTC ASIC3 -- cgit v1.2.3 From 9b798d50df3a98d22a6cbae565d9f4f630d161a6 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 27 Oct 2009 11:36:43 +0900 Subject: sh: intc: Make ack_regs generally available. Currently this is ifdef'ed under SH-3 and SH-4A, but there are other CPUs that will need this as well. Given the size of the existing data structures, this doesn't cause any additional cacheline utilization for the existing users, so has no direct impact on the data structures. Signed-off-by: Paul Mundt --- drivers/sh/intc.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/sh/intc.c b/drivers/sh/intc.c index 559b5fe9dc0..94e6e46ff82 100644 --- a/drivers/sh/intc.c +++ b/drivers/sh/intc.c @@ -70,9 +70,7 @@ static LIST_HEAD(intc_list); #endif static unsigned int intc_prio_level[NR_IRQS]; /* for now */ -#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A) static unsigned long ack_handle[NR_IRQS]; -#endif static inline struct intc_desc_int *get_intc_desc(unsigned int irq) { @@ -250,7 +248,6 @@ static int intc_set_wake(unsigned int irq, unsigned int on) return 0; /* allow wakeup, but setup hardware in intc_suspend() */ } -#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A) static void intc_mask_ack(unsigned int irq) { struct intc_desc_int *d = get_intc_desc(irq); @@ -282,7 +279,6 @@ static void intc_mask_ack(unsigned int irq) } } } -#endif static struct intc_handle_int *intc_find_irq(struct intc_handle_int *hp, unsigned int nr_hp, @@ -501,7 +497,6 @@ static unsigned int __init intc_prio_data(struct intc_desc *desc, return 0; } -#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A) static unsigned int __init intc_ack_data(struct intc_desc *desc, struct intc_desc_int *d, intc_enum enum_id) @@ -533,7 +528,6 @@ static unsigned int __init intc_ack_data(struct intc_desc *desc, return 0; } -#endif static unsigned int __init intc_sense_data(struct intc_desc *desc, struct intc_desc_int *d, @@ -641,10 +635,8 @@ static void __init intc_register_irq(struct intc_desc *desc, /* irq should be disabled by default */ d->chip.mask(irq); -#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A) if (desc->ack_regs) ack_handle[irq] = intc_ack_data(desc, d, enum_id); -#endif } static unsigned int __init save_reg(struct intc_desc_int *d, @@ -681,10 +673,8 @@ void __init register_intc_controller(struct intc_desc *desc) d->nr_reg = desc->mask_regs ? desc->nr_mask_regs * 2 : 0; d->nr_reg += desc->prio_regs ? desc->nr_prio_regs * 2 : 0; d->nr_reg += desc->sense_regs ? desc->nr_sense_regs : 0; - -#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A) d->nr_reg += desc->ack_regs ? desc->nr_ack_regs : 0; -#endif + d->reg = kzalloc(d->nr_reg * sizeof(*d->reg), GFP_NOWAIT); #ifdef CONFIG_SMP d->smp = kzalloc(d->nr_reg * sizeof(*d->smp), GFP_NOWAIT); @@ -727,14 +717,12 @@ void __init register_intc_controller(struct intc_desc *desc) d->chip.set_type = intc_set_sense; d->chip.set_wake = intc_set_wake; -#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A) if (desc->ack_regs) { for (i = 0; i < desc->nr_ack_regs; i++) k += save_reg(d, k, desc->ack_regs[i].set_reg, 0); d->chip.mask_ack = intc_mask_ack; } -#endif BUG_ON(k > 256); /* _INTC_ADDR_E() and _INTC_ADDR_D() are 8 bits */ -- cgit v1.2.3 From 1ce7b039b5029ab698f9d64c0ad603794bc31ae7 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Mon, 2 Nov 2009 10:30:26 +0900 Subject: sh: intc: dynamic IRQ support. This adds support for dynamic IRQ allocation/deallocation for all parts using the SH-style vectored IRQs. While this is not inherently INTC-specific, the INTC code is the main tie-in for vectored IRQ registration, and is the only place that a full view of the utilized vector map is possible. The implementation is fairly straightforward, implementing a flat IRQ map where each registered vector is reserved, allowing us to scan for holes and dynamically wire up IRQs lazily later on in the boot stage. This piggybacks on top of sparseirq in order to make the best use of the available vector space. Dynamic IRQs can be used for any number of things, ranging from MSI in the SH-X3 PCIe case down to demux vectors for board FPGAs and system controllers that presently allocate an arbitrary range. In the latter case, this also allows those platforms to use sparseirq without blowing up, which brings us one step closer to enabling sparseirq as the default for all platform and CPU combinations. Signed-off-by: Paul Mundt --- drivers/sh/intc.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/sh/intc.c b/drivers/sh/intc.c index 94e6e46ff82..4789df43c0f 100644 --- a/drivers/sh/intc.c +++ b/drivers/sh/intc.c @@ -2,6 +2,7 @@ * Shared interrupt handling code for IPR and INTC2 types of IRQs. * * Copyright (C) 2007, 2008 Magnus Damm + * Copyright (C) 2009 Paul Mundt * * Based on intc2.c and ipr.c * @@ -24,6 +25,7 @@ #include #include #include +#include #define _INTC_MK(fn, mode, addr_e, addr_d, width, shift) \ ((shift) | ((width) << 5) | ((fn) << 9) | ((mode) << 13) | \ @@ -59,6 +61,20 @@ struct intc_desc_int { static LIST_HEAD(intc_list); +/* + * The intc_irq_map provides a global map of bound IRQ vectors for a + * given platform. Allocation of IRQs are either static through the CPU + * vector map, or dynamic in the case of board mux vectors or MSI. + * + * As this is a central point for all IRQ controllers on the system, + * each of the available sources are mapped out here. This combined with + * sparseirq makes it quite trivial to keep the vector map tightly packed + * when dynamically creating IRQs, as well as tying in to otherwise + * unused irq_desc positions in the sparse array. + */ +static DECLARE_BITMAP(intc_irq_map, NR_IRQS); +static DEFINE_SPINLOCK(vector_lock); + #ifdef CONFIG_SMP #define IS_SMP(x) x.smp #define INTC_REG(d, x, c) (d->reg[(x)] + ((d->smp[(x)] & 0xff) * c)) @@ -566,6 +582,11 @@ static void __init intc_register_irq(struct intc_desc *desc, struct intc_handle_int *hp; unsigned int data[2], primary; + /* + * Register the IRQ position with the global IRQ map + */ + set_bit(irq, intc_irq_map); + /* Prefer single interrupt source bitmap over other combinations: * 1. bitmap, single interrupt source * 2. priority, single interrupt source @@ -844,5 +865,66 @@ static int __init register_intc_sysdevs(void) return error; } - device_initcall(register_intc_sysdevs); + +/* + * Dynamic IRQ allocation and deallocation + */ +static unsigned int create_irq_on_node(unsigned int irq_want, int node) +{ + unsigned int irq = 0, new; + unsigned long flags; + struct irq_desc *desc; + + spin_lock_irqsave(&vector_lock, flags); + + /* + * First try the wanted IRQ, then scan. + */ + if (test_and_set_bit(irq_want, intc_irq_map)) { + new = find_first_zero_bit(intc_irq_map, nr_irqs); + if (unlikely(new == nr_irqs)) + goto out_unlock; + + desc = irq_to_desc_alloc_node(new, node); + if (unlikely(!desc)) { + pr_info("can't get irq_desc for %d\n", new); + goto out_unlock; + } + + desc = move_irq_desc(desc, node); + __set_bit(new, intc_irq_map); + irq = new; + } + +out_unlock: + spin_unlock_irqrestore(&vector_lock, flags); + + if (irq > 0) + dynamic_irq_init(irq); + + return irq; +} + +int create_irq(void) +{ + int nid = cpu_to_node(smp_processor_id()); + int irq; + + irq = create_irq_on_node(NR_IRQS_LEGACY, nid); + if (irq == 0) + irq = -1; + + return irq; +} + +void destroy_irq(unsigned int irq) +{ + unsigned long flags; + + dynamic_irq_cleanup(irq); + + spin_lock_irqsave(&vector_lock, flags); + __clear_bit(irq, intc_irq_map); + spin_unlock_irqrestore(&vector_lock, flags); +} -- cgit v1.2.3 From 45b9deaf14e74543371aa8faea69c14e27b038c6 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Mon, 2 Nov 2009 15:43:20 +0900 Subject: sh: intc: Handle legacy IRQ reservation in vector map. Different CPUs will have different starting vectors, with varying amounts of reserved or unusable vector space prior to the first slot. This introduces a legacy vector reservation system that inserts itself in between the CPU vector map registration and the platform specific IRQ setup. This works fine in practice as the only new vectors that boards need to establish on their own should be dynamically allocated rather than arbitrarily assigned. As a plus, this also makes all of the converted platforms sparseirq ready. Signed-off-by: Paul Mundt --- drivers/sh/intc.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'drivers') diff --git a/drivers/sh/intc.c b/drivers/sh/intc.c index 4789df43c0f..a7e5c2e9986 100644 --- a/drivers/sh/intc.c +++ b/drivers/sh/intc.c @@ -928,3 +928,28 @@ void destroy_irq(unsigned int irq) __clear_bit(irq, intc_irq_map); spin_unlock_irqrestore(&vector_lock, flags); } + +int reserve_irq_vector(unsigned int irq) +{ + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&vector_lock, flags); + if (test_and_set_bit(irq, intc_irq_map)) + ret = -EBUSY; + spin_unlock_irqrestore(&vector_lock, flags); + + return ret; +} + +void reserve_irq_legacy(void) +{ + unsigned long flags; + int i, j; + + spin_lock_irqsave(&vector_lock, flags); + j = find_first_bit(intc_irq_map, nr_irqs); + for (i = 0; i < j; i++) + __set_bit(i, intc_irq_map); + spin_unlock_irqrestore(&vector_lock, flags); +} -- cgit v1.2.3 From 5c1a56b5f616f7063f91eb85f0ea209658f387dc Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 4 Nov 2009 15:59:04 +0900 Subject: video: sh_mobile_lcdcfb: Don't attempt to map zero-length scatterlists. More aggressive DMA mapping debugging has uncovered a long-standing buglet in the way that the sh_mobile_lcdcfb driver implements its deferred I/O callback. When used as a console driver the acceleration routines are called by the kernel which subsequently cause the deferred I/O work to be scheduled, resulting in the deferred I/O callback being entered without any dirty pages on the pagelist (the normal case for userspace accesses). It's also possible to get in to this situation via explicit calling of fsync() when nothing has dirtied the region. Unfortunately it's not sufficient to skip over the callback when the pagelist is empty given the console driver use case, so instead the callback has to conditionalize the work for panel updates and DMA mapping depending on whether anything is resident on the pagelist or not. Reported-by: Guennadi Liakhovetski Signed-off-by: Paul Mundt --- drivers/video/sh_mobile_lcdcfb.c | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c index 3ad5157f989..b4b5de930cf 100644 --- a/drivers/video/sh_mobile_lcdcfb.c +++ b/drivers/video/sh_mobile_lcdcfb.c @@ -281,18 +281,34 @@ static void sh_mobile_lcdc_deferred_io(struct fb_info *info, struct list_head *pagelist) { struct sh_mobile_lcdc_chan *ch = info->par; - unsigned int nr_pages; /* enable clocks before accessing hardware */ sh_mobile_lcdc_clk_on(ch->lcdc); - nr_pages = sh_mobile_lcdc_sginit(info, pagelist); - dma_map_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE); - - /* trigger panel update */ - lcdc_write_chan(ch, LDSM2R, 1); - - dma_unmap_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE); + /* + * It's possible to get here without anything on the pagelist via + * sh_mobile_lcdc_deferred_io_touch() or via a userspace fsync() + * invocation. In the former case, the acceleration routines are + * stepped in to when using the framebuffer console causing the + * workqueue to be scheduled without any dirty pages on the list. + * + * Despite this, a panel update is still needed given that the + * acceleration routines have their own methods for writing in + * that still need to be updated. + * + * The fsync() and empty pagelist case could be optimized for, + * but we don't bother, as any application exhibiting such + * behaviour is fundamentally broken anyways. + */ + if (!list_empty(pagelist)) { + unsigned int nr_pages = sh_mobile_lcdc_sginit(info, pagelist); + + /* trigger panel update */ + dma_map_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE); + lcdc_write_chan(ch, LDSM2R, 1); + dma_unmap_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE); + } else + lcdc_write_chan(ch, LDSM2R, 1); } static void sh_mobile_lcdc_deferred_io_touch(struct fb_info *info) -- cgit v1.2.3 From b9e05c64a02a1e699925cb49dd5542087eba0c3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 24 Nov 2009 22:07:23 +0100 Subject: rtc: don't use __exit_p to wrap ds1302_rtc_remove MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function ds1302_rtc_remove is defined using __devexit, so don't use __exit_p but __devexit_p to wrap it. Signed-off-by: Uwe Kleine-König Cc: Alessandro Zummo Cc: linux-kernel@vger.kernel.org Cc: Paul Gortmaker Cc: rtc-linux@googlegroups.com Signed-off-by: Paul Mundt --- drivers/rtc/rtc-ds1302.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-ds1302.c b/drivers/rtc/rtc-ds1302.c index d490628b64d..1e73c8f42e3 100644 --- a/drivers/rtc/rtc-ds1302.c +++ b/drivers/rtc/rtc-ds1302.c @@ -201,7 +201,7 @@ static struct platform_driver ds1302_platform_driver = { .name = DRV_NAME, .owner = THIS_MODULE, }, - .remove = __exit_p(ds1302_rtc_remove), + .remove = __devexit_p(ds1302_rtc_remove), }; static int __init ds1302_rtc_init(void) -- cgit v1.2.3 From b9e39c89a9639e5005d8225a23fb7faf118a85eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 24 Nov 2009 22:07:32 +0100 Subject: serial: sh-sci: don't use __devexit_p to wrap sci_remove MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function sci_remove is defined without any section modifier, so don't use __devexit_p to wrap it. Signed-off-by: Uwe Kleine-König Cc: Magnus Damm Cc: linux-kernel@vger.kernel.org Signed-off-by: Paul Mundt --- drivers/serial/sh-sci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c index 6498bd1fb6d..89421fa0d25 100644 --- a/drivers/serial/sh-sci.c +++ b/drivers/serial/sh-sci.c @@ -1370,7 +1370,7 @@ static struct dev_pm_ops sci_dev_pm_ops = { static struct platform_driver sci_driver = { .probe = sci_probe, - .remove = __devexit_p(sci_remove), + .remove = sci_remove, .driver = { .name = "sh-sci", .owner = THIS_MODULE, -- cgit v1.2.3 From edad1f208e6edabb917e4f8a33c7e45bf78bb79d Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 25 Nov 2009 16:23:35 +0900 Subject: serial: sh-sci: Depend on HAVE_CLK unconditionally. The sh-sci code conditionalized the clock framework support in order to give the other platforms a chance to catch up. sh64 supported this some time ago and the forthcoming ARM changes handle this as well, this leaves h8300 as the odd one out. H8300 has had since 2.5 to merge it's sh-sci support upstream, and has yet to do so. At this point I will no longer be holding back the driver to support an unreponsive architecture, 7 years is quite enough of a grace period. Support is easily implemented on the architecture if and when it ever decides to merge its changes upstream. Signed-off-by: Paul Mundt --- drivers/serial/Kconfig | 2 +- drivers/serial/sh-sci.c | 53 +------------------------------------------------ drivers/serial/sh-sci.h | 2 +- 3 files changed, 3 insertions(+), 54 deletions(-) (limited to 'drivers') diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index e5225725727..30b58eeb439 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -996,7 +996,7 @@ config SERIAL_IP22_ZILOG_CONSOLE config SERIAL_SH_SCI tristate "SuperH SCI(F) serial port support" - depends on SUPERH || H8300 + depends on HAVE_CLK && (SUPERH || H8300) select SERIAL_CORE config SERIAL_SH_SCI_NR_UARTS diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c index 89421fa0d25..972fca0a3ef 100644 --- a/drivers/serial/sh-sci.c +++ b/drivers/serial/sh-sci.c @@ -50,7 +50,6 @@ #include #ifdef CONFIG_SUPERH -#include #include #endif @@ -79,22 +78,18 @@ struct sci_port { struct timer_list break_timer; int break_flag; -#ifdef CONFIG_HAVE_CLK /* Interface clock */ struct clk *iclk; /* Data clock */ struct clk *dclk; -#endif + struct list_head node; }; struct sh_sci_priv { spinlock_t lock; struct list_head ports; - -#ifdef CONFIG_HAVE_CLK struct notifier_block clk_nb; -#endif }; /* Function prototypes */ @@ -156,32 +151,6 @@ static void sci_poll_put_char(struct uart_port *port, unsigned char c) } #endif /* CONFIG_CONSOLE_POLL || CONFIG_SERIAL_SH_SCI_CONSOLE */ -#if defined(__H8300S__) -enum { sci_disable, sci_enable }; - -static void h8300_sci_config(struct uart_port *port, unsigned int ctrl) -{ - volatile unsigned char *mstpcrl = (volatile unsigned char *)MSTPCRL; - int ch = (port->mapbase - SMR0) >> 3; - unsigned char mask = 1 << (ch+1); - - if (ctrl == sci_disable) - *mstpcrl |= mask; - else - *mstpcrl &= ~mask; -} - -static void h8300_sci_enable(struct uart_port *port) -{ - h8300_sci_config(port, sci_enable); -} - -static void h8300_sci_disable(struct uart_port *port) -{ - h8300_sci_config(port, sci_disable); -} -#endif - #if defined(__H8300H__) || defined(__H8300S__) static void sci_init_pins(struct uart_port *port, unsigned int cflag) { @@ -733,7 +702,6 @@ static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr) return ret; } -#ifdef CONFIG_HAVE_CLK /* * Here we define a transistion notifier so that we can update all of our * ports' baud rate when the peripheral clock changes. @@ -751,7 +719,6 @@ static int sci_notifier(struct notifier_block *self, spin_lock_irqsave(&priv->lock, flags); list_for_each_entry(sci_port, &priv->ports, node) sci_port->port.uartclk = clk_get_rate(sci_port->dclk); - spin_unlock_irqrestore(&priv->lock, flags); } @@ -778,7 +745,6 @@ static void sci_clk_disable(struct uart_port *port) clk_disable(sci_port->dclk); } -#endif static int sci_request_irq(struct sci_port *port) { @@ -1077,21 +1043,10 @@ static void __devinit sci_init_single(struct platform_device *dev, sci_port->port.iotype = UPIO_MEM; sci_port->port.line = index; sci_port->port.fifosize = 1; - -#if defined(__H8300H__) || defined(__H8300S__) -#ifdef __H8300S__ - sci_port->enable = h8300_sci_enable; - sci_port->disable = h8300_sci_disable; -#endif - sci_port->port.uartclk = CONFIG_CPU_CLOCK; -#elif defined(CONFIG_HAVE_CLK) sci_port->iclk = p->clk ? clk_get(&dev->dev, p->clk) : NULL; sci_port->dclk = clk_get(&dev->dev, "peripheral_clk"); sci_port->enable = sci_clk_enable; sci_port->disable = sci_clk_disable; -#else -#error "Need a valid uartclk" -#endif sci_port->break_timer.data = (unsigned long)sci_port; sci_port->break_timer.function = sci_break_timer; @@ -1106,7 +1061,6 @@ static void __devinit sci_init_single(struct platform_device *dev, sci_port->type = sci_port->port.type = p->type; memcpy(&sci_port->irqs, &p->irqs, sizeof(p->irqs)); - } #ifdef CONFIG_SERIAL_SH_SCI_CONSOLE @@ -1239,14 +1193,11 @@ static int sci_remove(struct platform_device *dev) struct sci_port *p; unsigned long flags; -#ifdef CONFIG_HAVE_CLK cpufreq_unregister_notifier(&priv->clk_nb, CPUFREQ_TRANSITION_NOTIFIER); -#endif spin_lock_irqsave(&priv->lock, flags); list_for_each_entry(p, &priv->ports, node) uart_remove_one_port(&sci_uart_driver, &p->port); - spin_unlock_irqrestore(&priv->lock, flags); kfree(priv); @@ -1307,10 +1258,8 @@ static int __devinit sci_probe(struct platform_device *dev) spin_lock_init(&priv->lock); platform_set_drvdata(dev, priv); -#ifdef CONFIG_HAVE_CLK priv->clk_nb.notifier_call = sci_notifier; cpufreq_register_notifier(&priv->clk_nb, CPUFREQ_TRANSITION_NOTIFIER); -#endif if (dev->id != -1) { ret = sci_probe_single(dev, dev->id, p, &sci_ports[dev->id]); diff --git a/drivers/serial/sh-sci.h b/drivers/serial/sh-sci.h index 3e2fcf93b42..a32094eeb42 100644 --- a/drivers/serial/sh-sci.h +++ b/drivers/serial/sh-sci.h @@ -1,5 +1,5 @@ #include -#include +#include #include #if defined(CONFIG_H83007) || defined(CONFIG_H83068) -- cgit v1.2.3 From be9cd7b6f84fd0cc59c8770771073b5c66f958ac Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Fri, 27 Nov 2009 04:31:27 +0000 Subject: mfd: Add power control platform data to SDHI driver This patch adds platform data with a function for power control to the SDHI driver. The idea is that board specific code can provide their own functions so power can be enabled and disabled for the sd-cards. Signed-off-by: Magnus Damm Signed-off-by: Paul Mundt --- drivers/mfd/sh_mobile_sdhi.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/sh_mobile_sdhi.c b/drivers/mfd/sh_mobile_sdhi.c index 56f72cc1d56..03efae8041a 100644 --- a/drivers/mfd/sh_mobile_sdhi.c +++ b/drivers/mfd/sh_mobile_sdhi.c @@ -24,6 +24,7 @@ #include #include +#include struct sh_mobile_sdhi { struct clk *clk; @@ -50,6 +51,15 @@ static struct mfd_cell sh_mobile_sdhi_cell = { .resources = sh_mobile_sdhi_resources, }; +static void sh_mobile_sdhi_set_pwr(struct platform_device *tmio, int state) +{ + struct platform_device *pdev = to_platform_device(tmio->dev.parent); + struct sh_mobile_sdhi_info *p = pdev->dev.platform_data; + + if (p && p->set_pwr) + p->set_pwr(pdev, state); +} + static int __init sh_mobile_sdhi_probe(struct platform_device *pdev) { struct sh_mobile_sdhi *priv; @@ -87,6 +97,7 @@ static int __init sh_mobile_sdhi_probe(struct platform_device *pdev) /* FIXME: silly const unsigned int hclk */ *(unsigned int *)&priv->mmc_data.hclk = clk_get_rate(priv->clk); + priv->mmc_data.set_pwr = sh_mobile_sdhi_set_pwr; memcpy(&priv->cell_mmc, &sh_mobile_sdhi_cell, sizeof(priv->cell_mmc)); priv->cell_mmc.driver_data = &priv->mmc_data; -- cgit v1.2.3 From fc1d003de39c306a44abce97c346921de31277cd Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Fri, 27 Nov 2009 07:32:24 +0000 Subject: sh: Move KEYSC header file This patch moves the KEYSC header file from the SuperH specific asm directory to a place where it can be shared by multiple architectures. Signed-off-by: Magnus Damm Signed-off-by: Paul Mundt --- drivers/input/keyboard/sh_keysc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/input/keyboard/sh_keysc.c b/drivers/input/keyboard/sh_keysc.c index 887af79b7bf..076111fc72d 100644 --- a/drivers/input/keyboard/sh_keysc.c +++ b/drivers/input/keyboard/sh_keysc.c @@ -18,9 +18,9 @@ #include #include #include +#include #include #include -#include #define KYCR1_OFFS 0x00 #define KYCR2_OFFS 0x04 -- cgit v1.2.3 From fae4339919c741f89f7e293b8c646207e1df28e1 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Fri, 27 Nov 2009 07:38:01 +0000 Subject: sh: Break out SuperH PFC code This file breaks out the SuperH PFC code from arch/sh/kernel/gpio.c + arch/sh/include/asm/gpio.h to drivers/sh/pfc.c + include/linux/sh_pfc.h. Similar to the INTC stuff. The non-SuperH specific file location makes it possible to share the code between multiple architectures. Signed-off-by: Magnus Damm Signed-off-by: Paul Mundt --- drivers/sh/Makefile | 1 + drivers/sh/pfc.c | 584 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 585 insertions(+) create mode 100644 drivers/sh/pfc.c (limited to 'drivers') diff --git a/drivers/sh/Makefile b/drivers/sh/Makefile index 6a025cefe6d..4956bf1f213 100644 --- a/drivers/sh/Makefile +++ b/drivers/sh/Makefile @@ -3,4 +3,5 @@ # obj-$(CONFIG_SUPERHYWAY) += superhyway/ obj-$(CONFIG_MAPLE) += maple/ +obj-$(CONFIG_GENERIC_GPIO) += pfc.o obj-y += intc.o diff --git a/drivers/sh/pfc.c b/drivers/sh/pfc.c new file mode 100644 index 00000000000..d22e5af699f --- /dev/null +++ b/drivers/sh/pfc.c @@ -0,0 +1,584 @@ +/* + * Pinmuxed GPIO support for SuperH. + * + * Copyright (C) 2008 Magnus Damm + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int enum_in_range(pinmux_enum_t enum_id, struct pinmux_range *r) +{ + if (enum_id < r->begin) + return 0; + + if (enum_id > r->end) + return 0; + + return 1; +} + +static unsigned long gpio_read_raw_reg(unsigned long reg, + unsigned long reg_width) +{ + switch (reg_width) { + case 8: + return ctrl_inb(reg); + case 16: + return ctrl_inw(reg); + case 32: + return ctrl_inl(reg); + } + + BUG(); + return 0; +} + +static void gpio_write_raw_reg(unsigned long reg, + unsigned long reg_width, + unsigned long data) +{ + switch (reg_width) { + case 8: + ctrl_outb(data, reg); + return; + case 16: + ctrl_outw(data, reg); + return; + case 32: + ctrl_outl(data, reg); + return; + } + + BUG(); +} + +static void gpio_write_bit(struct pinmux_data_reg *dr, + unsigned long in_pos, unsigned long value) +{ + unsigned long pos; + + pos = dr->reg_width - (in_pos + 1); + +#ifdef DEBUG + pr_info("write_bit addr = %lx, value = %ld, pos = %ld, " + "r_width = %ld\n", + dr->reg, !!value, pos, dr->reg_width); +#endif + + if (value) + set_bit(pos, &dr->reg_shadow); + else + clear_bit(pos, &dr->reg_shadow); + + gpio_write_raw_reg(dr->reg, dr->reg_width, dr->reg_shadow); +} + +static int gpio_read_reg(unsigned long reg, unsigned long reg_width, + unsigned long field_width, unsigned long in_pos) +{ + unsigned long data, mask, pos; + + data = 0; + mask = (1 << field_width) - 1; + pos = reg_width - ((in_pos + 1) * field_width); + +#ifdef DEBUG + pr_info("read_reg: addr = %lx, pos = %ld, " + "r_width = %ld, f_width = %ld\n", + reg, pos, reg_width, field_width); +#endif + + data = gpio_read_raw_reg(reg, reg_width); + return (data >> pos) & mask; +} + +static void gpio_write_reg(unsigned long reg, unsigned long reg_width, + unsigned long field_width, unsigned long in_pos, + unsigned long value) +{ + unsigned long mask, pos; + + mask = (1 << field_width) - 1; + pos = reg_width - ((in_pos + 1) * field_width); + +#ifdef DEBUG + pr_info("write_reg addr = %lx, value = %ld, pos = %ld, " + "r_width = %ld, f_width = %ld\n", + reg, value, pos, reg_width, field_width); +#endif + + mask = ~(mask << pos); + value = value << pos; + + switch (reg_width) { + case 8: + ctrl_outb((ctrl_inb(reg) & mask) | value, reg); + break; + case 16: + ctrl_outw((ctrl_inw(reg) & mask) | value, reg); + break; + case 32: + ctrl_outl((ctrl_inl(reg) & mask) | value, reg); + break; + } +} + +static int setup_data_reg(struct pinmux_info *gpioc, unsigned gpio) +{ + struct pinmux_gpio *gpiop = &gpioc->gpios[gpio]; + struct pinmux_data_reg *data_reg; + int k, n; + + if (!enum_in_range(gpiop->enum_id, &gpioc->data)) + return -1; + + k = 0; + while (1) { + data_reg = gpioc->data_regs + k; + + if (!data_reg->reg_width) + break; + + for (n = 0; n < data_reg->reg_width; n++) { + if (data_reg->enum_ids[n] == gpiop->enum_id) { + gpiop->flags &= ~PINMUX_FLAG_DREG; + gpiop->flags |= (k << PINMUX_FLAG_DREG_SHIFT); + gpiop->flags &= ~PINMUX_FLAG_DBIT; + gpiop->flags |= (n << PINMUX_FLAG_DBIT_SHIFT); + return 0; + } + } + k++; + } + + BUG(); + + return -1; +} + +static void setup_data_regs(struct pinmux_info *gpioc) +{ + struct pinmux_data_reg *drp; + int k; + + for (k = gpioc->first_gpio; k <= gpioc->last_gpio; k++) + setup_data_reg(gpioc, k); + + k = 0; + while (1) { + drp = gpioc->data_regs + k; + + if (!drp->reg_width) + break; + + drp->reg_shadow = gpio_read_raw_reg(drp->reg, drp->reg_width); + k++; + } +} + +static int get_data_reg(struct pinmux_info *gpioc, unsigned gpio, + struct pinmux_data_reg **drp, int *bitp) +{ + struct pinmux_gpio *gpiop = &gpioc->gpios[gpio]; + int k, n; + + if (!enum_in_range(gpiop->enum_id, &gpioc->data)) + return -1; + + k = (gpiop->flags & PINMUX_FLAG_DREG) >> PINMUX_FLAG_DREG_SHIFT; + n = (gpiop->flags & PINMUX_FLAG_DBIT) >> PINMUX_FLAG_DBIT_SHIFT; + *drp = gpioc->data_regs + k; + *bitp = n; + return 0; +} + +static int get_config_reg(struct pinmux_info *gpioc, pinmux_enum_t enum_id, + struct pinmux_cfg_reg **crp, int *indexp, + unsigned long **cntp) +{ + struct pinmux_cfg_reg *config_reg; + unsigned long r_width, f_width; + int k, n; + + k = 0; + while (1) { + config_reg = gpioc->cfg_regs + k; + + r_width = config_reg->reg_width; + f_width = config_reg->field_width; + + if (!r_width) + break; + for (n = 0; n < (r_width / f_width) * 1 << f_width; n++) { + if (config_reg->enum_ids[n] == enum_id) { + *crp = config_reg; + *indexp = n; + *cntp = &config_reg->cnt[n / (1 << f_width)]; + return 0; + } + } + k++; + } + + return -1; +} + +static int get_gpio_enum_id(struct pinmux_info *gpioc, unsigned gpio, + int pos, pinmux_enum_t *enum_idp) +{ + pinmux_enum_t enum_id = gpioc->gpios[gpio].enum_id; + pinmux_enum_t *data = gpioc->gpio_data; + int k; + + if (!enum_in_range(enum_id, &gpioc->data)) { + if (!enum_in_range(enum_id, &gpioc->mark)) { + pr_err("non data/mark enum_id for gpio %d\n", gpio); + return -1; + } + } + + if (pos) { + *enum_idp = data[pos + 1]; + return pos + 1; + } + + for (k = 0; k < gpioc->gpio_data_size; k++) { + if (data[k] == enum_id) { + *enum_idp = data[k + 1]; + return k + 1; + } + } + + pr_err("cannot locate data/mark enum_id for gpio %d\n", gpio); + return -1; +} + +static void write_config_reg(struct pinmux_info *gpioc, + struct pinmux_cfg_reg *crp, + int index) +{ + unsigned long ncomb, pos, value; + + ncomb = 1 << crp->field_width; + pos = index / ncomb; + value = index % ncomb; + + gpio_write_reg(crp->reg, crp->reg_width, crp->field_width, pos, value); +} + +static int check_config_reg(struct pinmux_info *gpioc, + struct pinmux_cfg_reg *crp, + int index) +{ + unsigned long ncomb, pos, value; + + ncomb = 1 << crp->field_width; + pos = index / ncomb; + value = index % ncomb; + + if (gpio_read_reg(crp->reg, crp->reg_width, + crp->field_width, pos) == value) + return 0; + + return -1; +} + +enum { GPIO_CFG_DRYRUN, GPIO_CFG_REQ, GPIO_CFG_FREE }; + +static int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio, + int pinmux_type, int cfg_mode) +{ + struct pinmux_cfg_reg *cr = NULL; + pinmux_enum_t enum_id; + struct pinmux_range *range; + int in_range, pos, index; + unsigned long *cntp; + + switch (pinmux_type) { + + case PINMUX_TYPE_FUNCTION: + range = NULL; + break; + + case PINMUX_TYPE_OUTPUT: + range = &gpioc->output; + break; + + case PINMUX_TYPE_INPUT: + range = &gpioc->input; + break; + + case PINMUX_TYPE_INPUT_PULLUP: + range = &gpioc->input_pu; + break; + + case PINMUX_TYPE_INPUT_PULLDOWN: + range = &gpioc->input_pd; + break; + + default: + goto out_err; + } + + pos = 0; + enum_id = 0; + index = 0; + while (1) { + pos = get_gpio_enum_id(gpioc, gpio, pos, &enum_id); + if (pos <= 0) + goto out_err; + + if (!enum_id) + break; + + in_range = enum_in_range(enum_id, &gpioc->function); + if (!in_range && range) { + in_range = enum_in_range(enum_id, range); + + if (in_range && enum_id == range->force) + continue; + } + + if (!in_range) + continue; + + if (get_config_reg(gpioc, enum_id, &cr, &index, &cntp) != 0) + goto out_err; + + switch (cfg_mode) { + case GPIO_CFG_DRYRUN: + if (!*cntp || !check_config_reg(gpioc, cr, index)) + continue; + break; + + case GPIO_CFG_REQ: + write_config_reg(gpioc, cr, index); + *cntp = *cntp + 1; + break; + + case GPIO_CFG_FREE: + *cntp = *cntp - 1; + break; + } + } + + return 0; + out_err: + return -1; +} + +static DEFINE_SPINLOCK(gpio_lock); + +static struct pinmux_info *chip_to_pinmux(struct gpio_chip *chip) +{ + return container_of(chip, struct pinmux_info, chip); +} + +static int sh_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + struct pinmux_info *gpioc = chip_to_pinmux(chip); + struct pinmux_data_reg *dummy; + unsigned long flags; + int i, ret, pinmux_type; + + ret = -EINVAL; + + if (!gpioc) + goto err_out; + + spin_lock_irqsave(&gpio_lock, flags); + + if ((gpioc->gpios[offset].flags & PINMUX_FLAG_TYPE) != PINMUX_TYPE_NONE) + goto err_unlock; + + /* setup pin function here if no data is associated with pin */ + + if (get_data_reg(gpioc, offset, &dummy, &i) != 0) + pinmux_type = PINMUX_TYPE_FUNCTION; + else + pinmux_type = PINMUX_TYPE_GPIO; + + if (pinmux_type == PINMUX_TYPE_FUNCTION) { + if (pinmux_config_gpio(gpioc, offset, + pinmux_type, + GPIO_CFG_DRYRUN) != 0) + goto err_unlock; + + if (pinmux_config_gpio(gpioc, offset, + pinmux_type, + GPIO_CFG_REQ) != 0) + BUG(); + } + + gpioc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; + gpioc->gpios[offset].flags |= pinmux_type; + + ret = 0; + err_unlock: + spin_unlock_irqrestore(&gpio_lock, flags); + err_out: + return ret; +} + +static void sh_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + struct pinmux_info *gpioc = chip_to_pinmux(chip); + unsigned long flags; + int pinmux_type; + + if (!gpioc) + return; + + spin_lock_irqsave(&gpio_lock, flags); + + pinmux_type = gpioc->gpios[offset].flags & PINMUX_FLAG_TYPE; + pinmux_config_gpio(gpioc, offset, pinmux_type, GPIO_CFG_FREE); + gpioc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; + gpioc->gpios[offset].flags |= PINMUX_TYPE_NONE; + + spin_unlock_irqrestore(&gpio_lock, flags); +} + +static int pinmux_direction(struct pinmux_info *gpioc, + unsigned gpio, int new_pinmux_type) +{ + int pinmux_type; + int ret = -EINVAL; + + if (!gpioc) + goto err_out; + + pinmux_type = gpioc->gpios[gpio].flags & PINMUX_FLAG_TYPE; + + switch (pinmux_type) { + case PINMUX_TYPE_GPIO: + break; + case PINMUX_TYPE_OUTPUT: + case PINMUX_TYPE_INPUT: + case PINMUX_TYPE_INPUT_PULLUP: + case PINMUX_TYPE_INPUT_PULLDOWN: + pinmux_config_gpio(gpioc, gpio, pinmux_type, GPIO_CFG_FREE); + break; + default: + goto err_out; + } + + if (pinmux_config_gpio(gpioc, gpio, + new_pinmux_type, + GPIO_CFG_DRYRUN) != 0) + goto err_out; + + if (pinmux_config_gpio(gpioc, gpio, + new_pinmux_type, + GPIO_CFG_REQ) != 0) + BUG(); + + gpioc->gpios[gpio].flags &= ~PINMUX_FLAG_TYPE; + gpioc->gpios[gpio].flags |= new_pinmux_type; + + ret = 0; + err_out: + return ret; +} + +static int sh_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct pinmux_info *gpioc = chip_to_pinmux(chip); + unsigned long flags; + int ret; + + spin_lock_irqsave(&gpio_lock, flags); + ret = pinmux_direction(gpioc, offset, PINMUX_TYPE_INPUT); + spin_unlock_irqrestore(&gpio_lock, flags); + + return ret; +} + +static void sh_gpio_set_value(struct pinmux_info *gpioc, + unsigned gpio, int value) +{ + struct pinmux_data_reg *dr = NULL; + int bit = 0; + + if (!gpioc || get_data_reg(gpioc, gpio, &dr, &bit) != 0) + BUG(); + else + gpio_write_bit(dr, bit, value); +} + +static int sh_gpio_direction_output(struct gpio_chip *chip, unsigned offset, + int value) +{ + struct pinmux_info *gpioc = chip_to_pinmux(chip); + unsigned long flags; + int ret; + + sh_gpio_set_value(gpioc, offset, value); + spin_lock_irqsave(&gpio_lock, flags); + ret = pinmux_direction(gpioc, offset, PINMUX_TYPE_OUTPUT); + spin_unlock_irqrestore(&gpio_lock, flags); + + return ret; +} + +static int sh_gpio_get_value(struct pinmux_info *gpioc, unsigned gpio) +{ + struct pinmux_data_reg *dr = NULL; + int bit = 0; + + if (!gpioc || get_data_reg(gpioc, gpio, &dr, &bit) != 0) { + BUG(); + return 0; + } + + return gpio_read_reg(dr->reg, dr->reg_width, 1, bit); +} + +static int sh_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + return sh_gpio_get_value(chip_to_pinmux(chip), offset); +} + +static void sh_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + sh_gpio_set_value(chip_to_pinmux(chip), offset, value); +} + +int register_pinmux(struct pinmux_info *pip) +{ + struct gpio_chip *chip = &pip->chip; + + pr_info("sh pinmux: %s handling gpio %d -> %d\n", + pip->name, pip->first_gpio, pip->last_gpio); + + setup_data_regs(pip); + + chip->request = sh_gpio_request; + chip->free = sh_gpio_free; + chip->direction_input = sh_gpio_direction_input; + chip->get = sh_gpio_get; + chip->direction_output = sh_gpio_direction_output; + chip->set = sh_gpio_set; + + WARN_ON(pip->first_gpio != 0); /* needs testing */ + + chip->label = pip->name; + chip->owner = THIS_MODULE; + chip->base = pip->first_gpio; + chip->ngpio = (pip->last_gpio - pip->first_gpio) + 1; + + return gpiochip_add(chip); +} -- cgit v1.2.3 From 9cdae914b2d08febca6e6e0440817d60da115ba5 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Mon, 30 Nov 2009 12:10:41 +0900 Subject: sh: pfc: Convert from ctrl_xxx() to __raw_xxx() I/O routines. Now that the PFC code is exposed for other architectures, use the common __raw_xxx() routines instead of the ctrl_xxx() ones. This will be needed for ARM-based SH-Mobiles amongst others. Signed-off-by: Paul Mundt --- drivers/sh/pfc.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/sh/pfc.c b/drivers/sh/pfc.c index d22e5af699f..448ba232c40 100644 --- a/drivers/sh/pfc.c +++ b/drivers/sh/pfc.c @@ -35,11 +35,11 @@ static unsigned long gpio_read_raw_reg(unsigned long reg, { switch (reg_width) { case 8: - return ctrl_inb(reg); + return __raw_readb(reg); case 16: - return ctrl_inw(reg); + return __raw_readw(reg); case 32: - return ctrl_inl(reg); + return __raw_readl(reg); } BUG(); @@ -52,13 +52,13 @@ static void gpio_write_raw_reg(unsigned long reg, { switch (reg_width) { case 8: - ctrl_outb(data, reg); + __raw_writeb(data, reg); return; case 16: - ctrl_outw(data, reg); + __raw_writew(data, reg); return; case 32: - ctrl_outl(data, reg); + __raw_writel(data, reg); return; } @@ -125,13 +125,13 @@ static void gpio_write_reg(unsigned long reg, unsigned long reg_width, switch (reg_width) { case 8: - ctrl_outb((ctrl_inb(reg) & mask) | value, reg); + __raw_writeb((__raw_readb(reg) & mask) | value, reg); break; case 16: - ctrl_outw((ctrl_inw(reg) & mask) | value, reg); + __raw_writew((__raw_readw(reg) & mask) | value, reg); break; case 32: - ctrl_outl((ctrl_inl(reg) & mask) | value, reg); + __raw_writel((__raw_readl(reg) & mask) | value, reg); break; } } -- cgit v1.2.3 From fd2cb0ce74e07babaf8c7bf96ef03c25d194e463 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Mon, 30 Nov 2009 12:15:04 +0900 Subject: sh: pfc: pr_info() -> pr_debug() cleanups. For some reason this was using pr_info() nested under an ifdef DEBUG. While this is appealing in that it circumvents the effort necessary to change ones loglevel, it's not terribly practical. So, convert it over to pr_debug(). Signed-off-by: Paul Mundt --- drivers/sh/pfc.c | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/sh/pfc.c b/drivers/sh/pfc.c index 448ba232c40..841ed5030c8 100644 --- a/drivers/sh/pfc.c +++ b/drivers/sh/pfc.c @@ -7,7 +7,6 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. */ - #include #include #include @@ -72,11 +71,9 @@ static void gpio_write_bit(struct pinmux_data_reg *dr, pos = dr->reg_width - (in_pos + 1); -#ifdef DEBUG - pr_info("write_bit addr = %lx, value = %ld, pos = %ld, " - "r_width = %ld\n", - dr->reg, !!value, pos, dr->reg_width); -#endif + pr_debug("write_bit addr = %lx, value = %ld, pos = %ld, " + "r_width = %ld\n", + dr->reg, !!value, pos, dr->reg_width); if (value) set_bit(pos, &dr->reg_shadow); @@ -95,11 +92,9 @@ static int gpio_read_reg(unsigned long reg, unsigned long reg_width, mask = (1 << field_width) - 1; pos = reg_width - ((in_pos + 1) * field_width); -#ifdef DEBUG - pr_info("read_reg: addr = %lx, pos = %ld, " - "r_width = %ld, f_width = %ld\n", - reg, pos, reg_width, field_width); -#endif + pr_debug("read_reg: addr = %lx, pos = %ld, " + "r_width = %ld, f_width = %ld\n", + reg, pos, reg_width, field_width); data = gpio_read_raw_reg(reg, reg_width); return (data >> pos) & mask; @@ -114,11 +109,9 @@ static void gpio_write_reg(unsigned long reg, unsigned long reg_width, mask = (1 << field_width) - 1; pos = reg_width - ((in_pos + 1) * field_width); -#ifdef DEBUG - pr_info("write_reg addr = %lx, value = %ld, pos = %ld, " - "r_width = %ld, f_width = %ld\n", - reg, value, pos, reg_width, field_width); -#endif + pr_debug("write_reg addr = %lx, value = %ld, pos = %ld, " + "r_width = %ld, f_width = %ld\n", + reg, value, pos, reg_width, field_width); mask = ~(mask << pos); value = value << pos; -- cgit v1.2.3 From b1516803d5274386256bef4972dfbf8c9eed5165 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 1 Dec 2009 09:54:46 +0000 Subject: serial: sh-sci: Fix too early port disabling. Currently serial ports on SH CPUs get disabled too early, because the sci_tx_empty() routine claims to not be able to detect whether the transmission has been completed and just always returns TIOCSER_TEMT. This results in corrupt output of last characters if the port is not open for reading at the same time. It is however possible to detect whether transmission has been completed. Use the TEND bit of the status register for this. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Paul Mundt --- drivers/serial/sh-sci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c index 972fca0a3ef..ff38dbdb5c6 100644 --- a/drivers/serial/sh-sci.c +++ b/drivers/serial/sh-sci.c @@ -799,8 +799,8 @@ static void sci_free_irq(struct sci_port *port) static unsigned int sci_tx_empty(struct uart_port *port) { - /* Can't detect */ - return TIOCSER_TEMT; + unsigned short status = sci_in(port, SCxSR); + return status & SCxSR_TEND(port) ? TIOCSER_TEMT : 0; } static void sci_set_mctrl(struct uart_port *port, unsigned int mctrl) -- cgit v1.2.3