From c584f9532115aecf24ba126dd5c528c994b6950f Mon Sep 17 00:00:00 2001 From: Finn Thain Date: Fri, 7 Feb 2020 10:01:13 +1100 Subject: fbdev/g364fb: Fix build failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch resolves these compiler errors and warnings -- CC drivers/video/fbdev/g364fb.o drivers/video/fbdev/g364fb.c: In function 'g364fb_cursor': drivers/video/fbdev/g364fb.c:137:9: error: 'x' undeclared (first use in this function) drivers/video/fbdev/g364fb.c:137:9: note: each undeclared identifier is reported only once for each function it appears in drivers/video/fbdev/g364fb.c:137:7: error: implicit declaration of function 'fontwidth' [-Werror=implicit-function-declaration] drivers/video/fbdev/g364fb.c:137:23: error: 'p' undeclared (first use in this function) drivers/video/fbdev/g364fb.c:137:38: error: 'y' undeclared (first use in this function) drivers/video/fbdev/g364fb.c:137:7: error: implicit declaration of function 'fontheight' [-Werror=implicit-function-declaration] drivers/video/fbdev/g364fb.c: In function 'g364fb_init': drivers/video/fbdev/g364fb.c:233:24: error: 'fbvar' undeclared (first use in this function) drivers/video/fbdev/g364fb.c:234:24: error: 'xres' undeclared (first use in this function) drivers/video/fbdev/g364fb.c:201:14: warning: unused variable 'j' [-Wunused-variable] drivers/video/fbdev/g364fb.c:197:25: warning: unused variable 'pal_ptr' [-Wunused-variable] The MIPS Magnum framebuffer console now works when tested in QEMU. Cc: Bartlomiej Zolnierkiewicz Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Finn Thain Acked-by: Bartlomiej Zolnierkiewicz Tested-by: Philippe Mathieu-Daudé Signed-off-by: Paul Burton Cc: Ralf Baechle Cc: James Hogan Cc: Thomas Bogendoerfer Cc: linux-mips@vger.kernel.org Cc: linux-kernel@vger.kernel.org Cc: dri-devel@lists.freedesktop.org Cc: linux-fbdev@vger.kernel.org --- drivers/video/fbdev/g364fb.c | 29 +++-------------------------- 1 file changed, 3 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/video/fbdev/g364fb.c b/drivers/video/fbdev/g364fb.c index 845b79da2a7c..05837a3b985c 100644 --- a/drivers/video/fbdev/g364fb.c +++ b/drivers/video/fbdev/g364fb.c @@ -108,7 +108,6 @@ static int g364fb_pan_display(struct fb_var_screeninfo *var, static int g364fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, u_int transp, struct fb_info *info); -static int g364fb_cursor(struct fb_info *info, struct fb_cursor *cursor); static int g364fb_blank(int blank, struct fb_info *info); static const struct fb_ops g364fb_ops = { @@ -119,28 +118,8 @@ static const struct fb_ops g364fb_ops = { .fb_fillrect = cfb_fillrect, .fb_copyarea = cfb_copyarea, .fb_imageblit = cfb_imageblit, - .fb_cursor = g364fb_cursor, }; -int g364fb_cursor(struct fb_info *info, struct fb_cursor *cursor) -{ - - switch (cursor->enable) { - case CM_ERASE: - *(unsigned int *) CTLA_REG |= CURS_TOGGLE; - break; - - case CM_MOVE: - case CM_DRAW: - *(unsigned int *) CTLA_REG &= ~CURS_TOGGLE; - *(unsigned int *) CURS_POS_REG = - ((x * fontwidth(p)) << 12) | ((y * fontheight(p)) - - info->var.yoffset); - break; - } - return 0; -} - /* * Pan or Wrap the Display * @@ -194,11 +173,9 @@ static int g364fb_setcolreg(u_int regno, u_int red, u_int green, */ int __init g364fb_init(void) { - volatile unsigned int *pal_ptr = - (volatile unsigned int *) CLR_PAL_REG; volatile unsigned int *curs_pal_ptr = (volatile unsigned int *) CURS_PAL_REG; - int mem, i, j; + int mem, i; if (fb_get_options("g364fb", NULL)) return -ENODEV; @@ -230,8 +207,8 @@ int __init g364fb_init(void) */ *(unsigned short *) (CURS_PAT_REG + 14 * 64) = 0xffff; *(unsigned short *) (CURS_PAT_REG + 15 * 64) = 0xffff; - fb_var.xres_virtual = fbvar.xres; - fb_fix.line_length = (xres / 8) * fb_var.bits_per_pixel; + fb_var.xres_virtual = fb_var.xres; + fb_fix.line_length = fb_var.xres_virtual * fb_var.bits_per_pixel / 8; fb_fix.smem_start = 0x40000000; /* physical address */ /* get size of video memory; this is special for the JAZZ hardware */ mem = (r4030_read_reg32(JAZZ_R4030_CONFIG) >> 8) & 3; -- cgit v1.3 From 3da27a4eb8c214d692e024473415fe7d2e88e7d7 Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Wed, 5 Feb 2020 12:08:32 +0800 Subject: firmware: dmi: Add macro SMBIOS_ENTRY_POINT_SCAN_START Use SMBIOS_ENTRY_POINT_SCAN_START instead of 0xF0000, because other archtecture maybe use a special start address such as 0xFFFE000 for Loongson platform. Signed-off-by: Tiezhu Yang Reviewed-by: Jean Delvare Signed-off-by: Thomas Bogendoerfer --- drivers/firmware/dmi_scan.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c index 2045566d622f..f59163cb7cba 100644 --- a/drivers/firmware/dmi_scan.c +++ b/drivers/firmware/dmi_scan.c @@ -11,6 +11,10 @@ #include #include +#ifndef SMBIOS_ENTRY_POINT_SCAN_START +#define SMBIOS_ENTRY_POINT_SCAN_START 0xF0000 +#endif + struct kobject *dmi_kobj; EXPORT_SYMBOL_GPL(dmi_kobj); @@ -663,7 +667,7 @@ static void __init dmi_scan_machine(void) return; } } else if (IS_ENABLED(CONFIG_DMI_SCAN_MACHINE_NON_EFI_FALLBACK)) { - p = dmi_early_remap(0xF0000, 0x10000); + p = dmi_early_remap(SMBIOS_ENTRY_POINT_SCAN_START, 0x10000); if (p == NULL) goto error; -- cgit v1.3 From 823b30fe22d3bb1e98e5cb7d5c37b78bf14da48c Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 24 Mar 2020 12:14:18 +0100 Subject: ide: remove no longer used au1xxx-ide driver Commit 54ff4a1d1732 ("MIPS: Alchemy: pata_platform for DB1200") from year 2014 converted the only user of au1xxx-ide IDE host driver (MIPS Alchemy DB1200 platform) to use pata_platform libata host driver instead. This patch removes dead au1xxx-ide driver code. Acked-by: David S. Miller Acked-by: Manuel Lauss Acked-by: Christoph Hellwig Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Thomas Bogendoerfer --- drivers/ide/Kconfig | 20 +- drivers/ide/Makefile | 2 - drivers/ide/au1xxx-ide.c | 597 ----------------------------------------------- 3 files changed, 1 insertion(+), 618 deletions(-) delete mode 100644 drivers/ide/au1xxx-ide.c (limited to 'drivers') diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig index 1c227ea8ecd3..593f149c2910 100644 --- a/drivers/ide/Kconfig +++ b/drivers/ide/Kconfig @@ -662,23 +662,6 @@ config BLK_DEV_IDE_PMAC_ATA100FIRST CD-ROM on hda. This option changes this to more natural hda for hard disk and hdc for CD-ROM. -config BLK_DEV_IDE_AU1XXX - bool "IDE for AMD Alchemy Au1200" - depends on MIPS_ALCHEMY - select IDE_XFER_MODE -choice - prompt "IDE Mode for AMD Alchemy Au1200" - default BLK_DEV_IDE_AU1XXX_PIO_DBDMA - depends on BLK_DEV_IDE_AU1XXX - -config BLK_DEV_IDE_AU1XXX_PIO_DBDMA - bool "PIO+DbDMA IDE for AMD Alchemy Au1200" - -config BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA - bool "MDMA2+DbDMA IDE for AMD Alchemy Au1200" - depends on BLK_DEV_IDE_AU1XXX -endchoice - config BLK_DEV_IDE_TX4938 tristate "TX4938 internal IDE support" depends on SOC_TX4938 @@ -859,8 +842,7 @@ config BLK_DEV_UMC8672 endif config BLK_DEV_IDEDMA - def_bool BLK_DEV_IDEDMA_SFF || \ - BLK_DEV_IDEDMA_ICS || BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA + def_bool BLK_DEV_IDEDMA_SFF || BLK_DEV_IDEDMA_ICS select IDE_XFER_MODE endif # IDE diff --git a/drivers/ide/Makefile b/drivers/ide/Makefile index d4f4409cfb8b..2605b3cdaf47 100644 --- a/drivers/ide/Makefile +++ b/drivers/ide/Makefile @@ -107,7 +107,5 @@ obj-$(CONFIG_BLK_DEV_IDE_ICSIDE) += icside.o obj-$(CONFIG_BLK_DEV_IDE_RAPIDE) += rapide.o obj-$(CONFIG_BLK_DEV_PALMCHIP_BK3710) += palm_bk3710.o -obj-$(CONFIG_BLK_DEV_IDE_AU1XXX) += au1xxx-ide.o - obj-$(CONFIG_BLK_DEV_IDE_TX4938) += tx4938ide.o obj-$(CONFIG_BLK_DEV_IDE_TX4939) += tx4939ide.o diff --git a/drivers/ide/au1xxx-ide.c b/drivers/ide/au1xxx-ide.c deleted file mode 100644 index 4d181a918d72..000000000000 --- a/drivers/ide/au1xxx-ide.c +++ /dev/null @@ -1,597 +0,0 @@ -/* - * BRIEF MODULE DESCRIPTION - * AMD Alchemy Au1xxx IDE interface routines over the Static Bus - * - * Copyright (c) 2003-2005 AMD, Personal Connectivity Solutions - * - * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * 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. - * - * Note: for more information, please refer "AMD Alchemy Au1200/Au1550 IDE - * Interface and Linux Device Driver" Application Note. - */ -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define DRV_NAME "au1200-ide" -#define DRV_AUTHOR "Enrico Walther / Pete Popov " - -#ifndef IDE_REG_SHIFT -#define IDE_REG_SHIFT 5 -#endif - -/* enable the burstmode in the dbdma */ -#define IDE_AU1XXX_BURSTMODE 1 - -static _auide_hwif auide_hwif; - -#if defined(CONFIG_BLK_DEV_IDE_AU1XXX_PIO_DBDMA) - -static inline void auide_insw(unsigned long port, void *addr, u32 count) -{ - _auide_hwif *ahwif = &auide_hwif; - chan_tab_t *ctp; - au1x_ddma_desc_t *dp; - - if (!au1xxx_dbdma_put_dest(ahwif->rx_chan, virt_to_phys(addr), - count << 1, DDMA_FLAGS_NOIE)) { - printk(KERN_ERR "%s failed %d\n", __func__, __LINE__); - return; - } - ctp = *((chan_tab_t **)ahwif->rx_chan); - dp = ctp->cur_ptr; - while (dp->dscr_cmd0 & DSCR_CMD0_V) - ; - ctp->cur_ptr = au1xxx_ddma_get_nextptr_virt(dp); -} - -static inline void auide_outsw(unsigned long port, void *addr, u32 count) -{ - _auide_hwif *ahwif = &auide_hwif; - chan_tab_t *ctp; - au1x_ddma_desc_t *dp; - - if (!au1xxx_dbdma_put_source(ahwif->tx_chan, virt_to_phys(addr), - count << 1, DDMA_FLAGS_NOIE)) { - printk(KERN_ERR "%s failed %d\n", __func__, __LINE__); - return; - } - ctp = *((chan_tab_t **)ahwif->tx_chan); - dp = ctp->cur_ptr; - while (dp->dscr_cmd0 & DSCR_CMD0_V) - ; - ctp->cur_ptr = au1xxx_ddma_get_nextptr_virt(dp); -} - -static void au1xxx_input_data(ide_drive_t *drive, struct ide_cmd *cmd, - void *buf, unsigned int len) -{ - auide_insw(drive->hwif->io_ports.data_addr, buf, (len + 1) / 2); -} - -static void au1xxx_output_data(ide_drive_t *drive, struct ide_cmd *cmd, - void *buf, unsigned int len) -{ - auide_outsw(drive->hwif->io_ports.data_addr, buf, (len + 1) / 2); -} -#endif - -static void au1xxx_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) -{ - int mem_sttime = 0, mem_stcfg = au_readl(MEM_STCFG2); - - switch (drive->pio_mode - XFER_PIO_0) { - case 0: - mem_sttime = SBC_IDE_TIMING(PIO0); - - /* set configuration for RCS2# */ - mem_stcfg |= TS_MASK; - mem_stcfg &= ~TCSOE_MASK; - mem_stcfg &= ~TOECS_MASK; - mem_stcfg |= SBC_IDE_PIO0_TCSOE | SBC_IDE_PIO0_TOECS; - break; - - case 1: - mem_sttime = SBC_IDE_TIMING(PIO1); - - /* set configuration for RCS2# */ - mem_stcfg |= TS_MASK; - mem_stcfg &= ~TCSOE_MASK; - mem_stcfg &= ~TOECS_MASK; - mem_stcfg |= SBC_IDE_PIO1_TCSOE | SBC_IDE_PIO1_TOECS; - break; - - case 2: - mem_sttime = SBC_IDE_TIMING(PIO2); - - /* set configuration for RCS2# */ - mem_stcfg &= ~TS_MASK; - mem_stcfg &= ~TCSOE_MASK; - mem_stcfg &= ~TOECS_MASK; - mem_stcfg |= SBC_IDE_PIO2_TCSOE | SBC_IDE_PIO2_TOECS; - break; - - case 3: - mem_sttime = SBC_IDE_TIMING(PIO3); - - /* set configuration for RCS2# */ - mem_stcfg &= ~TS_MASK; - mem_stcfg &= ~TCSOE_MASK; - mem_stcfg &= ~TOECS_MASK; - mem_stcfg |= SBC_IDE_PIO3_TCSOE | SBC_IDE_PIO3_TOECS; - - break; - - case 4: - mem_sttime = SBC_IDE_TIMING(PIO4); - - /* set configuration for RCS2# */ - mem_stcfg &= ~TS_MASK; - mem_stcfg &= ~TCSOE_MASK; - mem_stcfg &= ~TOECS_MASK; - mem_stcfg |= SBC_IDE_PIO4_TCSOE | SBC_IDE_PIO4_TOECS; - break; - } - - au_writel(mem_sttime,MEM_STTIME2); - au_writel(mem_stcfg,MEM_STCFG2); -} - -static void auide_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive) -{ - int mem_sttime = 0, mem_stcfg = au_readl(MEM_STCFG2); - - switch (drive->dma_mode) { -#ifdef CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA - case XFER_MW_DMA_2: - mem_sttime = SBC_IDE_TIMING(MDMA2); - - /* set configuration for RCS2# */ - mem_stcfg &= ~TS_MASK; - mem_stcfg &= ~TCSOE_MASK; - mem_stcfg &= ~TOECS_MASK; - mem_stcfg |= SBC_IDE_MDMA2_TCSOE | SBC_IDE_MDMA2_TOECS; - - break; - case XFER_MW_DMA_1: - mem_sttime = SBC_IDE_TIMING(MDMA1); - - /* set configuration for RCS2# */ - mem_stcfg &= ~TS_MASK; - mem_stcfg &= ~TCSOE_MASK; - mem_stcfg &= ~TOECS_MASK; - mem_stcfg |= SBC_IDE_MDMA1_TCSOE | SBC_IDE_MDMA1_TOECS; - - break; - case XFER_MW_DMA_0: - mem_sttime = SBC_IDE_TIMING(MDMA0); - - /* set configuration for RCS2# */ - mem_stcfg |= TS_MASK; - mem_stcfg &= ~TCSOE_MASK; - mem_stcfg &= ~TOECS_MASK; - mem_stcfg |= SBC_IDE_MDMA0_TCSOE | SBC_IDE_MDMA0_TOECS; - - break; -#endif - } - - au_writel(mem_sttime,MEM_STTIME2); - au_writel(mem_stcfg,MEM_STCFG2); -} - -/* - * Multi-Word DMA + DbDMA functions - */ - -#ifdef CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA -static int auide_build_dmatable(ide_drive_t *drive, struct ide_cmd *cmd) -{ - ide_hwif_t *hwif = drive->hwif; - _auide_hwif *ahwif = &auide_hwif; - struct scatterlist *sg; - int i = cmd->sg_nents, count = 0; - int iswrite = !!(cmd->tf_flags & IDE_TFLAG_WRITE); - - /* Save for interrupt context */ - ahwif->drive = drive; - - /* fill the descriptors */ - sg = hwif->sg_table; - while (i && sg_dma_len(sg)) { - u32 cur_addr; - u32 cur_len; - - cur_addr = sg_dma_address(sg); - cur_len = sg_dma_len(sg); - - while (cur_len) { - u32 flags = DDMA_FLAGS_NOIE; - unsigned int tc = (cur_len < 0xfe00)? cur_len: 0xfe00; - - if (++count >= PRD_ENTRIES) { - printk(KERN_WARNING "%s: DMA table too small\n", - drive->name); - return 0; - } - - /* Lets enable intr for the last descriptor only */ - if (1==i) - flags = DDMA_FLAGS_IE; - else - flags = DDMA_FLAGS_NOIE; - - if (iswrite) { - if (!au1xxx_dbdma_put_source(ahwif->tx_chan, - sg_phys(sg), tc, flags)) { - printk(KERN_ERR "%s failed %d\n", - __func__, __LINE__); - } - } else { - if (!au1xxx_dbdma_put_dest(ahwif->rx_chan, - sg_phys(sg), tc, flags)) { - printk(KERN_ERR "%s failed %d\n", - __func__, __LINE__); - } - } - - cur_addr += tc; - cur_len -= tc; - } - sg = sg_next(sg); - i--; - } - - if (count) - return 1; - - return 0; /* revert to PIO for this request */ -} - -static int auide_dma_end(ide_drive_t *drive) -{ - return 0; -} - -static void auide_dma_start(ide_drive_t *drive ) -{ -} - - -static int auide_dma_setup(ide_drive_t *drive, struct ide_cmd *cmd) -{ - if (auide_build_dmatable(drive, cmd) == 0) - return 1; - - return 0; -} - -static int auide_dma_test_irq(ide_drive_t *drive) -{ - /* If dbdma didn't execute the STOP command yet, the - * active bit is still set - */ - drive->waiting_for_dma++; - if (drive->waiting_for_dma >= DMA_WAIT_TIMEOUT) { - printk(KERN_WARNING "%s: timeout waiting for ddma to complete\n", - drive->name); - return 1; - } - udelay(10); - return 0; -} - -static void auide_dma_host_set(ide_drive_t *drive, int on) -{ -} - -static void auide_ddma_tx_callback(int irq, void *param) -{ -} - -static void auide_ddma_rx_callback(int irq, void *param) -{ -} -#endif /* end CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA */ - -static void auide_init_dbdma_dev(dbdev_tab_t *dev, u32 dev_id, u32 tsize, - u32 devwidth, u32 flags, u32 regbase) -{ - dev->dev_id = dev_id; - dev->dev_physaddr = CPHYSADDR(regbase); - dev->dev_intlevel = 0; - dev->dev_intpolarity = 0; - dev->dev_tsize = tsize; - dev->dev_devwidth = devwidth; - dev->dev_flags = flags; -} - -#ifdef CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA -static const struct ide_dma_ops au1xxx_dma_ops = { - .dma_host_set = auide_dma_host_set, - .dma_setup = auide_dma_setup, - .dma_start = auide_dma_start, - .dma_end = auide_dma_end, - .dma_test_irq = auide_dma_test_irq, - .dma_lost_irq = ide_dma_lost_irq, -}; - -static int auide_ddma_init(ide_hwif_t *hwif, const struct ide_port_info *d) -{ - _auide_hwif *auide = &auide_hwif; - dbdev_tab_t source_dev_tab, target_dev_tab; - u32 dev_id, tsize, devwidth, flags; - - dev_id = hwif->ddma_id; - - tsize = 8; /* 1 */ - devwidth = 32; /* 16 */ - -#ifdef IDE_AU1XXX_BURSTMODE - flags = DEV_FLAGS_SYNC | DEV_FLAGS_BURSTABLE; -#else - flags = DEV_FLAGS_SYNC; -#endif - - /* setup dev_tab for tx channel */ - auide_init_dbdma_dev(&source_dev_tab, dev_id, tsize, devwidth, - DEV_FLAGS_OUT | flags, auide->regbase); - auide->tx_dev_id = au1xxx_ddma_add_device( &source_dev_tab ); - - auide_init_dbdma_dev(&source_dev_tab, dev_id, tsize, devwidth, - DEV_FLAGS_IN | flags, auide->regbase); - auide->rx_dev_id = au1xxx_ddma_add_device( &source_dev_tab ); - - /* We also need to add a target device for the DMA */ - auide_init_dbdma_dev(&target_dev_tab, (u32)DSCR_CMD0_ALWAYS, tsize, - devwidth, DEV_FLAGS_ANYUSE, auide->regbase); - auide->target_dev_id = au1xxx_ddma_add_device(&target_dev_tab); - - /* Get a channel for TX */ - auide->tx_chan = au1xxx_dbdma_chan_alloc(auide->target_dev_id, - auide->tx_dev_id, - auide_ddma_tx_callback, - (void*)auide); - - /* Get a channel for RX */ - auide->rx_chan = au1xxx_dbdma_chan_alloc(auide->rx_dev_id, - auide->target_dev_id, - auide_ddma_rx_callback, - (void*)auide); - - auide->tx_desc_head = (void*)au1xxx_dbdma_ring_alloc(auide->tx_chan, - NUM_DESCRIPTORS); - auide->rx_desc_head = (void*)au1xxx_dbdma_ring_alloc(auide->rx_chan, - NUM_DESCRIPTORS); - - /* FIXME: check return value */ - (void)ide_allocate_dma_engine(hwif); - - au1xxx_dbdma_start( auide->tx_chan ); - au1xxx_dbdma_start( auide->rx_chan ); - - return 0; -} -#else -static int auide_ddma_init(ide_hwif_t *hwif, const struct ide_port_info *d) -{ - _auide_hwif *auide = &auide_hwif; - dbdev_tab_t source_dev_tab; - int flags; - -#ifdef IDE_AU1XXX_BURSTMODE - flags = DEV_FLAGS_SYNC | DEV_FLAGS_BURSTABLE; -#else - flags = DEV_FLAGS_SYNC; -#endif - - /* setup dev_tab for tx channel */ - auide_init_dbdma_dev(&source_dev_tab, (u32)DSCR_CMD0_ALWAYS, 8, 32, - DEV_FLAGS_OUT | flags, auide->regbase); - auide->tx_dev_id = au1xxx_ddma_add_device( &source_dev_tab ); - - auide_init_dbdma_dev(&source_dev_tab, (u32)DSCR_CMD0_ALWAYS, 8, 32, - DEV_FLAGS_IN | flags, auide->regbase); - auide->rx_dev_id = au1xxx_ddma_add_device( &source_dev_tab ); - - /* Get a channel for TX */ - auide->tx_chan = au1xxx_dbdma_chan_alloc(DSCR_CMD0_ALWAYS, - auide->tx_dev_id, - NULL, - (void*)auide); - - /* Get a channel for RX */ - auide->rx_chan = au1xxx_dbdma_chan_alloc(auide->rx_dev_id, - DSCR_CMD0_ALWAYS, - NULL, - (void*)auide); - - auide->tx_desc_head = (void*)au1xxx_dbdma_ring_alloc(auide->tx_chan, - NUM_DESCRIPTORS); - auide->rx_desc_head = (void*)au1xxx_dbdma_ring_alloc(auide->rx_chan, - NUM_DESCRIPTORS); - - au1xxx_dbdma_start( auide->tx_chan ); - au1xxx_dbdma_start( auide->rx_chan ); - - return 0; -} -#endif - -static void auide_setup_ports(struct ide_hw *hw, _auide_hwif *ahwif) -{ - int i; - unsigned long *ata_regs = hw->io_ports_array; - - /* FIXME? */ - for (i = 0; i < 8; i++) - *ata_regs++ = ahwif->regbase + (i << IDE_REG_SHIFT); - - /* set the Alternative Status register */ - *ata_regs = ahwif->regbase + (14 << IDE_REG_SHIFT); -} - -#ifdef CONFIG_BLK_DEV_IDE_AU1XXX_PIO_DBDMA -static const struct ide_tp_ops au1xxx_tp_ops = { - .exec_command = ide_exec_command, - .read_status = ide_read_status, - .read_altstatus = ide_read_altstatus, - .write_devctl = ide_write_devctl, - - .dev_select = ide_dev_select, - .tf_load = ide_tf_load, - .tf_read = ide_tf_read, - - .input_data = au1xxx_input_data, - .output_data = au1xxx_output_data, -}; -#endif - -static const struct ide_port_ops au1xxx_port_ops = { - .set_pio_mode = au1xxx_set_pio_mode, - .set_dma_mode = auide_set_dma_mode, -}; - -static const struct ide_port_info au1xxx_port_info = { - .init_dma = auide_ddma_init, -#ifdef CONFIG_BLK_DEV_IDE_AU1XXX_PIO_DBDMA - .tp_ops = &au1xxx_tp_ops, -#endif - .port_ops = &au1xxx_port_ops, -#ifdef CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA - .dma_ops = &au1xxx_dma_ops, -#endif - .host_flags = IDE_HFLAG_POST_SET_MODE | - IDE_HFLAG_NO_IO_32BIT | - IDE_HFLAG_UNMASK_IRQS, - .pio_mask = ATA_PIO4, -#ifdef CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA - .mwdma_mask = ATA_MWDMA2, -#endif - .chipset = ide_au1xxx, -}; - -static int au_ide_probe(struct platform_device *dev) -{ - _auide_hwif *ahwif = &auide_hwif; - struct resource *res; - struct ide_host *host; - int ret = 0; - struct ide_hw hw, *hws[] = { &hw }; - -#if defined(CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA) - char *mode = "MWDMA2"; -#elif defined(CONFIG_BLK_DEV_IDE_AU1XXX_PIO_DBDMA) - char *mode = "PIO+DDMA(offload)"; -#endif - - memset(&auide_hwif, 0, sizeof(_auide_hwif)); - ahwif->irq = platform_get_irq(dev, 0); - - res = platform_get_resource(dev, IORESOURCE_MEM, 0); - - if (res == NULL) { - pr_debug("%s %d: no base address\n", DRV_NAME, dev->id); - ret = -ENODEV; - goto out; - } - if (ahwif->irq < 0) { - pr_debug("%s %d: no IRQ\n", DRV_NAME, dev->id); - ret = -ENODEV; - goto out; - } - - if (!request_mem_region(res->start, resource_size(res), dev->name)) { - pr_debug("%s: request_mem_region failed\n", DRV_NAME); - ret = -EBUSY; - goto out; - } - - ahwif->regbase = (u32)ioremap(res->start, resource_size(res)); - if (ahwif->regbase == 0) { - ret = -ENOMEM; - goto out; - } - - res = platform_get_resource(dev, IORESOURCE_DMA, 0); - if (!res) { - pr_debug("%s: no DDMA ID resource\n", DRV_NAME); - ret = -ENODEV; - goto out; - } - ahwif->ddma_id = res->start; - - memset(&hw, 0, sizeof(hw)); - auide_setup_ports(&hw, ahwif); - hw.irq = ahwif->irq; - hw.dev = &dev->dev; - - ret = ide_host_add(&au1xxx_port_info, hws, 1, &host); - if (ret) - goto out; - - auide_hwif.hwif = host->ports[0]; - - platform_set_drvdata(dev, host); - - printk(KERN_INFO "Au1xxx IDE(builtin) configured for %s\n", mode ); - - out: - return ret; -} - -static int au_ide_remove(struct platform_device *dev) -{ - struct resource *res; - struct ide_host *host = platform_get_drvdata(dev); - _auide_hwif *ahwif = &auide_hwif; - - ide_host_remove(host); - - iounmap((void *)ahwif->regbase); - - res = platform_get_resource(dev, IORESOURCE_MEM, 0); - release_mem_region(res->start, resource_size(res)); - - return 0; -} - -static struct platform_driver au1200_ide_driver = { - .driver = { - .name = "au1200-ide", - }, - .probe = au_ide_probe, - .remove = au_ide_remove, -}; - -module_platform_driver(au1200_ide_driver); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("AU1200 IDE driver"); -- cgit v1.3 From dbb152267908c4b2c3639492a94b6838821bc195 Mon Sep 17 00:00:00 2001 From: Jiaxun Yang Date: Wed, 25 Mar 2020 11:54:54 +0800 Subject: irqchip: Add driver for Loongson I/O Local Interrupt Controller This controller appeared on Loongson family of chips as the primary package interrupt source. Signed-off-by: Jiaxun Yang Co-developed-by: Huacai Chen Signed-off-by: Huacai Chen Reviewed-by: Marc Zyngier Signed-off-by: Thomas Bogendoerfer --- drivers/irqchip/Kconfig | 9 ++ drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-loongson-liointc.c | 261 +++++++++++++++++++++++++++++++++ 3 files changed, 271 insertions(+) create mode 100644 drivers/irqchip/irq-loongson-liointc.c (limited to 'drivers') diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 6d397732138d..c609eaa319d2 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -513,4 +513,13 @@ config EXYNOS_IRQ_COMBINER Say yes here to add support for the IRQ combiner devices embedded in Samsung Exynos chips. +config LOONGSON_LIOINTC + bool "Loongson Local I/O Interrupt Controller" + depends on MACH_LOONGSON64 + default y + select IRQ_DOMAIN + select GENERIC_IRQ_CHIP + help + Support for the Loongson Local I/O Interrupt Controller. + endmenu diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index eae0d78cbf22..5e7678efdfe6 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -105,3 +105,4 @@ obj-$(CONFIG_MADERA_IRQ) += irq-madera.o obj-$(CONFIG_LS1X_IRQ) += irq-ls1x.o obj-$(CONFIG_TI_SCI_INTR_IRQCHIP) += irq-ti-sci-intr.o obj-$(CONFIG_TI_SCI_INTA_IRQCHIP) += irq-ti-sci-inta.o +obj-$(CONFIG_LOONGSON_LIOINTC) += irq-loongson-liointc.o diff --git a/drivers/irqchip/irq-loongson-liointc.c b/drivers/irqchip/irq-loongson-liointc.c new file mode 100644 index 000000000000..18de2c09ece4 --- /dev/null +++ b/drivers/irqchip/irq-loongson-liointc.c @@ -0,0 +1,261 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020, Jiaxun Yang + * Loongson Local IO Interrupt Controller support + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define LIOINTC_CHIP_IRQ 32 +#define LIOINTC_NUM_PARENT 4 + +#define LIOINTC_INTC_CHIP_START 0x20 + +#define LIOINTC_REG_INTC_STATUS (LIOINTC_INTC_CHIP_START + 0x20) +#define LIOINTC_REG_INTC_EN_STATUS (LIOINTC_INTC_CHIP_START + 0x04) +#define LIOINTC_REG_INTC_ENABLE (LIOINTC_INTC_CHIP_START + 0x08) +#define LIOINTC_REG_INTC_DISABLE (LIOINTC_INTC_CHIP_START + 0x0c) +#define LIOINTC_REG_INTC_POL (LIOINTC_INTC_CHIP_START + 0x10) +#define LIOINTC_REG_INTC_EDGE (LIOINTC_INTC_CHIP_START + 0x14) + +#define LIOINTC_SHIFT_INTx 4 + +struct liointc_handler_data { + struct liointc_priv *priv; + u32 parent_int_map; +}; + +struct liointc_priv { + struct irq_chip_generic *gc; + struct liointc_handler_data handler[LIOINTC_NUM_PARENT]; + u8 map_cache[LIOINTC_CHIP_IRQ]; +}; + +static void liointc_chained_handle_irq(struct irq_desc *desc) +{ + struct liointc_handler_data *handler = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + struct irq_chip_generic *gc = handler->priv->gc; + u32 pending; + + chained_irq_enter(chip, desc); + + pending = readl(gc->reg_base + LIOINTC_REG_INTC_STATUS); + + if (!pending) + spurious_interrupt(); + + while (pending) { + int bit = __ffs(pending); + + generic_handle_irq(irq_find_mapping(gc->domain, bit)); + pending &= ~BIT(bit); + } + + chained_irq_exit(chip, desc); +} + +static void liointc_set_bit(struct irq_chip_generic *gc, + unsigned int offset, + u32 mask, bool set) +{ + if (set) + writel(readl(gc->reg_base + offset) | mask, + gc->reg_base + offset); + else + writel(readl(gc->reg_base + offset) & ~mask, + gc->reg_base + offset); +} + +static int liointc_set_type(struct irq_data *data, unsigned int type) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); + u32 mask = data->mask; + unsigned long flags; + + irq_gc_lock_irqsave(gc, flags); + switch (type) { + case IRQ_TYPE_LEVEL_HIGH: + liointc_set_bit(gc, LIOINTC_REG_INTC_EDGE, mask, false); + liointc_set_bit(gc, LIOINTC_REG_INTC_POL, mask, true); + break; + case IRQ_TYPE_LEVEL_LOW: + liointc_set_bit(gc, LIOINTC_REG_INTC_EDGE, mask, false); + liointc_set_bit(gc, LIOINTC_REG_INTC_POL, mask, false); + break; + case IRQ_TYPE_EDGE_RISING: + liointc_set_bit(gc, LIOINTC_REG_INTC_EDGE, mask, true); + liointc_set_bit(gc, LIOINTC_REG_INTC_POL, mask, true); + break; + case IRQ_TYPE_EDGE_FALLING: + liointc_set_bit(gc, LIOINTC_REG_INTC_EDGE, mask, true); + liointc_set_bit(gc, LIOINTC_REG_INTC_POL, mask, false); + break; + default: + return -EINVAL; + } + irq_gc_unlock_irqrestore(gc, flags); + + irqd_set_trigger_type(data, type); + return 0; +} + +static void liointc_resume(struct irq_chip_generic *gc) +{ + struct liointc_priv *priv = gc->private; + unsigned long flags; + int i; + + irq_gc_lock_irqsave(gc, flags); + /* Disable all at first */ + writel(0xffffffff, gc->reg_base + LIOINTC_REG_INTC_DISABLE); + /* Revert map cache */ + for (i = 0; i < LIOINTC_CHIP_IRQ; i++) + writeb(priv->map_cache[i], gc->reg_base + i); + /* Revert mask cache */ + writel(~gc->mask_cache, gc->reg_base + LIOINTC_REG_INTC_ENABLE); + irq_gc_unlock_irqrestore(gc, flags); +} + +static const char * const parent_names[] = {"int0", "int1", "int2", "int3"}; + +int __init liointc_of_init(struct device_node *node, + struct device_node *parent) +{ + struct irq_chip_generic *gc; + struct irq_domain *domain; + struct irq_chip_type *ct; + struct liointc_priv *priv; + void __iomem *base; + u32 of_parent_int_map[LIOINTC_NUM_PARENT]; + int parent_irq[LIOINTC_NUM_PARENT]; + bool have_parent = FALSE; + int sz, i, err = 0; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + base = of_iomap(node, 0); + if (!base) { + err = -ENODEV; + goto out_free_priv; + } + + for (i = 0; i < LIOINTC_NUM_PARENT; i++) { + parent_irq[i] = of_irq_get_byname(node, parent_names[i]); + if (parent_irq[i] > 0) + have_parent = TRUE; + } + if (!have_parent) { + err = -ENODEV; + goto out_iounmap; + } + + sz = of_property_read_variable_u32_array(node, + "loongson,parent_int_map", + &of_parent_int_map[0], + LIOINTC_NUM_PARENT, + LIOINTC_NUM_PARENT); + if (sz < 4) { + pr_err("loongson-liointc: No parent_int_map\n"); + err = -ENODEV; + goto out_iounmap; + } + + for (i = 0; i < LIOINTC_NUM_PARENT; i++) + priv->handler[i].parent_int_map = of_parent_int_map[i]; + + /* Setup IRQ domain */ + domain = irq_domain_add_linear(node, 32, + &irq_generic_chip_ops, priv); + if (!domain) { + pr_err("loongson-liointc: cannot add IRQ domain\n"); + err = -EINVAL; + goto out_iounmap; + } + + err = irq_alloc_domain_generic_chips(domain, 32, 1, + node->full_name, handle_level_irq, + IRQ_NOPROBE, 0, 0); + if (err) { + pr_err("loongson-liointc: unable to register IRQ domain\n"); + goto out_free_domain; + } + + + /* Disable all IRQs */ + writel(0xffffffff, base + LIOINTC_REG_INTC_DISABLE); + /* Set to level triggered */ + writel(0x0, base + LIOINTC_REG_INTC_EDGE); + + /* Generate parent INT part of map cache */ + for (i = 0; i < LIOINTC_NUM_PARENT; i++) { + u32 pending = priv->handler[i].parent_int_map; + + while (pending) { + int bit = __ffs(pending); + + priv->map_cache[bit] = BIT(i) << LIOINTC_SHIFT_INTx; + pending &= ~BIT(bit); + } + } + + for (i = 0; i < LIOINTC_CHIP_IRQ; i++) { + /* Generate core part of map cache */ + priv->map_cache[i] |= BIT(loongson_sysconf.boot_cpu_id); + writeb(priv->map_cache[i], base + i); + } + + gc = irq_get_domain_generic_chip(domain, 0); + gc->private = priv; + gc->reg_base = base; + gc->domain = domain; + gc->resume = liointc_resume; + + ct = gc->chip_types; + ct->regs.enable = LIOINTC_REG_INTC_ENABLE; + ct->regs.disable = LIOINTC_REG_INTC_DISABLE; + ct->chip.irq_unmask = irq_gc_unmask_enable_reg; + ct->chip.irq_mask = irq_gc_mask_disable_reg; + ct->chip.irq_mask_ack = irq_gc_mask_disable_reg; + ct->chip.irq_set_type = liointc_set_type; + + gc->mask_cache = 0xffffffff; + priv->gc = gc; + + for (i = 0; i < LIOINTC_NUM_PARENT; i++) { + if (parent_irq[i] <= 0) + continue; + + priv->handler[i].priv = priv; + irq_set_chained_handler_and_data(parent_irq[i], + liointc_chained_handle_irq, &priv->handler[i]); + } + + return 0; + +out_free_domain: + irq_domain_remove(domain); +out_iounmap: + iounmap(base); +out_free_priv: + kfree(priv); + + return err; +} + +IRQCHIP_DECLARE(loongson_liointc_1_0, "loongson,liointc-1.0", liointc_of_init); +IRQCHIP_DECLARE(loongson_liointc_1_0a, "loongson,liointc-1.0a", liointc_of_init); -- cgit v1.3 From be09ef09e290e1c8bd361e431d3659e13e65094c Mon Sep 17 00:00:00 2001 From: Jiaxun Yang Date: Wed, 25 Mar 2020 11:54:55 +0800 Subject: irqchip: loongson-liointc: Workaround LPC IRQ Errata The 1.0 version of that controller has a bug that status bit of LPC IRQ sometimes doesn't get set correctly. So we can always blame LPC IRQ when spurious interrupt happens at the parent interrupt line which LPC IRQ supposed to route to. Signed-off-by: Jiaxun Yang Co-developed-by: Huacai Chen Signed-off-by: Huacai Chen Reviewed-by: Marc Zyngier Signed-off-by: Thomas Bogendoerfer --- drivers/irqchip/irq-loongson-liointc.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-loongson-liointc.c b/drivers/irqchip/irq-loongson-liointc.c index 18de2c09ece4..63b61474a0cc 100644 --- a/drivers/irqchip/irq-loongson-liointc.c +++ b/drivers/irqchip/irq-loongson-liointc.c @@ -32,6 +32,8 @@ #define LIOINTC_SHIFT_INTx 4 +#define LIOINTC_ERRATA_IRQ 10 + struct liointc_handler_data { struct liointc_priv *priv; u32 parent_int_map; @@ -41,6 +43,7 @@ struct liointc_priv { struct irq_chip_generic *gc; struct liointc_handler_data handler[LIOINTC_NUM_PARENT]; u8 map_cache[LIOINTC_CHIP_IRQ]; + bool has_lpc_irq_errata; }; static void liointc_chained_handle_irq(struct irq_desc *desc) @@ -54,8 +57,15 @@ static void liointc_chained_handle_irq(struct irq_desc *desc) pending = readl(gc->reg_base + LIOINTC_REG_INTC_STATUS); - if (!pending) - spurious_interrupt(); + if (!pending) { + /* Always blame LPC IRQ if we have that bug */ + if (handler->priv->has_lpc_irq_errata && + (handler->parent_int_map & ~gc->mask_cache & + BIT(LIOINTC_ERRATA_IRQ))) + pending = BIT(LIOINTC_ERRATA_IRQ); + else + spurious_interrupt(); + } while (pending) { int bit = __ffs(pending); -- cgit v1.3 From a93f1d903fa34fc2c5d9fa450bdb6c28d6fdfe00 Mon Sep 17 00:00:00 2001 From: Jiaxun Yang Date: Wed, 25 Mar 2020 11:54:57 +0800 Subject: irqchip: Add driver for Loongson-3 HyperTransport PIC controller This controller appeared on Loongson-3 family of chips to receive interrupts from PCH PIC. It is a I8259 with optimized interrupt polling flow. We can poll interrupt number from HT vector directly but still have to follow standard I8259 routines to mask, unmask and EOI. Signed-off-by: Jiaxun Yang Co-developed-by: Huacai Chen Signed-off-by: Huacai Chen Reviewed-by: Marc Zyngier Signed-off-by: Thomas Bogendoerfer --- arch/mips/include/asm/i8259.h | 1 + drivers/irqchip/Kconfig | 10 +++ drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-loongson-htpic.c | 149 +++++++++++++++++++++++++++++++++++ 4 files changed, 161 insertions(+) create mode 100644 drivers/irqchip/irq-loongson-htpic.c (limited to 'drivers') diff --git a/arch/mips/include/asm/i8259.h b/arch/mips/include/asm/i8259.h index 97a5e41ed1ab..a54b9649de22 100644 --- a/arch/mips/include/asm/i8259.h +++ b/arch/mips/include/asm/i8259.h @@ -36,6 +36,7 @@ extern raw_spinlock_t i8259A_lock; extern void make_8259A_irq(unsigned int irq); extern void init_i8259_irqs(void); +extern struct irq_domain *__init_i8259_irqs(struct device_node *node); /** * i8159_set_poll() - Override the i8259 polling function diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index c609eaa319d2..cae6f480c987 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -522,4 +522,14 @@ config LOONGSON_LIOINTC help Support for the Loongson Local I/O Interrupt Controller. +config LOONGSON_HTPIC + bool "Loongson3 HyperTransport PIC Controller" + depends on MACH_LOONGSON64 + default y + select IRQ_DOMAIN + select GENERIC_IRQ_CHIP + select I8259 + help + Support for the Loongson-3 HyperTransport PIC Controller. + endmenu diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 5e7678efdfe6..37bbe39bf909 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -106,3 +106,4 @@ obj-$(CONFIG_LS1X_IRQ) += irq-ls1x.o obj-$(CONFIG_TI_SCI_INTR_IRQCHIP) += irq-ti-sci-intr.o obj-$(CONFIG_TI_SCI_INTA_IRQCHIP) += irq-ti-sci-inta.o obj-$(CONFIG_LOONGSON_LIOINTC) += irq-loongson-liointc.o +obj-$(CONFIG_LOONGSON_HTPIC) += irq-loongson-htpic.o diff --git a/drivers/irqchip/irq-loongson-htpic.c b/drivers/irqchip/irq-loongson-htpic.c new file mode 100644 index 000000000000..dd018c22ea83 --- /dev/null +++ b/drivers/irqchip/irq-loongson-htpic.c @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020, Jiaxun Yang + * Loongson HTPIC IRQ support + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define HTPIC_MAX_PARENT_IRQ 4 +#define HTINT_NUM_VECTORS 8 +#define HTINT_EN_OFF 0x20 + +struct loongson_htpic { + void __iomem *base; + struct irq_domain *domain; +}; + +static struct loongson_htpic *htpic; + +static void htpic_irq_dispatch(struct irq_desc *desc) +{ + struct loongson_htpic *priv = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + uint32_t pending; + + chained_irq_enter(chip, desc); + pending = readl(priv->base); + /* Ack all IRQs at once, otherwise IRQ flood might happen */ + writel(pending, priv->base); + + if (!pending) + spurious_interrupt(); + + while (pending) { + int bit = __ffs(pending); + + if (unlikely(bit > 15)) { + spurious_interrupt(); + break; + } + + generic_handle_irq(irq_linear_revmap(priv->domain, bit)); + pending &= ~BIT(bit); + } + chained_irq_exit(chip, desc); +} + +static void htpic_reg_init(void) +{ + int i; + + for (i = 0; i < HTINT_NUM_VECTORS; i++) { + uint32_t val; + + /* Disable all HT Vectors */ + writel(0x0, htpic->base + HTINT_EN_OFF + i * 0x4); + val = readl(htpic->base + i * 0x4); + /* Ack all possible pending IRQs */ + writel(GENMASK(31, 0), htpic->base + i * 0x4); + } + + /* Enable 16 vectors for PIC */ + writel(0xffff, htpic->base + HTINT_EN_OFF); +} + +static void htpic_resume(void) +{ + htpic_reg_init(); +} + +struct syscore_ops htpic_syscore_ops = { + .resume = htpic_resume, +}; + +int __init htpic_of_init(struct device_node *node, struct device_node *parent) +{ + unsigned int parent_irq[4]; + int i, err; + int num_parents = 0; + + if (htpic) { + pr_err("loongson-htpic: Only one HTPIC is allowed in the system\n"); + return -ENODEV; + } + + htpic = kzalloc(sizeof(*htpic), GFP_KERNEL); + if (!htpic) { + err = -ENOMEM; + goto out_free; + } + + htpic->base = of_iomap(node, 0); + if (!htpic->base) { + err = -ENODEV; + goto out_free; + } + + htpic->domain = __init_i8259_irqs(node); + if (!htpic->domain) { + pr_err("loongson-htpic: Failed to initialize i8259 IRQs\n"); + err = -ENOMEM; + goto out_iounmap; + } + + /* Interrupt may come from any of the 4 interrupt line */ + for (i = 0; i < HTPIC_MAX_PARENT_IRQ; i++) { + parent_irq[i] = irq_of_parse_and_map(node, i); + if (parent_irq[i] <= 0) + break; + + num_parents++; + } + + if (!num_parents) { + pr_err("loongson-htpic: Failed to get parent irqs\n"); + err = -ENODEV; + goto out_remove_domain; + } + + htpic_reg_init(); + + for (i = 0; i < num_parents; i++) { + irq_set_chained_handler_and_data(parent_irq[i], + htpic_irq_dispatch, htpic); + } + + register_syscore_ops(&htpic_syscore_ops); + + return 0; + +out_remove_domain: + irq_domain_remove(htpic->domain); +out_iounmap: + iounmap(htpic->base); +out_free: + kfree(htpic); + return err; +} + +IRQCHIP_DECLARE(loongson_htpic, "loongson,htpic-1.0", htpic_of_init); -- cgit v1.3