SuperH updates for 3.6-rc1 merge window
- Migration off of old-style dynamic IRQ API.
 
 - irqdomain and generic irq chip propagation.
 
 - div4/6 clock consolidation, another step towards co-existing
   with the common struct clk infrastructure.
 
 - Extensive PFC rework
   - Decoupling GPIO from pin state.
   - Initial pinctrl support to facilitate incremental migration
     off of legacy pinmux.
   - gpiolib support made optional, and made pinctrl-backed.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.11 (GNU/Linux)
 
 iEYEABECAAYFAlAM6oMACgkQGkmNcg7/o7i63gCgru+ZynCrxdH5zq4er46MFoAF
 DVsAoLq3P+UCPDZc7pvksr6kgFiYGHG3
 =Dd+j
 -----END PGP SIGNATURE-----
Merge tag 'sh-for-linus' of git://github.com/pmundt/linux-sh
Pull SuperH updates from Paul Mundt:
 - Migration off of old-style dynamic IRQ API.
 - irqdomain and generic irq chip propagation.
 - div4/6 clock consolidation, another step towards co-existing with the
   common struct clk infrastructure.
 - Extensive PFC rework
   - Decoupling GPIO from pin state.
   - Initial pinctrl support to facilitate incremental migration off of
     legacy pinmux.
   - gpiolib support made optional, and made pinctrl-backed.
* tag 'sh-for-linus' of git://github.com/pmundt/linux-sh: (38 commits)
  sh: pfc: pin config get/set support.
  sh: pfc: Prefer DRV_NAME over KBUILD_MODNAME.
  sh: pfc: pinctrl legacy group support.
  sh: pfc: Ignore pinmux GPIOs with invalid enum IDs.
  sh: pfc: Export pinctrl binding init symbol.
  sh: pfc: Error out on pinctrl init resolution failure.
  sh: pfc: Make pr_fmt consistent across pfc drivers.
  sh: pfc: pinctrl legacy function support.
  sh: pfc: Rudimentary pinctrl-backed GPIO support.
  sh: pfc: Dumb GPIO stringification.
  sh: pfc: Shuffle PFC support core.
  sh: pfc: Verify pin type encoding size at build time.
  sh: pfc: Kill off unused pinmux bias flags.
  sh: pfc: Make gpio chip support optional where possible.
  sh: pfc: Split out gpio chip support.
  sh64: Fix up section mismatch warnings.
  sh64: Attempt to make reserved insn trap handler resemble C.
  sh: Consolidate die definitions for trap handlers.
  sh64: Kill off old exception debugging helpers.
  sh64: Use generic unaligned access control/counters.
  ...
			
			
This commit is contained in:
		
						commit
						47b170af84
					
				| @ -60,6 +60,7 @@ config SUPERH32 | ||||
| 
 | ||||
| config SUPERH64 | ||||
| 	def_bool ARCH = "sh64" | ||||
| 	select KALLSYMS | ||||
| 
 | ||||
| config ARCH_DEFCONFIG | ||||
| 	string | ||||
|  | ||||
| @ -44,6 +44,8 @@ config SH_7721_SOLUTION_ENGINE | ||||
| config SH_7722_SOLUTION_ENGINE | ||||
| 	bool "SolutionEngine7722" | ||||
| 	select SOLUTION_ENGINE | ||||
| 	select GENERIC_IRQ_CHIP | ||||
| 	select IRQ_DOMAIN | ||||
| 	depends on CPU_SUBTYPE_SH7722 | ||||
| 	help | ||||
| 	  Select 7722 SolutionEngine if configuring for a Hitachi SH772 | ||||
| @ -80,6 +82,8 @@ config SH_7780_SOLUTION_ENGINE | ||||
| config SH_7343_SOLUTION_ENGINE | ||||
| 	bool "SolutionEngine7343" | ||||
| 	select SOLUTION_ENGINE | ||||
| 	select GENERIC_IRQ_CHIP | ||||
| 	select IRQ_DOMAIN | ||||
| 	depends on CPU_SUBTYPE_SH7343 | ||||
| 	help | ||||
| 	  Select 7343 SolutionEngine if configuring for a Hitachi | ||||
| @ -295,6 +299,7 @@ config SH_X3PROTO | ||||
| 	bool "SH-X3 Prototype board" | ||||
| 	depends on CPU_SUBTYPE_SHX3 | ||||
| 	select NO_IOPORT if !PCI | ||||
| 	select IRQ_DOMAIN | ||||
| 
 | ||||
| config SH_MAGIC_PANEL_R2 | ||||
| 	bool "Magic Panel R2" | ||||
|  | ||||
| @ -8,10 +8,11 @@ | ||||
|  * This file is part of the LinuxDC project (www.linuxdc.org) | ||||
|  * Released under the terms of the GNU GPL v2.0 | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/irq.h> | ||||
| #include <linux/io.h> | ||||
| #include <asm/irq.h> | ||||
| #include <linux/irq.h> | ||||
| #include <linux/export.h> | ||||
| #include <linux/err.h> | ||||
| #include <mach/sysasic.h> | ||||
| 
 | ||||
| /*
 | ||||
| @ -141,26 +142,15 @@ int systemasic_irq_demux(int irq) | ||||
| 
 | ||||
| void systemasic_irq_init(void) | ||||
| { | ||||
| 	int i, nid = cpu_to_node(boot_cpu_data); | ||||
| 	int irq_base, i; | ||||
| 
 | ||||
| 	/* Assign all virtual IRQs to the System ASIC int. handler */ | ||||
| 	for (i = HW_EVENT_IRQ_BASE; i < HW_EVENT_IRQ_MAX; i++) { | ||||
| 		unsigned int irq; | ||||
| 
 | ||||
| 		irq = create_irq_nr(i, nid); | ||||
| 		if (unlikely(irq == 0)) { | ||||
| 			pr_err("%s: failed hooking irq %d for systemasic\n", | ||||
| 			       __func__, i); | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		if (unlikely(irq != i)) { | ||||
| 			pr_err("%s: got irq %d but wanted %d, bailing.\n", | ||||
| 			       __func__, irq, i); | ||||
| 			destroy_irq(irq); | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		irq_set_chip_and_handler(i, &systemasic_int, handle_level_irq); | ||||
| 	irq_base = irq_alloc_descs(HW_EVENT_IRQ_BASE, HW_EVENT_IRQ_BASE, | ||||
| 				   HW_EVENT_IRQ_MAX - HW_EVENT_IRQ_BASE, -1); | ||||
| 	if (IS_ERR_VALUE(irq_base)) { | ||||
| 		pr_err("%s: failed hooking irqs\n", __func__); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	for (i = HW_EVENT_IRQ_BASE; i < HW_EVENT_IRQ_MAX; i++) | ||||
| 		irq_set_chip_and_handler(i, &systemasic_int, handle_level_irq); | ||||
| } | ||||
|  | ||||
| @ -1,54 +1,109 @@ | ||||
| /*
 | ||||
|  * linux/arch/sh/boards/se/7343/irq.c | ||||
|  * Hitachi UL SolutionEngine 7343 FPGA IRQ Support. | ||||
|  * | ||||
|  * Copyright (C) 2008  Yoshihiro Shimoda | ||||
|  * Copyright (C) 2012  Paul Mundt | ||||
|  * | ||||
|  * Based on linux/arch/sh/boards/se/7722/irq.c | ||||
|  * Based on linux/arch/sh/boards/se/7343/irq.c | ||||
|  * Copyright (C) 2007  Nobuhiro Iwamatsu | ||||
|  * | ||||
|  * 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. | ||||
|  */ | ||||
| #define DRV_NAME "SE7343-FPGA" | ||||
| #define pr_fmt(fmt) DRV_NAME ": " fmt | ||||
| 
 | ||||
| #define irq_reg_readl	ioread16 | ||||
| #define irq_reg_writel	iowrite16 | ||||
| 
 | ||||
| #include <linux/init.h> | ||||
| #include <linux/irq.h> | ||||
| #include <linux/interrupt.h> | ||||
| #include <linux/irqdomain.h> | ||||
| #include <linux/io.h> | ||||
| #include <asm/sizes.h> | ||||
| #include <mach-se/mach/se7343.h> | ||||
| 
 | ||||
| unsigned int se7343_fpga_irq[SE7343_FPGA_IRQ_NR] = { 0, }; | ||||
| #define PA_CPLD_BASE_ADDR	0x11400000 | ||||
| #define PA_CPLD_ST_REG		0x08	/* CPLD Interrupt status register */ | ||||
| #define PA_CPLD_IMSK_REG	0x0a	/* CPLD Interrupt mask register */ | ||||
| 
 | ||||
| static void disable_se7343_irq(struct irq_data *data) | ||||
| { | ||||
| 	unsigned int bit = (unsigned int)irq_data_get_irq_chip_data(data); | ||||
| 	__raw_writew(__raw_readw(PA_CPLD_IMSK) | 1 << bit, PA_CPLD_IMSK); | ||||
| } | ||||
| 
 | ||||
| static void enable_se7343_irq(struct irq_data *data) | ||||
| { | ||||
| 	unsigned int bit = (unsigned int)irq_data_get_irq_chip_data(data); | ||||
| 	__raw_writew(__raw_readw(PA_CPLD_IMSK) & ~(1 << bit), PA_CPLD_IMSK); | ||||
| } | ||||
| 
 | ||||
| static struct irq_chip se7343_irq_chip __read_mostly = { | ||||
| 	.name		= "SE7343-FPGA", | ||||
| 	.irq_mask	= disable_se7343_irq, | ||||
| 	.irq_unmask	= enable_se7343_irq, | ||||
| }; | ||||
| static void __iomem *se7343_irq_regs; | ||||
| struct irq_domain *se7343_irq_domain; | ||||
| 
 | ||||
| static void se7343_irq_demux(unsigned int irq, struct irq_desc *desc) | ||||
| { | ||||
| 	unsigned short intv = __raw_readw(PA_CPLD_ST); | ||||
| 	unsigned int ext_irq = 0; | ||||
| 	struct irq_data *data = irq_get_irq_data(irq); | ||||
| 	struct irq_chip *chip = irq_data_get_irq_chip(data); | ||||
| 	unsigned long mask; | ||||
| 	int bit; | ||||
| 
 | ||||
| 	intv &= (1 << SE7343_FPGA_IRQ_NR) - 1; | ||||
| 	chip->irq_mask_ack(data); | ||||
| 
 | ||||
| 	for (; intv; intv >>= 1, ext_irq++) { | ||||
| 		if (!(intv & 1)) | ||||
| 			continue; | ||||
| 	mask = ioread16(se7343_irq_regs + PA_CPLD_ST_REG); | ||||
| 
 | ||||
| 		generic_handle_irq(se7343_fpga_irq[ext_irq]); | ||||
| 	for_each_set_bit(bit, &mask, SE7343_FPGA_IRQ_NR) | ||||
| 		generic_handle_irq(irq_linear_revmap(se7343_irq_domain, bit)); | ||||
| 
 | ||||
| 	chip->irq_unmask(data); | ||||
| } | ||||
| 
 | ||||
| static void __init se7343_domain_init(void) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	se7343_irq_domain = irq_domain_add_linear(NULL, SE7343_FPGA_IRQ_NR, | ||||
| 						  &irq_domain_simple_ops, NULL); | ||||
| 	if (unlikely(!se7343_irq_domain)) { | ||||
| 		printk("Failed to get IRQ domain\n"); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	for (i = 0; i < SE7343_FPGA_IRQ_NR; i++) { | ||||
| 		int irq = irq_create_mapping(se7343_irq_domain, i); | ||||
| 
 | ||||
| 		if (unlikely(irq == 0)) { | ||||
| 			printk("Failed to allocate IRQ %d\n", i); | ||||
| 			return; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void __init se7343_gc_init(void) | ||||
| { | ||||
| 	struct irq_chip_generic *gc; | ||||
| 	struct irq_chip_type *ct; | ||||
| 	unsigned int irq_base; | ||||
| 
 | ||||
| 	irq_base = irq_linear_revmap(se7343_irq_domain, 0); | ||||
| 
 | ||||
| 	gc = irq_alloc_generic_chip(DRV_NAME, 1, irq_base, se7343_irq_regs, | ||||
| 				    handle_level_irq); | ||||
| 	if (unlikely(!gc)) | ||||
| 		return; | ||||
| 
 | ||||
| 	ct = gc->chip_types; | ||||
| 	ct->chip.irq_mask = irq_gc_mask_set_bit; | ||||
| 	ct->chip.irq_unmask = irq_gc_mask_clr_bit; | ||||
| 
 | ||||
| 	ct->regs.mask = PA_CPLD_IMSK_REG; | ||||
| 
 | ||||
| 	irq_setup_generic_chip(gc, IRQ_MSK(SE7343_FPGA_IRQ_NR), | ||||
| 			       IRQ_GC_INIT_MASK_CACHE, | ||||
| 			       IRQ_NOREQUEST | IRQ_NOPROBE, 0); | ||||
| 
 | ||||
| 	irq_set_chained_handler(IRQ0_IRQ, se7343_irq_demux); | ||||
| 	irq_set_irq_type(IRQ0_IRQ, IRQ_TYPE_LEVEL_LOW); | ||||
| 
 | ||||
| 	irq_set_chained_handler(IRQ1_IRQ, se7343_irq_demux); | ||||
| 	irq_set_irq_type(IRQ1_IRQ, IRQ_TYPE_LEVEL_LOW); | ||||
| 
 | ||||
| 	irq_set_chained_handler(IRQ4_IRQ, se7343_irq_demux); | ||||
| 	irq_set_irq_type(IRQ4_IRQ, IRQ_TYPE_LEVEL_LOW); | ||||
| 
 | ||||
| 	irq_set_chained_handler(IRQ5_IRQ, se7343_irq_demux); | ||||
| 	irq_set_irq_type(IRQ5_IRQ, IRQ_TYPE_LEVEL_LOW); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
| @ -56,31 +111,19 @@ static void se7343_irq_demux(unsigned int irq, struct irq_desc *desc) | ||||
|  */ | ||||
| void __init init_7343se_IRQ(void) | ||||
| { | ||||
| 	int i, irq; | ||||
| 
 | ||||
| 	__raw_writew(0, PA_CPLD_IMSK);	/* disable all irqs */ | ||||
| 	__raw_writew(0x2000, 0xb03fffec);	/* mrshpc irq enable */ | ||||
| 
 | ||||
| 	for (i = 0; i < SE7343_FPGA_IRQ_NR; i++) { | ||||
| 		irq = create_irq(); | ||||
| 		if (irq < 0) | ||||
| 			return; | ||||
| 		se7343_fpga_irq[i] = irq; | ||||
| 
 | ||||
| 		irq_set_chip_and_handler_name(se7343_fpga_irq[i], | ||||
| 					      &se7343_irq_chip, | ||||
| 					      handle_level_irq, | ||||
| 					      "level"); | ||||
| 
 | ||||
| 		irq_set_chip_data(se7343_fpga_irq[i], (void *)i); | ||||
| 	se7343_irq_regs = ioremap(PA_CPLD_BASE_ADDR, SZ_16); | ||||
| 	if (unlikely(!se7343_irq_regs)) { | ||||
| 		pr_err("Failed to remap CPLD\n"); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	irq_set_chained_handler(IRQ0_IRQ, se7343_irq_demux); | ||||
| 	irq_set_irq_type(IRQ0_IRQ, IRQ_TYPE_LEVEL_LOW); | ||||
| 	irq_set_chained_handler(IRQ1_IRQ, se7343_irq_demux); | ||||
| 	irq_set_irq_type(IRQ1_IRQ, IRQ_TYPE_LEVEL_LOW); | ||||
| 	irq_set_chained_handler(IRQ4_IRQ, se7343_irq_demux); | ||||
| 	irq_set_irq_type(IRQ4_IRQ, IRQ_TYPE_LEVEL_LOW); | ||||
| 	irq_set_chained_handler(IRQ5_IRQ, se7343_irq_demux); | ||||
| 	irq_set_irq_type(IRQ5_IRQ, IRQ_TYPE_LEVEL_LOW); | ||||
| 	/*
 | ||||
| 	 * All FPGA IRQs disabled by default | ||||
| 	 */ | ||||
| 	iowrite16(0, se7343_irq_regs + PA_CPLD_IMSK_REG); | ||||
| 
 | ||||
| 	__raw_writew(0x2000, 0xb03fffec);	/* mrshpc irq enable */ | ||||
| 
 | ||||
| 	se7343_domain_init(); | ||||
| 	se7343_gc_init(); | ||||
| } | ||||
|  | ||||
| @ -5,6 +5,7 @@ | ||||
| #include <linux/serial_reg.h> | ||||
| #include <linux/usb/isp116x.h> | ||||
| #include <linux/delay.h> | ||||
| #include <linux/irqdomain.h> | ||||
| #include <asm/machvec.h> | ||||
| #include <mach-se/mach/se7343.h> | ||||
| #include <asm/heartbeat.h> | ||||
| @ -145,11 +146,12 @@ static struct platform_device *sh7343se_platform_devices[] __initdata = { | ||||
| static int __init sh7343se_devices_setup(void) | ||||
| { | ||||
| 	/* Wire-up dynamic vectors */ | ||||
| 	serial_platform_data[0].irq = se7343_fpga_irq[SE7343_FPGA_IRQ_UARTA]; | ||||
| 	serial_platform_data[1].irq = se7343_fpga_irq[SE7343_FPGA_IRQ_UARTB]; | ||||
| 
 | ||||
| 	serial_platform_data[0].irq = irq_find_mapping(se7343_irq_domain, | ||||
| 						       SE7343_FPGA_IRQ_UARTA); | ||||
| 	serial_platform_data[1].irq = irq_find_mapping(se7343_irq_domain, | ||||
| 						       SE7343_FPGA_IRQ_UARTB); | ||||
| 	usb_resources[2].start = usb_resources[2].end = | ||||
| 		se7343_fpga_irq[SE7343_FPGA_IRQ_USB]; | ||||
| 		irq_find_mapping(se7343_irq_domain, SE7343_FPGA_IRQ_USB); | ||||
| 
 | ||||
| 	return platform_add_devices(sh7343se_platform_devices, | ||||
| 				    ARRAY_SIZE(sh7343se_platform_devices)); | ||||
|  | ||||
| @ -1,79 +1,96 @@ | ||||
| /*
 | ||||
|  * linux/arch/sh/boards/se/7722/irq.c | ||||
|  * Hitachi UL SolutionEngine 7722 FPGA IRQ Support. | ||||
|  * | ||||
|  * Copyright (C) 2007  Nobuhiro Iwamatsu | ||||
|  * | ||||
|  * Hitachi UL SolutionEngine 7722 Support. | ||||
|  * Copyright (C) 2012  Paul Mundt | ||||
|  * | ||||
|  * 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. | ||||
|  */ | ||||
| #define DRV_NAME "SE7722-FPGA" | ||||
| #define pr_fmt(fmt) DRV_NAME ": " fmt | ||||
| 
 | ||||
| #define irq_reg_readl	ioread16 | ||||
| #define irq_reg_writel	iowrite16 | ||||
| 
 | ||||
| #include <linux/init.h> | ||||
| #include <linux/irq.h> | ||||
| #include <linux/interrupt.h> | ||||
| #include <asm/irq.h> | ||||
| #include <asm/io.h> | ||||
| #include <linux/irqdomain.h> | ||||
| #include <linux/io.h> | ||||
| #include <linux/err.h> | ||||
| #include <asm/sizes.h> | ||||
| #include <mach-se/mach/se7722.h> | ||||
| 
 | ||||
| unsigned int se7722_fpga_irq[SE7722_FPGA_IRQ_NR] = { 0, }; | ||||
| #define IRQ01_BASE_ADDR	0x11800000 | ||||
| #define IRQ01_MODE_REG	0 | ||||
| #define IRQ01_STS_REG	4 | ||||
| #define IRQ01_MASK_REG	8 | ||||
| 
 | ||||
| static void disable_se7722_irq(struct irq_data *data) | ||||
| { | ||||
| 	unsigned int bit = (unsigned int)irq_data_get_irq_chip_data(data); | ||||
| 	__raw_writew(__raw_readw(IRQ01_MASK) | 1 << bit, IRQ01_MASK); | ||||
| } | ||||
| 
 | ||||
| static void enable_se7722_irq(struct irq_data *data) | ||||
| { | ||||
| 	unsigned int bit = (unsigned int)irq_data_get_irq_chip_data(data); | ||||
| 	__raw_writew(__raw_readw(IRQ01_MASK) & ~(1 << bit), IRQ01_MASK); | ||||
| } | ||||
| 
 | ||||
| static struct irq_chip se7722_irq_chip __read_mostly = { | ||||
| 	.name		= "SE7722-FPGA", | ||||
| 	.irq_mask	= disable_se7722_irq, | ||||
| 	.irq_unmask	= enable_se7722_irq, | ||||
| }; | ||||
| static void __iomem *se7722_irq_regs; | ||||
| struct irq_domain *se7722_irq_domain; | ||||
| 
 | ||||
| static void se7722_irq_demux(unsigned int irq, struct irq_desc *desc) | ||||
| { | ||||
| 	unsigned short intv = __raw_readw(IRQ01_STS); | ||||
| 	unsigned int ext_irq = 0; | ||||
| 	struct irq_data *data = irq_get_irq_data(irq); | ||||
| 	struct irq_chip *chip = irq_data_get_irq_chip(data); | ||||
| 	unsigned long mask; | ||||
| 	int bit; | ||||
| 
 | ||||
| 	intv &= (1 << SE7722_FPGA_IRQ_NR) - 1; | ||||
| 	chip->irq_mask_ack(data); | ||||
| 
 | ||||
| 	for (; intv; intv >>= 1, ext_irq++) { | ||||
| 		if (!(intv & 1)) | ||||
| 			continue; | ||||
| 	mask = ioread16(se7722_irq_regs + IRQ01_STS_REG); | ||||
| 
 | ||||
| 		generic_handle_irq(se7722_fpga_irq[ext_irq]); | ||||
| 	for_each_set_bit(bit, &mask, SE7722_FPGA_IRQ_NR) | ||||
| 		generic_handle_irq(irq_linear_revmap(se7722_irq_domain, bit)); | ||||
| 
 | ||||
| 	chip->irq_unmask(data); | ||||
| } | ||||
| 
 | ||||
| static void __init se7722_domain_init(void) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	se7722_irq_domain = irq_domain_add_linear(NULL, SE7722_FPGA_IRQ_NR, | ||||
| 						  &irq_domain_simple_ops, NULL); | ||||
| 	if (unlikely(!se7722_irq_domain)) { | ||||
| 		printk("Failed to get IRQ domain\n"); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	for (i = 0; i < SE7722_FPGA_IRQ_NR; i++) { | ||||
| 		int irq = irq_create_mapping(se7722_irq_domain, i); | ||||
| 
 | ||||
| 		if (unlikely(irq == 0)) { | ||||
| 			printk("Failed to allocate IRQ %d\n", i); | ||||
| 			return; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Initialize IRQ setting | ||||
|  */ | ||||
| void __init init_se7722_IRQ(void) | ||||
| static void __init se7722_gc_init(void) | ||||
| { | ||||
| 	int i, irq; | ||||
| 	struct irq_chip_generic *gc; | ||||
| 	struct irq_chip_type *ct; | ||||
| 	unsigned int irq_base; | ||||
| 
 | ||||
| 	__raw_writew(0, IRQ01_MASK);       /* disable all irqs */ | ||||
| 	__raw_writew(0x2000, 0xb03fffec);  /* mrshpc irq enable */ | ||||
| 	irq_base = irq_linear_revmap(se7722_irq_domain, 0); | ||||
| 
 | ||||
| 	for (i = 0; i < SE7722_FPGA_IRQ_NR; i++) { | ||||
| 		irq = create_irq(); | ||||
| 		if (irq < 0) | ||||
| 			return; | ||||
| 		se7722_fpga_irq[i] = irq; | ||||
| 	gc = irq_alloc_generic_chip(DRV_NAME, 1, irq_base, se7722_irq_regs, | ||||
| 				    handle_level_irq); | ||||
| 	if (unlikely(!gc)) | ||||
| 		return; | ||||
| 
 | ||||
| 		irq_set_chip_and_handler_name(se7722_fpga_irq[i], | ||||
| 					      &se7722_irq_chip, | ||||
| 					      handle_level_irq, | ||||
| 					      "level"); | ||||
| 	ct = gc->chip_types; | ||||
| 	ct->chip.irq_mask = irq_gc_mask_set_bit; | ||||
| 	ct->chip.irq_unmask = irq_gc_mask_clr_bit; | ||||
| 
 | ||||
| 		irq_set_chip_data(se7722_fpga_irq[i], (void *)i); | ||||
| 	} | ||||
| 	ct->regs.mask = IRQ01_MASK_REG; | ||||
| 
 | ||||
| 	irq_setup_generic_chip(gc, IRQ_MSK(SE7722_FPGA_IRQ_NR), | ||||
| 			       IRQ_GC_INIT_MASK_CACHE, | ||||
| 			       IRQ_NOREQUEST | IRQ_NOPROBE, 0); | ||||
| 
 | ||||
| 	irq_set_chained_handler(IRQ0_IRQ, se7722_irq_demux); | ||||
| 	irq_set_irq_type(IRQ0_IRQ, IRQ_TYPE_LEVEL_LOW); | ||||
| @ -81,3 +98,25 @@ void __init init_se7722_IRQ(void) | ||||
| 	irq_set_chained_handler(IRQ1_IRQ, se7722_irq_demux); | ||||
| 	irq_set_irq_type(IRQ1_IRQ, IRQ_TYPE_LEVEL_LOW); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Initialize FPGA IRQs | ||||
|  */ | ||||
| void __init init_se7722_IRQ(void) | ||||
| { | ||||
| 	se7722_irq_regs = ioremap(IRQ01_BASE_ADDR, SZ_16); | ||||
| 	if (unlikely(!se7722_irq_regs)) { | ||||
| 		printk("Failed to remap IRQ01 regs\n"); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * All FPGA IRQs disabled by default | ||||
| 	 */ | ||||
| 	iowrite16(0, se7722_irq_regs + IRQ01_MASK_REG); | ||||
| 
 | ||||
| 	__raw_writew(0x2000, 0xb03fffec);  /* mrshpc irq enable */ | ||||
| 
 | ||||
| 	se7722_domain_init(); | ||||
| 	se7722_gc_init(); | ||||
| } | ||||
|  | ||||
| @ -2,6 +2,7 @@ | ||||
|  * linux/arch/sh/boards/se/7722/setup.c | ||||
|  * | ||||
|  * Copyright (C) 2007 Nobuhiro Iwamatsu | ||||
|  * Copyright (C) 2012 Paul Mundt | ||||
|  * | ||||
|  * Hitachi UL SolutionEngine 7722 Support. | ||||
|  * | ||||
| @ -15,6 +16,7 @@ | ||||
| #include <linux/ata_platform.h> | ||||
| #include <linux/input.h> | ||||
| #include <linux/input/sh_keysc.h> | ||||
| #include <linux/irqdomain.h> | ||||
| #include <linux/smc91x.h> | ||||
| #include <linux/sh_intc.h> | ||||
| #include <mach-se/mach/se7722.h> | ||||
| @ -143,10 +145,10 @@ static int __init se7722_devices_setup(void) | ||||
| 
 | ||||
| 	/* Wire-up dynamic vectors */ | ||||
| 	cf_ide_resources[2].start = cf_ide_resources[2].end = | ||||
| 		se7722_fpga_irq[SE7722_FPGA_IRQ_MRSHPC0]; | ||||
| 		irq_find_mapping(se7722_irq_domain, SE7722_FPGA_IRQ_MRSHPC0); | ||||
| 
 | ||||
| 	smc91x_eth_resources[1].start = smc91x_eth_resources[1].end = | ||||
| 		se7722_fpga_irq[SE7722_FPGA_IRQ_SMC]; | ||||
| 		irq_find_mapping(se7722_irq_domain, SE7722_FPGA_IRQ_SMC); | ||||
| 
 | ||||
| 	return platform_add_devices(se7722_devices, ARRAY_SIZE(se7722_devices)); | ||||
| } | ||||
|  | ||||
| @ -17,8 +17,10 @@ | ||||
| #include <linux/init.h> | ||||
| #include <linux/irq.h> | ||||
| #include <linux/interrupt.h> | ||||
| #include <asm/irq.h> | ||||
| #include <asm/io.h> | ||||
| #include <linux/export.h> | ||||
| #include <linux/topology.h> | ||||
| #include <linux/io.h> | ||||
| #include <linux/err.h> | ||||
| #include <mach-se/mach/se7724.h> | ||||
| 
 | ||||
| struct fpga_irq { | ||||
| @ -111,7 +113,7 @@ static void se7724_irq_demux(unsigned int irq, struct irq_desc *desc) | ||||
|  */ | ||||
| void __init init_se7724_IRQ(void) | ||||
| { | ||||
| 	int i, nid = cpu_to_node(boot_cpu_data); | ||||
| 	int irq_base, i; | ||||
| 
 | ||||
| 	__raw_writew(0xffff, IRQ0_MR);  /* mask all */ | ||||
| 	__raw_writew(0xffff, IRQ1_MR);  /* mask all */ | ||||
| @ -121,29 +123,17 @@ void __init init_se7724_IRQ(void) | ||||
| 	__raw_writew(0x0000, IRQ2_SR);  /* clear irq */ | ||||
| 	__raw_writew(0x002a, IRQ_MODE); /* set irq type */ | ||||
| 
 | ||||
| 	for (i = 0; i < SE7724_FPGA_IRQ_NR; i++) { | ||||
| 		int irq, wanted; | ||||
| 
 | ||||
| 		wanted = SE7724_FPGA_IRQ_BASE + i; | ||||
| 
 | ||||
| 		irq = create_irq_nr(wanted, nid); | ||||
| 		if (unlikely(irq == 0)) { | ||||
| 			pr_err("%s: failed hooking irq %d for FPGA\n", | ||||
| 			       __func__, wanted); | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		if (unlikely(irq != wanted)) { | ||||
| 			pr_err("%s: got irq %d but wanted %d, bailing.\n", | ||||
| 			       __func__, irq, wanted); | ||||
| 			destroy_irq(irq); | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		irq_set_chip_and_handler_name(irq, &se7724_irq_chip, | ||||
| 					      handle_level_irq, "level"); | ||||
| 	irq_base = irq_alloc_descs(SE7724_FPGA_IRQ_BASE, SE7724_FPGA_IRQ_BASE, | ||||
| 				   SE7724_FPGA_IRQ_NR, numa_node_id()); | ||||
| 	if (IS_ERR_VALUE(irq_base)) { | ||||
| 		pr_err("%s: failed hooking irqs for FPGA\n", __func__); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	for (i = 0; i < SE7724_FPGA_IRQ_NR; i++) | ||||
| 		irq_set_chip_and_handler_name(irq_base + i, &se7724_irq_chip, | ||||
| 					      handle_level_irq, "level"); | ||||
| 
 | ||||
| 	irq_set_chained_handler(IRQ0_IRQ, se7724_irq_demux); | ||||
| 	irq_set_irq_type(IRQ0_IRQ, IRQ_TYPE_LEVEL_LOW); | ||||
| 
 | ||||
|  | ||||
| @ -3,7 +3,7 @@ | ||||
|  * | ||||
|  * Renesas SH-X3 Prototype Baseboard GPIO Support. | ||||
|  * | ||||
|  * Copyright (C) 2010  Paul Mundt | ||||
|  * Copyright (C) 2010 - 2012  Paul Mundt | ||||
|  * | ||||
|  * 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 | ||||
| @ -17,6 +17,7 @@ | ||||
| #include <linux/irq.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/spinlock.h> | ||||
| #include <linux/irqdomain.h> | ||||
| #include <linux/io.h> | ||||
| #include <mach/ilsel.h> | ||||
| #include <mach/hardware.h> | ||||
| @ -26,7 +27,7 @@ | ||||
| #define KEYDETR 0xb81c0004 | ||||
| 
 | ||||
| static DEFINE_SPINLOCK(x3proto_gpio_lock); | ||||
| static unsigned int x3proto_gpio_irq_map[NR_BASEBOARD_GPIOS] = { 0, }; | ||||
| static struct irq_domain *x3proto_irq_domain; | ||||
| 
 | ||||
| static int x3proto_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) | ||||
| { | ||||
| @ -49,7 +50,14 @@ static int x3proto_gpio_get(struct gpio_chip *chip, unsigned gpio) | ||||
| 
 | ||||
| static int x3proto_gpio_to_irq(struct gpio_chip *chip, unsigned gpio) | ||||
| { | ||||
| 	return x3proto_gpio_irq_map[gpio]; | ||||
| 	int virq; | ||||
| 
 | ||||
| 	if (gpio < chip->ngpio) | ||||
| 		virq = irq_create_mapping(x3proto_irq_domain, gpio); | ||||
| 	else | ||||
| 		virq = -ENXIO; | ||||
| 
 | ||||
| 	return virq; | ||||
| } | ||||
| 
 | ||||
| static void x3proto_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) | ||||
| @ -62,9 +70,8 @@ static void x3proto_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) | ||||
| 	chip->irq_mask_ack(data); | ||||
| 
 | ||||
| 	mask = __raw_readw(KEYDETR); | ||||
| 
 | ||||
| 	for_each_set_bit(pin, &mask, NR_BASEBOARD_GPIOS) | ||||
| 		generic_handle_irq(x3proto_gpio_to_irq(NULL, pin)); | ||||
| 		generic_handle_irq(irq_linear_revmap(x3proto_irq_domain, pin)); | ||||
| 
 | ||||
| 	chip->irq_unmask(data); | ||||
| } | ||||
| @ -78,10 +85,23 @@ struct gpio_chip x3proto_gpio_chip = { | ||||
| 	.ngpio			= NR_BASEBOARD_GPIOS, | ||||
| }; | ||||
| 
 | ||||
| static int x3proto_gpio_irq_map(struct irq_domain *domain, unsigned int virq, | ||||
| 				irq_hw_number_t hwirq) | ||||
| { | ||||
| 	irq_set_chip_and_handler_name(virq, &dummy_irq_chip, handle_simple_irq, | ||||
| 				      "gpio"); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static struct irq_domain_ops x3proto_gpio_irq_ops = { | ||||
| 	.map	= x3proto_gpio_irq_map, | ||||
| 	.xlate	= irq_domain_xlate_twocell, | ||||
| }; | ||||
| 
 | ||||
| int __init x3proto_gpio_setup(void) | ||||
| { | ||||
| 	int ilsel; | ||||
| 	int ret, i; | ||||
| 	int ilsel, ret; | ||||
| 
 | ||||
| 	ilsel = ilsel_enable(ILSEL_KEY); | ||||
| 	if (unlikely(ilsel < 0)) | ||||
| @ -91,21 +111,10 @@ int __init x3proto_gpio_setup(void) | ||||
| 	if (unlikely(ret)) | ||||
| 		goto err_gpio; | ||||
| 
 | ||||
| 	for (i = 0; i < NR_BASEBOARD_GPIOS; i++) { | ||||
| 		unsigned long flags; | ||||
| 		int irq = create_irq(); | ||||
| 
 | ||||
| 		if (unlikely(irq < 0)) { | ||||
| 			ret = -EINVAL; | ||||
| 			goto err_irq; | ||||
| 		} | ||||
| 
 | ||||
| 		spin_lock_irqsave(&x3proto_gpio_lock, flags); | ||||
| 		x3proto_gpio_irq_map[i] = irq; | ||||
| 		irq_set_chip_and_handler_name(irq, &dummy_irq_chip, | ||||
| 					      handle_simple_irq, "gpio"); | ||||
| 		spin_unlock_irqrestore(&x3proto_gpio_lock, flags); | ||||
| 	} | ||||
| 	x3proto_irq_domain = irq_domain_add_linear(NULL, NR_BASEBOARD_GPIOS, | ||||
| 						   &x3proto_gpio_irq_ops, NULL); | ||||
| 	if (unlikely(!x3proto_irq_domain)) | ||||
| 		goto err_irq; | ||||
| 
 | ||||
| 	pr_info("registering '%s' support, handling GPIOs %u -> %u, " | ||||
| 		"bound to IRQ %u\n", | ||||
| @ -119,10 +128,6 @@ int __init x3proto_gpio_setup(void) | ||||
| 	return 0; | ||||
| 
 | ||||
| err_irq: | ||||
| 	for (; i >= 0; --i) | ||||
| 		if (x3proto_gpio_irq_map[i]) | ||||
| 			destroy_irq(x3proto_gpio_irq_map[i]); | ||||
| 
 | ||||
| 	ret = gpiochip_remove(&x3proto_gpio_chip); | ||||
| 	if (unlikely(ret)) | ||||
| 		pr_err("Failed deregistering GPIO\n"); | ||||
|  | ||||
| @ -73,10 +73,7 @@ static void hd64461_irq_demux(unsigned int irq, struct irq_desc *desc) | ||||
| 
 | ||||
| int __init setup_hd64461(void) | ||||
| { | ||||
| 	int i, nid = cpu_to_node(boot_cpu_data); | ||||
| 
 | ||||
| 	if (!MACH_HD64461) | ||||
| 		return 0; | ||||
| 	int irq_base, i; | ||||
| 
 | ||||
| 	printk(KERN_INFO | ||||
| 	       "HD64461 configured at 0x%x on irq %d(mapped into %d to %d)\n", | ||||
| @ -89,28 +86,16 @@ int __init setup_hd64461(void) | ||||
| #endif | ||||
| 	__raw_writew(0xffff, HD64461_NIMR); | ||||
| 
 | ||||
| 	/*  IRQ 80 -> 95 belongs to HD64461  */ | ||||
| 	for (i = HD64461_IRQBASE; i < HD64461_IRQBASE + 16; i++) { | ||||
| 		unsigned int irq; | ||||
| 
 | ||||
| 		irq = create_irq_nr(i, nid); | ||||
| 		if (unlikely(irq == 0)) { | ||||
| 			pr_err("%s: failed hooking irq %d for HD64461\n", | ||||
| 			       __func__, i); | ||||
| 			return -EBUSY; | ||||
| 		} | ||||
| 
 | ||||
| 		if (unlikely(irq != i)) { | ||||
| 			pr_err("%s: got irq %d but wanted %d, bailing.\n", | ||||
| 			       __func__, irq, i); | ||||
| 			destroy_irq(irq); | ||||
| 			return -EINVAL; | ||||
| 		} | ||||
| 
 | ||||
| 		irq_set_chip_and_handler(i, &hd64461_irq_chip, | ||||
| 					 handle_level_irq); | ||||
| 	irq_base = irq_alloc_descs(HD64461_IRQBASE, HD64461_IRQBASE, 16, -1); | ||||
| 	if (IS_ERR_VALUE(irq_base)) { | ||||
| 		pr_err("%s: failed hooking irqs for HD64461\n", __func__); | ||||
| 		return irq_base; | ||||
| 	} | ||||
| 
 | ||||
| 	for (i = 0; i < 16; i++) | ||||
| 		irq_set_chip_and_handler(irq_base + i, &hd64461_irq_chip, | ||||
| 					 handle_level_irq); | ||||
| 
 | ||||
| 	irq_set_chained_handler(CONFIG_HD64461_IRQ, hd64461_irq_demux); | ||||
| 	irq_set_irq_type(CONFIG_HD64461_IRQ, IRQ_TYPE_LEVEL_LOW); | ||||
| 
 | ||||
|  | ||||
| @ -110,6 +110,10 @@ do {							\ | ||||
| #include <asm-generic/bug.h> | ||||
| 
 | ||||
| struct pt_regs; | ||||
| 
 | ||||
| /* arch/sh/kernel/traps.c */ | ||||
| extern void die(const char *str, struct pt_regs *regs, long err) __attribute__ ((noreturn)); | ||||
| extern void die_if_kernel(const char *str, struct pt_regs *regs, long err); | ||||
| extern void die_if_no_fixup(const char *str, struct pt_regs *regs, long err); | ||||
| 
 | ||||
| #endif /* __ASM_SH_BUG_H */ | ||||
|  | ||||
| @ -10,6 +10,8 @@ enum die_val { | ||||
| 	DIE_SSTEP, | ||||
| }; | ||||
| 
 | ||||
| /* arch/sh/kernel/dumpstack.c */ | ||||
| extern void printk_address(unsigned long address, int reliable); | ||||
| extern void dump_mem(const char *str, unsigned long bottom, unsigned long top); | ||||
| 
 | ||||
| #endif /* __ASM_SH_KDEBUG_H */ | ||||
|  | ||||
| @ -50,9 +50,6 @@ | ||||
| #define PA_LED		0xb0C00000	/* LED */ | ||||
| #define LED_SHIFT       0 | ||||
| #define PA_DIPSW	0xb0900000	/* Dip switch 31 */ | ||||
| #define PA_CPLD_MODESET	0xb1400004	/* CPLD Mode set register */ | ||||
| #define PA_CPLD_ST	0xb1400008	/* CPLD Interrupt status register */ | ||||
| #define PA_CPLD_IMSK	0xb140000a	/* CPLD Interrupt mask register */ | ||||
| /* Area 5 */ | ||||
| #define PA_EXT5		0x14000000 | ||||
| #define PA_EXT5_SIZE	0x04000000 | ||||
| @ -135,8 +132,10 @@ | ||||
| 
 | ||||
| #define SE7343_FPGA_IRQ_NR	12 | ||||
| 
 | ||||
| struct irq_domain; | ||||
| 
 | ||||
| /* arch/sh/boards/se/7343/irq.c */ | ||||
| extern unsigned int se7343_fpga_irq[]; | ||||
| extern struct irq_domain *se7343_irq_domain; | ||||
| 
 | ||||
| void init_7343se_IRQ(void); | ||||
| 
 | ||||
|  | ||||
| @ -81,12 +81,6 @@ | ||||
| #define IRQ0_IRQ        evt2irq(0x600) | ||||
| #define IRQ1_IRQ        evt2irq(0x620) | ||||
| 
 | ||||
| #define IRQ01_MODE      0xb1800000 | ||||
| #define IRQ01_STS       0xb1800004 | ||||
| #define IRQ01_MASK      0xb1800008 | ||||
| 
 | ||||
| /* Bits in IRQ01_* registers */ | ||||
| 
 | ||||
| #define SE7722_FPGA_IRQ_USB	0 /* IRQ0 */ | ||||
| #define SE7722_FPGA_IRQ_SMC	1 /* IRQ0 */ | ||||
| #define SE7722_FPGA_IRQ_MRSHPC0	2 /* IRQ1 */ | ||||
| @ -95,8 +89,10 @@ | ||||
| #define SE7722_FPGA_IRQ_MRSHPC3	5 /* IRQ1 */ | ||||
| #define SE7722_FPGA_IRQ_NR	6 | ||||
| 
 | ||||
| struct irq_domain; | ||||
| 
 | ||||
| /* arch/sh/boards/se/7722/irq.c */ | ||||
| extern unsigned int se7722_fpga_irq[]; | ||||
| extern struct irq_domain *se7722_irq_domain; | ||||
| 
 | ||||
| void init_se7722_IRQ(void); | ||||
| 
 | ||||
|  | ||||
| @ -16,6 +16,8 @@ | ||||
| #include <asm/ptrace.h> | ||||
| #include <asm/processor.h> | ||||
| #include <asm/io.h> | ||||
| #include <asm/unwinder.h> | ||||
| #include <asm/stacktrace.h> | ||||
| 
 | ||||
| static u8 regcache[63]; | ||||
| 
 | ||||
| @ -199,8 +201,11 @@ static int lookup_prev_stack_frame(unsigned long fp, unsigned long pc, | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /* Don't put this on the stack since we'll want to call sh64_unwind
 | ||||
|  * when we're close to underflowing the stack anyway. */ | ||||
| /*
 | ||||
|  * Don't put this on the stack since we'll want to call in to | ||||
|  * sh64_unwinder_dump() when we're close to underflowing the stack | ||||
|  * anyway. | ||||
|  */ | ||||
| static struct pt_regs here_regs; | ||||
| 
 | ||||
| extern const char syscall_ret; | ||||
| @ -208,17 +213,19 @@ extern const char ret_from_syscall; | ||||
| extern const char ret_from_exception; | ||||
| extern const char ret_from_irq; | ||||
| 
 | ||||
| static void sh64_unwind_inner(struct pt_regs *regs); | ||||
| static void sh64_unwind_inner(const struct stacktrace_ops *ops, | ||||
| 			      void *data, struct pt_regs *regs); | ||||
| 
 | ||||
| static void unwind_nested (unsigned long pc, unsigned long fp) | ||||
| static inline void unwind_nested(const struct stacktrace_ops *ops, void *data, | ||||
| 				 unsigned long pc, unsigned long fp) | ||||
| { | ||||
| 	if ((fp >= __MEMORY_START) && | ||||
| 	    ((fp & 7) == 0)) { | ||||
| 		sh64_unwind_inner((struct pt_regs *) fp); | ||||
| 	} | ||||
| 	    ((fp & 7) == 0)) | ||||
| 		sh64_unwind_inner(ops, data, (struct pt_regs *)fp); | ||||
| } | ||||
| 
 | ||||
| static void sh64_unwind_inner(struct pt_regs *regs) | ||||
| static void sh64_unwind_inner(const struct stacktrace_ops *ops, | ||||
| 			      void *data, struct pt_regs *regs) | ||||
| { | ||||
| 	unsigned long pc, fp; | ||||
| 	int ofs = 0; | ||||
| @ -232,29 +239,29 @@ static void sh64_unwind_inner(struct pt_regs *regs) | ||||
| 		int cond; | ||||
| 		unsigned long next_fp, next_pc; | ||||
| 
 | ||||
| 		if (pc == ((unsigned long) &syscall_ret & ~1)) { | ||||
| 		if (pc == ((unsigned long)&syscall_ret & ~1)) { | ||||
| 			printk("SYSCALL\n"); | ||||
| 			unwind_nested(pc,fp); | ||||
| 			unwind_nested(ops, data, pc, fp); | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		if (pc == ((unsigned long) &ret_from_syscall & ~1)) { | ||||
| 		if (pc == ((unsigned long)&ret_from_syscall & ~1)) { | ||||
| 			printk("SYSCALL (PREEMPTED)\n"); | ||||
| 			unwind_nested(pc,fp); | ||||
| 			unwind_nested(ops, data, pc, fp); | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		/* In this case, the PC is discovered by lookup_prev_stack_frame but
 | ||||
| 		   it has 4 taken off it to look like the 'caller' */ | ||||
| 		if (pc == ((unsigned long) &ret_from_exception & ~1)) { | ||||
| 		if (pc == ((unsigned long)&ret_from_exception & ~1)) { | ||||
| 			printk("EXCEPTION\n"); | ||||
| 			unwind_nested(pc,fp); | ||||
| 			unwind_nested(ops, data, pc, fp); | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		if (pc == ((unsigned long) &ret_from_irq & ~1)) { | ||||
| 		if (pc == ((unsigned long)&ret_from_irq & ~1)) { | ||||
| 			printk("IRQ\n"); | ||||
| 			unwind_nested(pc,fp); | ||||
| 			unwind_nested(ops, data, pc, fp); | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| @ -263,8 +270,7 @@ static void sh64_unwind_inner(struct pt_regs *regs) | ||||
| 
 | ||||
| 		pc -= ofs; | ||||
| 
 | ||||
| 		printk("[<%08lx>] ", pc); | ||||
| 		print_symbol("%s\n", pc); | ||||
| 		ops->address(data, pc, 1); | ||||
| 
 | ||||
| 		if (first_pass) { | ||||
| 			/* If the innermost frame is a leaf function, it's
 | ||||
| @ -287,10 +293,13 @@ static void sh64_unwind_inner(struct pt_regs *regs) | ||||
| 	} | ||||
| 
 | ||||
| 	printk("\n"); | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| void sh64_unwind(struct pt_regs *regs) | ||||
| static void sh64_unwinder_dump(struct task_struct *task, | ||||
| 			       struct pt_regs *regs, | ||||
| 			       unsigned long *sp, | ||||
| 			       const struct stacktrace_ops *ops, | ||||
| 			       void *data) | ||||
| { | ||||
| 	if (!regs) { | ||||
| 		/*
 | ||||
| @ -320,7 +329,17 @@ void sh64_unwind(struct pt_regs *regs) | ||||
| 		); | ||||
| 	} | ||||
| 
 | ||||
| 	printk("\nCall Trace:\n"); | ||||
| 	sh64_unwind_inner(regs); | ||||
| 	sh64_unwind_inner(ops, data, regs); | ||||
| } | ||||
| 
 | ||||
| static struct unwinder sh64_unwinder = { | ||||
| 	.name	= "sh64-unwinder", | ||||
| 	.dump	= sh64_unwinder_dump, | ||||
| 	.rating	= 150, | ||||
| }; | ||||
| 
 | ||||
| static int __init sh64_unwinder_init(void) | ||||
| { | ||||
| 	return unwinder_register(&sh64_unwinder); | ||||
| } | ||||
| early_initcall(sh64_unwinder_init); | ||||
|  | ||||
| @ -2,13 +2,48 @@ | ||||
|  *  Copyright (C) 1991, 1992  Linus Torvalds | ||||
|  *  Copyright (C) 2000, 2001, 2002 Andi Kleen, SuSE Labs | ||||
|  *  Copyright (C) 2009  Matt Fleming | ||||
|  *  Copyright (C) 2002 - 2012  Paul Mundt | ||||
|  * | ||||
|  * 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/kallsyms.h> | ||||
| #include <linux/ftrace.h> | ||||
| #include <linux/debug_locks.h> | ||||
| #include <linux/kdebug.h> | ||||
| #include <linux/export.h> | ||||
| #include <linux/uaccess.h> | ||||
| #include <asm/unwinder.h> | ||||
| #include <asm/stacktrace.h> | ||||
| 
 | ||||
| void dump_mem(const char *str, unsigned long bottom, unsigned long top) | ||||
| { | ||||
| 	unsigned long p; | ||||
| 	int i; | ||||
| 
 | ||||
| 	printk("%s(0x%08lx to 0x%08lx)\n", str, bottom, top); | ||||
| 
 | ||||
| 	for (p = bottom & ~31; p < top; ) { | ||||
| 		printk("%04lx: ", p & 0xffff); | ||||
| 
 | ||||
| 		for (i = 0; i < 8; i++, p += 4) { | ||||
| 			unsigned int val; | ||||
| 
 | ||||
| 			if (p < bottom || p >= top) | ||||
| 				printk("         "); | ||||
| 			else { | ||||
| 				if (__get_user(val, (unsigned int __user *)p)) { | ||||
| 					printk("\n"); | ||||
| 					return; | ||||
| 				} | ||||
| 				printk("%08x ", val); | ||||
| 			} | ||||
| 		} | ||||
| 		printk("\n"); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void printk_address(unsigned long address, int reliable) | ||||
| { | ||||
| 	printk(" [<%p>] %s%pS\n", (void *) address, | ||||
| @ -106,3 +141,26 @@ void show_trace(struct task_struct *tsk, unsigned long *sp, | ||||
| 
 | ||||
| 	debug_show_held_locks(tsk); | ||||
| } | ||||
| 
 | ||||
| void show_stack(struct task_struct *tsk, unsigned long *sp) | ||||
| { | ||||
| 	unsigned long stack; | ||||
| 
 | ||||
| 	if (!tsk) | ||||
| 		tsk = current; | ||||
| 	if (tsk == current) | ||||
| 		sp = (unsigned long *)current_stack_pointer; | ||||
| 	else | ||||
| 		sp = (unsigned long *)tsk->thread.sp; | ||||
| 
 | ||||
| 	stack = (unsigned long)sp; | ||||
| 	dump_mem("Stack: ", stack, THREAD_SIZE + | ||||
| 		 (unsigned long)task_stack_page(tsk)); | ||||
| 	show_trace(tsk, sp, NULL); | ||||
| } | ||||
| 
 | ||||
| void dump_stack(void) | ||||
| { | ||||
| 	show_stack(NULL, NULL); | ||||
| } | ||||
| EXPORT_SYMBOL(dump_stack); | ||||
|  | ||||
| @ -231,16 +231,6 @@ void __init init_IRQ(void) | ||||
| 	irq_ctx_init(smp_processor_id()); | ||||
| } | ||||
| 
 | ||||
| #ifdef CONFIG_SPARSE_IRQ | ||||
| int __init arch_probe_nr_irqs(void) | ||||
| { | ||||
| 	/*
 | ||||
| 	 * No pre-allocated IRQs. | ||||
| 	 */ | ||||
| 	return 0; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| #ifdef CONFIG_HOTPLUG_CPU | ||||
| static void route_irq(struct irq_data *data, unsigned int irq, unsigned int cpu) | ||||
| { | ||||
|  | ||||
| @ -6,9 +6,80 @@ | ||||
| #include <linux/sched.h> | ||||
| #include <linux/uaccess.h> | ||||
| #include <linux/hardirq.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/kexec.h> | ||||
| #include <linux/module.h> | ||||
| #include <asm/unwinder.h> | ||||
| #include <asm/traps.h> | ||||
| 
 | ||||
| static DEFINE_SPINLOCK(die_lock); | ||||
| 
 | ||||
| void die(const char *str, struct pt_regs *regs, long err) | ||||
| { | ||||
| 	static int die_counter; | ||||
| 
 | ||||
| 	oops_enter(); | ||||
| 
 | ||||
| 	spin_lock_irq(&die_lock); | ||||
| 	console_verbose(); | ||||
| 	bust_spinlocks(1); | ||||
| 
 | ||||
| 	printk("%s: %04lx [#%d]\n", str, err & 0xffff, ++die_counter); | ||||
| 	print_modules(); | ||||
| 	show_regs(regs); | ||||
| 
 | ||||
| 	printk("Process: %s (pid: %d, stack limit = %p)\n", current->comm, | ||||
| 			task_pid_nr(current), task_stack_page(current) + 1); | ||||
| 
 | ||||
| 	if (!user_mode(regs) || in_interrupt()) | ||||
| 		dump_mem("Stack: ", regs->regs[15], THREAD_SIZE + | ||||
| 			 (unsigned long)task_stack_page(current)); | ||||
| 
 | ||||
| 	notify_die(DIE_OOPS, str, regs, err, 255, SIGSEGV); | ||||
| 
 | ||||
| 	bust_spinlocks(0); | ||||
| 	add_taint(TAINT_DIE); | ||||
| 	spin_unlock_irq(&die_lock); | ||||
| 	oops_exit(); | ||||
| 
 | ||||
| 	if (kexec_should_crash(current)) | ||||
| 		crash_kexec(regs); | ||||
| 
 | ||||
| 	if (in_interrupt()) | ||||
| 		panic("Fatal exception in interrupt"); | ||||
| 
 | ||||
| 	if (panic_on_oops) | ||||
| 		panic("Fatal exception"); | ||||
| 
 | ||||
| 	do_exit(SIGSEGV); | ||||
| } | ||||
| 
 | ||||
| void die_if_kernel(const char *str, struct pt_regs *regs, long err) | ||||
| { | ||||
| 	if (!user_mode(regs)) | ||||
| 		die(str, regs, err); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * try and fix up kernelspace address errors | ||||
|  * - userspace errors just cause EFAULT to be returned, resulting in SEGV | ||||
|  * - kernel/userspace interfaces cause a jump to an appropriate handler | ||||
|  * - other kernel errors are bad | ||||
|  */ | ||||
| void die_if_no_fixup(const char *str, struct pt_regs *regs, long err) | ||||
| { | ||||
| 	if (!user_mode(regs)) { | ||||
| 		const struct exception_table_entry *fixup; | ||||
| 		fixup = search_exception_tables(regs->pc); | ||||
| 		if (fixup) { | ||||
| 			regs->pc = fixup->fixup; | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		die(str, regs, err); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| #ifdef CONFIG_GENERIC_BUG | ||||
| static void handle_BUG(struct pt_regs *regs) | ||||
| { | ||||
|  | ||||
| @ -16,13 +16,11 @@ | ||||
| #include <linux/hardirq.h> | ||||
| #include <linux/init.h> | ||||
| #include <linux/spinlock.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/kallsyms.h> | ||||
| #include <linux/io.h> | ||||
| #include <linux/bug.h> | ||||
| #include <linux/debug_locks.h> | ||||
| #include <linux/kdebug.h> | ||||
| #include <linux/kexec.h> | ||||
| #include <linux/limits.h> | ||||
| #include <linux/sysfs.h> | ||||
| #include <linux/uaccess.h> | ||||
| @ -48,102 +46,6 @@ | ||||
| #define TRAP_ILLEGAL_SLOT_INST	13 | ||||
| #endif | ||||
| 
 | ||||
| static void dump_mem(const char *str, unsigned long bottom, unsigned long top) | ||||
| { | ||||
| 	unsigned long p; | ||||
| 	int i; | ||||
| 
 | ||||
| 	printk("%s(0x%08lx to 0x%08lx)\n", str, bottom, top); | ||||
| 
 | ||||
| 	for (p = bottom & ~31; p < top; ) { | ||||
| 		printk("%04lx: ", p & 0xffff); | ||||
| 
 | ||||
| 		for (i = 0; i < 8; i++, p += 4) { | ||||
| 			unsigned int val; | ||||
| 
 | ||||
| 			if (p < bottom || p >= top) | ||||
| 				printk("         "); | ||||
| 			else { | ||||
| 				if (__get_user(val, (unsigned int __user *)p)) { | ||||
| 					printk("\n"); | ||||
| 					return; | ||||
| 				} | ||||
| 				printk("%08x ", val); | ||||
| 			} | ||||
| 		} | ||||
| 		printk("\n"); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static DEFINE_SPINLOCK(die_lock); | ||||
| 
 | ||||
| void die(const char * str, struct pt_regs * regs, long err) | ||||
| { | ||||
| 	static int die_counter; | ||||
| 
 | ||||
| 	oops_enter(); | ||||
| 
 | ||||
| 	spin_lock_irq(&die_lock); | ||||
| 	console_verbose(); | ||||
| 	bust_spinlocks(1); | ||||
| 
 | ||||
| 	printk("%s: %04lx [#%d]\n", str, err & 0xffff, ++die_counter); | ||||
| 	print_modules(); | ||||
| 	show_regs(regs); | ||||
| 
 | ||||
| 	printk("Process: %s (pid: %d, stack limit = %p)\n", current->comm, | ||||
| 			task_pid_nr(current), task_stack_page(current) + 1); | ||||
| 
 | ||||
| 	if (!user_mode(regs) || in_interrupt()) | ||||
| 		dump_mem("Stack: ", regs->regs[15], THREAD_SIZE + | ||||
| 			 (unsigned long)task_stack_page(current)); | ||||
| 
 | ||||
| 	notify_die(DIE_OOPS, str, regs, err, 255, SIGSEGV); | ||||
| 
 | ||||
| 	bust_spinlocks(0); | ||||
| 	add_taint(TAINT_DIE); | ||||
| 	spin_unlock_irq(&die_lock); | ||||
| 	oops_exit(); | ||||
| 
 | ||||
| 	if (kexec_should_crash(current)) | ||||
| 		crash_kexec(regs); | ||||
| 
 | ||||
| 	if (in_interrupt()) | ||||
| 		panic("Fatal exception in interrupt"); | ||||
| 
 | ||||
| 	if (panic_on_oops) | ||||
| 		panic("Fatal exception"); | ||||
| 
 | ||||
| 	do_exit(SIGSEGV); | ||||
| } | ||||
| 
 | ||||
| static inline void die_if_kernel(const char *str, struct pt_regs *regs, | ||||
| 				 long err) | ||||
| { | ||||
| 	if (!user_mode(regs)) | ||||
| 		die(str, regs, err); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * try and fix up kernelspace address errors | ||||
|  * - userspace errors just cause EFAULT to be returned, resulting in SEGV | ||||
|  * - kernel/userspace interfaces cause a jump to an appropriate handler | ||||
|  * - other kernel errors are bad | ||||
|  */ | ||||
| static void die_if_no_fixup(const char * str, struct pt_regs * regs, long err) | ||||
| { | ||||
| 	if (!user_mode(regs)) { | ||||
| 		const struct exception_table_entry *fixup; | ||||
| 		fixup = search_exception_tables(regs->pc); | ||||
| 		if (fixup) { | ||||
| 			regs->pc = fixup->fixup; | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		die(str, regs, err); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline void sign_extend(unsigned int count, unsigned char *dst) | ||||
| { | ||||
| #ifdef __LITTLE_ENDIAN__ | ||||
| @ -900,26 +802,3 @@ void __init trap_init(void) | ||||
| 	set_exception_table_vec(TRAP_UBC, breakpoint_trap_handler); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| void show_stack(struct task_struct *tsk, unsigned long *sp) | ||||
| { | ||||
| 	unsigned long stack; | ||||
| 
 | ||||
| 	if (!tsk) | ||||
| 		tsk = current; | ||||
| 	if (tsk == current) | ||||
| 		sp = (unsigned long *)current_stack_pointer; | ||||
| 	else | ||||
| 		sp = (unsigned long *)tsk->thread.sp; | ||||
| 
 | ||||
| 	stack = (unsigned long)sp; | ||||
| 	dump_mem("Stack: ", stack, THREAD_SIZE + | ||||
| 		 (unsigned long)task_stack_page(tsk)); | ||||
| 	show_trace(tsk, sp, NULL); | ||||
| } | ||||
| 
 | ||||
| void dump_stack(void) | ||||
| { | ||||
| 	show_stack(NULL, NULL); | ||||
| } | ||||
| EXPORT_SYMBOL(dump_stack); | ||||
|  | ||||
| @ -27,283 +27,25 @@ | ||||
| #include <linux/perf_event.h> | ||||
| #include <asm/uaccess.h> | ||||
| #include <asm/io.h> | ||||
| #include <linux/atomic.h> | ||||
| #include <asm/alignment.h> | ||||
| #include <asm/processor.h> | ||||
| #include <asm/pgtable.h> | ||||
| #include <asm/fpu.h> | ||||
| 
 | ||||
| #undef DEBUG_EXCEPTION | ||||
| #ifdef DEBUG_EXCEPTION | ||||
| /* implemented in ../lib/dbg.c */ | ||||
| extern void show_excp_regs(char *fname, int trapnr, int signr, | ||||
| 			   struct pt_regs *regs); | ||||
| #else | ||||
| #define show_excp_regs(a, b, c, d) | ||||
| #endif | ||||
| 
 | ||||
| static void do_unhandled_exception(int trapnr, int signr, char *str, char *fn_name, | ||||
| 		unsigned long error_code, struct pt_regs *regs, struct task_struct *tsk); | ||||
| 
 | ||||
| #define DO_ERROR(trapnr, signr, str, name, tsk) \ | ||||
| asmlinkage void do_##name(unsigned long error_code, struct pt_regs *regs) \ | ||||
| { \ | ||||
| 	do_unhandled_exception(trapnr, signr, str, __stringify(name), error_code, regs, current); \ | ||||
| } | ||||
| 
 | ||||
| static DEFINE_SPINLOCK(die_lock); | ||||
| 
 | ||||
| void die(const char * str, struct pt_regs * regs, long err) | ||||
| { | ||||
| 	console_verbose(); | ||||
| 	spin_lock_irq(&die_lock); | ||||
| 	printk("%s: %lx\n", str, (err & 0xffffff)); | ||||
| 	show_regs(regs); | ||||
| 	spin_unlock_irq(&die_lock); | ||||
| 	do_exit(SIGSEGV); | ||||
| } | ||||
| 
 | ||||
| static inline void die_if_kernel(const char * str, struct pt_regs * regs, long err) | ||||
| { | ||||
| 	if (!user_mode(regs)) | ||||
| 		die(str, regs, err); | ||||
| } | ||||
| 
 | ||||
| static void die_if_no_fixup(const char * str, struct pt_regs * regs, long err) | ||||
| { | ||||
| 	if (!user_mode(regs)) { | ||||
| 		const struct exception_table_entry *fixup; | ||||
| 		fixup = search_exception_tables(regs->pc); | ||||
| 		if (fixup) { | ||||
| 			regs->pc = fixup->fixup; | ||||
| 			return; | ||||
| 		} | ||||
| 		die(str, regs, err); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| DO_ERROR(13, SIGILL,  "illegal slot instruction", illegal_slot_inst, current) | ||||
| DO_ERROR(87, SIGSEGV, "address error (exec)", address_error_exec, current) | ||||
| 
 | ||||
| 
 | ||||
| /* Implement misaligned load/store handling for kernel (and optionally for user
 | ||||
|    mode too).  Limitation : only SHmedia mode code is handled - there is no | ||||
|    handling at all for misaligned accesses occurring in SHcompact code yet. */ | ||||
| 
 | ||||
| static int misaligned_fixup(struct pt_regs *regs); | ||||
| 
 | ||||
| asmlinkage void do_address_error_load(unsigned long error_code, struct pt_regs *regs) | ||||
| { | ||||
| 	if (misaligned_fixup(regs) < 0) { | ||||
| 		do_unhandled_exception(7, SIGSEGV, "address error(load)", | ||||
| 				"do_address_error_load", | ||||
| 				error_code, regs, current); | ||||
| 	} | ||||
| 	return; | ||||
| } | ||||
| 
 | ||||
| asmlinkage void do_address_error_store(unsigned long error_code, struct pt_regs *regs) | ||||
| { | ||||
| 	if (misaligned_fixup(regs) < 0) { | ||||
| 		do_unhandled_exception(8, SIGSEGV, "address error(store)", | ||||
| 				"do_address_error_store", | ||||
| 				error_code, regs, current); | ||||
| 	} | ||||
| 	return; | ||||
| } | ||||
| 
 | ||||
| #if defined(CONFIG_SH64_ID2815_WORKAROUND) | ||||
| 
 | ||||
| #define OPCODE_INVALID      0 | ||||
| #define OPCODE_USER_VALID   1 | ||||
| #define OPCODE_PRIV_VALID   2 | ||||
| 
 | ||||
| /* getcon/putcon - requires checking which control register is referenced. */ | ||||
| #define OPCODE_CTRL_REG     3 | ||||
| 
 | ||||
| /* Table of valid opcodes for SHmedia mode.
 | ||||
|    Form a 10-bit value by concatenating the major/minor opcodes i.e. | ||||
|    opcode[31:26,20:16].  The 6 MSBs of this value index into the following | ||||
|    array.  The 4 LSBs select the bit-pair in the entry (bits 1:0 correspond to | ||||
|    LSBs==4'b0000 etc). */ | ||||
| static unsigned long shmedia_opcode_table[64] = { | ||||
| 	0x55554044,0x54445055,0x15141514,0x14541414,0x00000000,0x10001000,0x01110055,0x04050015, | ||||
| 	0x00000444,0xc0000000,0x44545515,0x40405555,0x55550015,0x10005555,0x55555505,0x04050000, | ||||
| 	0x00000555,0x00000404,0x00040445,0x15151414,0x00000000,0x00000000,0x00000000,0x00000000, | ||||
| 	0x00000055,0x40404444,0x00000404,0xc0009495,0x00000000,0x00000000,0x00000000,0x00000000, | ||||
| 	0x55555555,0x55555555,0x55555555,0x55555555,0x55555555,0x55555555,0x55555555,0x55555555, | ||||
| 	0x55555555,0x55555555,0x55555555,0x55555555,0x55555555,0x55555555,0x55555555,0x55555555, | ||||
| 	0x80005050,0x04005055,0x55555555,0x55555555,0x55555555,0x55555555,0x55555555,0x55555555, | ||||
| 	0x81055554,0x00000404,0x55555555,0x55555555,0x00000000,0x00000000,0x00000000,0x00000000 | ||||
| }; | ||||
| 
 | ||||
| void do_reserved_inst(unsigned long error_code, struct pt_regs *regs) | ||||
| { | ||||
| 	/* Workaround SH5-101 cut2 silicon defect #2815 :
 | ||||
| 	   in some situations, inter-mode branches from SHcompact -> SHmedia | ||||
| 	   which should take ITLBMISS or EXECPROT exceptions at the target | ||||
| 	   falsely take RESINST at the target instead. */ | ||||
| 
 | ||||
| 	unsigned long opcode = 0x6ff4fff0; /* guaranteed reserved opcode */ | ||||
| 	unsigned long pc, aligned_pc; | ||||
| 	int get_user_error; | ||||
| 	int trapnr = 12; | ||||
| 	int signr = SIGILL; | ||||
| 	char *exception_name = "reserved_instruction"; | ||||
| 
 | ||||
| 	pc = regs->pc; | ||||
| 	if ((pc & 3) == 1) { | ||||
| 		/* SHmedia : check for defect.  This requires executable vmas
 | ||||
| 		   to be readable too. */ | ||||
| 		aligned_pc = pc & ~3; | ||||
| 		if (!access_ok(VERIFY_READ, aligned_pc, sizeof(unsigned long))) { | ||||
| 			get_user_error = -EFAULT; | ||||
| 		} else { | ||||
| 			get_user_error = __get_user(opcode, (unsigned long *)aligned_pc); | ||||
| 		} | ||||
| 		if (get_user_error >= 0) { | ||||
| 			unsigned long index, shift; | ||||
| 			unsigned long major, minor, combined; | ||||
| 			unsigned long reserved_field; | ||||
| 			reserved_field = opcode & 0xf; /* These bits are currently reserved as zero in all valid opcodes */ | ||||
| 			major = (opcode >> 26) & 0x3f; | ||||
| 			minor = (opcode >> 16) & 0xf; | ||||
| 			combined = (major << 4) | minor; | ||||
| 			index = major; | ||||
| 			shift = minor << 1; | ||||
| 			if (reserved_field == 0) { | ||||
| 				int opcode_state = (shmedia_opcode_table[index] >> shift) & 0x3; | ||||
| 				switch (opcode_state) { | ||||
| 					case OPCODE_INVALID: | ||||
| 						/* Trap. */ | ||||
| 						break; | ||||
| 					case OPCODE_USER_VALID: | ||||
| 						/* Restart the instruction : the branch to the instruction will now be from an RTE
 | ||||
| 						   not from SHcompact so the silicon defect won't be triggered. */ | ||||
| 						return; | ||||
| 					case OPCODE_PRIV_VALID: | ||||
| 						if (!user_mode(regs)) { | ||||
| 							/* Should only ever get here if a module has
 | ||||
| 							   SHcompact code inside it.  If so, the same fix up is needed. */ | ||||
| 							return; /* same reason */ | ||||
| 						} | ||||
| 						/* Otherwise, user mode trying to execute a privileged instruction -
 | ||||
| 						   fall through to trap. */ | ||||
| 						break; | ||||
| 					case OPCODE_CTRL_REG: | ||||
| 						/* If in privileged mode, return as above. */ | ||||
| 						if (!user_mode(regs)) return; | ||||
| 						/* In user mode ... */ | ||||
| 						if (combined == 0x9f) { /* GETCON */ | ||||
| 							unsigned long regno = (opcode >> 20) & 0x3f; | ||||
| 							if (regno >= 62) { | ||||
| 								return; | ||||
| 							} | ||||
| 							/* Otherwise, reserved or privileged control register, => trap */ | ||||
| 						} else if (combined == 0x1bf) { /* PUTCON */ | ||||
| 							unsigned long regno = (opcode >> 4) & 0x3f; | ||||
| 							if (regno >= 62) { | ||||
| 								return; | ||||
| 							} | ||||
| 							/* Otherwise, reserved or privileged control register, => trap */ | ||||
| 						} else { | ||||
| 							/* Trap */ | ||||
| 						} | ||||
| 						break; | ||||
| 					default: | ||||
| 						/* Fall through to trap. */ | ||||
| 						break; | ||||
| 				} | ||||
| 			} | ||||
| 			/* fall through to normal resinst processing */ | ||||
| 		} else { | ||||
| 			/* Error trying to read opcode.  This typically means a
 | ||||
| 			   real fault, not a RESINST any more.  So change the | ||||
| 			   codes. */ | ||||
| 			trapnr = 87; | ||||
| 			exception_name = "address error (exec)"; | ||||
| 			signr = SIGSEGV; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	do_unhandled_exception(trapnr, signr, exception_name, "do_reserved_inst", error_code, regs, current); | ||||
| } | ||||
| 
 | ||||
| #else /* CONFIG_SH64_ID2815_WORKAROUND */ | ||||
| 
 | ||||
| /* If the workaround isn't needed, this is just a straightforward reserved
 | ||||
|    instruction */ | ||||
| DO_ERROR(12, SIGILL,  "reserved instruction", reserved_inst, current) | ||||
| 
 | ||||
| #endif /* CONFIG_SH64_ID2815_WORKAROUND */ | ||||
| 
 | ||||
| /* Called with interrupts disabled */ | ||||
| asmlinkage void do_exception_error(unsigned long ex, struct pt_regs *regs) | ||||
| { | ||||
| 	show_excp_regs(__func__, -1, -1, regs); | ||||
| 	die_if_kernel("exception", regs, ex); | ||||
| } | ||||
| 
 | ||||
| int do_unknown_trapa(unsigned long scId, struct pt_regs *regs) | ||||
| { | ||||
| 	/* Syscall debug */ | ||||
|         printk("System call ID error: [0x1#args:8 #syscall:16  0x%lx]\n", scId); | ||||
| 
 | ||||
| 	die_if_kernel("unknown trapa", regs, scId); | ||||
| 
 | ||||
| 	return -ENOSYS; | ||||
| } | ||||
| 
 | ||||
| void show_stack(struct task_struct *tsk, unsigned long *sp) | ||||
| { | ||||
| #ifdef CONFIG_KALLSYMS | ||||
| 	extern void sh64_unwind(struct pt_regs *regs); | ||||
| 	struct pt_regs *regs; | ||||
| 
 | ||||
| 	regs = tsk ? tsk->thread.kregs : NULL; | ||||
| 
 | ||||
| 	sh64_unwind(regs); | ||||
| #else | ||||
| 	printk(KERN_ERR "Can't backtrace on sh64 without CONFIG_KALLSYMS\n"); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| void show_task(unsigned long *sp) | ||||
| { | ||||
| 	show_stack(NULL, sp); | ||||
| } | ||||
| 
 | ||||
| void dump_stack(void) | ||||
| { | ||||
| 	show_task(NULL); | ||||
| } | ||||
| /* Needed by any user of WARN_ON in view of the defn in include/asm-sh/bug.h */ | ||||
| EXPORT_SYMBOL(dump_stack); | ||||
| 
 | ||||
| static void do_unhandled_exception(int trapnr, int signr, char *str, char *fn_name, | ||||
| 		unsigned long error_code, struct pt_regs *regs, struct task_struct *tsk) | ||||
| { | ||||
| 	show_excp_regs(fn_name, trapnr, signr, regs); | ||||
| 
 | ||||
| 	if (user_mode(regs)) | ||||
| 		force_sig(signr, tsk); | ||||
| 
 | ||||
| 	die_if_no_fixup(str, regs, error_code); | ||||
| } | ||||
| 
 | ||||
| static int read_opcode(unsigned long long pc, unsigned long *result_opcode, int from_user_mode) | ||||
| static int read_opcode(reg_size_t pc, insn_size_t *result_opcode, int from_user_mode) | ||||
| { | ||||
| 	int get_user_error; | ||||
| 	unsigned long aligned_pc; | ||||
| 	unsigned long opcode; | ||||
| 	insn_size_t opcode; | ||||
| 
 | ||||
| 	if ((pc & 3) == 1) { | ||||
| 		/* SHmedia */ | ||||
| 		aligned_pc = pc & ~3; | ||||
| 		if (from_user_mode) { | ||||
| 			if (!access_ok(VERIFY_READ, aligned_pc, sizeof(unsigned long))) { | ||||
| 			if (!access_ok(VERIFY_READ, aligned_pc, sizeof(insn_size_t))) { | ||||
| 				get_user_error = -EFAULT; | ||||
| 			} else { | ||||
| 				get_user_error = __get_user(opcode, (unsigned long *)aligned_pc); | ||||
| 				get_user_error = __get_user(opcode, (insn_size_t *)aligned_pc); | ||||
| 				*result_opcode = opcode; | ||||
| 			} | ||||
| 			return get_user_error; | ||||
| @ -311,7 +53,7 @@ static int read_opcode(unsigned long long pc, unsigned long *result_opcode, int | ||||
| 			/* If the fault was in the kernel, we can either read
 | ||||
| 			 * this directly, or if not, we fault. | ||||
| 			*/ | ||||
| 			*result_opcode = *(unsigned long *) aligned_pc; | ||||
| 			*result_opcode = *(insn_size_t *)aligned_pc; | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} else if ((pc & 1) == 0) { | ||||
| @ -337,17 +79,23 @@ static int address_is_sign_extended(__u64 a) | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| /* return -1 for fault, 0 for OK */ | ||||
| static int generate_and_check_address(struct pt_regs *regs, | ||||
| 				      __u32 opcode, | ||||
| 				      insn_size_t opcode, | ||||
| 				      int displacement_not_indexed, | ||||
| 				      int width_shift, | ||||
| 				      __u64 *address) | ||||
| { | ||||
| 	/* return -1 for fault, 0 for OK */ | ||||
| 
 | ||||
| 	__u64 base_address, addr; | ||||
| 	int basereg; | ||||
| 
 | ||||
| 	switch (1 << width_shift) { | ||||
| 	case 1: inc_unaligned_byte_access(); break; | ||||
| 	case 2: inc_unaligned_word_access(); break; | ||||
| 	case 4: inc_unaligned_dword_access(); break; | ||||
| 	case 8: inc_unaligned_multi_access(); break; | ||||
| 	} | ||||
| 
 | ||||
| 	basereg = (opcode >> 20) & 0x3f; | ||||
| 	base_address = regs->regs[basereg]; | ||||
| 	if (displacement_not_indexed) { | ||||
| @ -364,28 +112,28 @@ static int generate_and_check_address(struct pt_regs *regs, | ||||
| 	} | ||||
| 
 | ||||
| 	/* Check sign extended */ | ||||
| 	if (!address_is_sign_extended(addr)) { | ||||
| 	if (!address_is_sign_extended(addr)) | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Check accessible.  For misaligned access in the kernel, assume the
 | ||||
| 	   address is always accessible (and if not, just fault when the | ||||
| 	   load/store gets done.) */ | ||||
| 	if (user_mode(regs)) { | ||||
| 		if (addr >= TASK_SIZE) { | ||||
| 		inc_unaligned_user_access(); | ||||
| 
 | ||||
| 		if (addr >= TASK_SIZE) | ||||
| 			return -1; | ||||
| 		} | ||||
| 		/* Do access_ok check later - it depends on whether it's a load or a store. */ | ||||
| 	} | ||||
| 	} else | ||||
| 		inc_unaligned_kernel_access(); | ||||
| 
 | ||||
| 	*address = addr; | ||||
| 
 | ||||
| 	perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, addr); | ||||
| 	unaligned_fixups_notify(current, opcode, regs); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int user_mode_unaligned_fixup_count = 10; | ||||
| static int user_mode_unaligned_fixup_enable = 1; | ||||
| static int kernel_mode_unaligned_fixup_count = 32; | ||||
| 
 | ||||
| static void misaligned_kernel_word_load(__u64 address, int do_sign_extend, __u64 *result) | ||||
| { | ||||
| 	unsigned short x; | ||||
| @ -415,7 +163,7 @@ static void misaligned_kernel_word_store(__u64 address, __u64 value) | ||||
| } | ||||
| 
 | ||||
| static int misaligned_load(struct pt_regs *regs, | ||||
| 			   __u32 opcode, | ||||
| 			   insn_size_t opcode, | ||||
| 			   int displacement_not_indexed, | ||||
| 			   int width_shift, | ||||
| 			   int do_sign_extend) | ||||
| @ -427,11 +175,8 @@ static int misaligned_load(struct pt_regs *regs, | ||||
| 
 | ||||
| 	error = generate_and_check_address(regs, opcode, | ||||
| 			displacement_not_indexed, width_shift, &address); | ||||
| 	if (error < 0) { | ||||
| 	if (error < 0) | ||||
| 		return error; | ||||
| 	} | ||||
| 
 | ||||
| 	perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, 1, regs, address); | ||||
| 
 | ||||
| 	destreg = (opcode >> 4) & 0x3f; | ||||
| 	if (user_mode(regs)) { | ||||
| @ -490,11 +235,10 @@ static int misaligned_load(struct pt_regs *regs, | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| static int misaligned_store(struct pt_regs *regs, | ||||
| 			    __u32 opcode, | ||||
| 			    insn_size_t opcode, | ||||
| 			    int displacement_not_indexed, | ||||
| 			    int width_shift) | ||||
| { | ||||
| @ -505,11 +249,8 @@ static int misaligned_store(struct pt_regs *regs, | ||||
| 
 | ||||
| 	error = generate_and_check_address(regs, opcode, | ||||
| 			displacement_not_indexed, width_shift, &address); | ||||
| 	if (error < 0) { | ||||
| 	if (error < 0) | ||||
| 		return error; | ||||
| 	} | ||||
| 
 | ||||
| 	perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, 1, regs, address); | ||||
| 
 | ||||
| 	srcreg = (opcode >> 4) & 0x3f; | ||||
| 	if (user_mode(regs)) { | ||||
| @ -563,13 +304,12 @@ static int misaligned_store(struct pt_regs *regs, | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| /* Never need to fix up misaligned FPU accesses within the kernel since that's a real
 | ||||
|    error. */ | ||||
| static int misaligned_fpu_load(struct pt_regs *regs, | ||||
| 			   __u32 opcode, | ||||
| 			   insn_size_t opcode, | ||||
| 			   int displacement_not_indexed, | ||||
| 			   int width_shift, | ||||
| 			   int do_paired_load) | ||||
| @ -581,11 +321,8 @@ static int misaligned_fpu_load(struct pt_regs *regs, | ||||
| 
 | ||||
| 	error = generate_and_check_address(regs, opcode, | ||||
| 			displacement_not_indexed, width_shift, &address); | ||||
| 	if (error < 0) { | ||||
| 	if (error < 0) | ||||
| 		return error; | ||||
| 	} | ||||
| 
 | ||||
| 	perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, address); | ||||
| 
 | ||||
| 	destreg = (opcode >> 4) & 0x3f; | ||||
| 	if (user_mode(regs)) { | ||||
| @ -641,12 +378,10 @@ static int misaligned_fpu_load(struct pt_regs *regs, | ||||
| 		die ("Misaligned FPU load inside kernel", regs, 0); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| static int misaligned_fpu_store(struct pt_regs *regs, | ||||
| 			   __u32 opcode, | ||||
| 			   insn_size_t opcode, | ||||
| 			   int displacement_not_indexed, | ||||
| 			   int width_shift, | ||||
| 			   int do_paired_load) | ||||
| @ -658,11 +393,8 @@ static int misaligned_fpu_store(struct pt_regs *regs, | ||||
| 
 | ||||
| 	error = generate_and_check_address(regs, opcode, | ||||
| 			displacement_not_indexed, width_shift, &address); | ||||
| 	if (error < 0) { | ||||
| 	if (error < 0) | ||||
| 		return error; | ||||
| 	} | ||||
| 
 | ||||
| 	perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, address); | ||||
| 
 | ||||
| 	srcreg = (opcode >> 4) & 0x3f; | ||||
| 	if (user_mode(regs)) { | ||||
| @ -723,11 +455,13 @@ static int misaligned_fpu_store(struct pt_regs *regs, | ||||
| 
 | ||||
| static int misaligned_fixup(struct pt_regs *regs) | ||||
| { | ||||
| 	unsigned long opcode; | ||||
| 	insn_size_t opcode; | ||||
| 	int error; | ||||
| 	int major, minor; | ||||
| 	unsigned int user_action; | ||||
| 
 | ||||
| 	if (!user_mode_unaligned_fixup_enable) | ||||
| 	user_action = unaligned_user_action(); | ||||
| 	if (!(user_action & UM_FIXUP)) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	error = read_opcode(regs->pc, &opcode, user_mode(regs)); | ||||
| @ -737,23 +471,6 @@ static int misaligned_fixup(struct pt_regs *regs) | ||||
| 	major = (opcode >> 26) & 0x3f; | ||||
| 	minor = (opcode >> 16) & 0xf; | ||||
| 
 | ||||
| 	if (user_mode(regs) && (user_mode_unaligned_fixup_count > 0)) { | ||||
| 		--user_mode_unaligned_fixup_count; | ||||
| 		/* Only do 'count' worth of these reports, to remove a potential DoS against syslog */ | ||||
| 		printk("Fixing up unaligned userspace access in \"%s\" pid=%d pc=0x%08x ins=0x%08lx\n", | ||||
| 		       current->comm, task_pid_nr(current), (__u32)regs->pc, opcode); | ||||
| 	} else if (!user_mode(regs) && (kernel_mode_unaligned_fixup_count > 0)) { | ||||
| 		--kernel_mode_unaligned_fixup_count; | ||||
| 		if (in_interrupt()) { | ||||
| 			printk("Fixing up unaligned kernelspace access in interrupt pc=0x%08x ins=0x%08lx\n", | ||||
| 			       (__u32)regs->pc, opcode); | ||||
| 		} else { | ||||
| 			printk("Fixing up unaligned kernelspace access in \"%s\" pid=%d pc=0x%08x ins=0x%08lx\n", | ||||
| 			       current->comm, task_pid_nr(current), (__u32)regs->pc, opcode); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 
 | ||||
| 	switch (major) { | ||||
| 		case (0x84>>2): /* LD.W */ | ||||
| 			error = misaligned_load(regs, opcode, 1, 1, 1); | ||||
| @ -878,59 +595,202 @@ static int misaligned_fixup(struct pt_regs *regs) | ||||
| 		regs->pc += 4; /* Skip the instruction that's just been emulated */ | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| static ctl_table unaligned_table[] = { | ||||
| 	{ | ||||
| 		.procname	= "kernel_reports", | ||||
| 		.data		= &kernel_mode_unaligned_fixup_count, | ||||
| 		.maxlen		= sizeof(int), | ||||
| 		.mode		= 0644, | ||||
| 		.proc_handler	= proc_dointvec | ||||
| 	}, | ||||
| 	{ | ||||
| 		.procname	= "user_reports", | ||||
| 		.data		= &user_mode_unaligned_fixup_count, | ||||
| 		.maxlen		= sizeof(int), | ||||
| 		.mode		= 0644, | ||||
| 		.proc_handler	= proc_dointvec | ||||
| 	}, | ||||
| 	{ | ||||
| 		.procname	= "user_enable", | ||||
| 		.data		= &user_mode_unaligned_fixup_enable, | ||||
| 		.maxlen		= sizeof(int), | ||||
| 		.mode		= 0644, | ||||
| 		.proc_handler	= proc_dointvec}, | ||||
| 	{} | ||||
| }; | ||||
| 
 | ||||
| static ctl_table unaligned_root[] = { | ||||
| 	{ | ||||
| 		.procname	= "unaligned_fixup", | ||||
| 		.mode		= 0555, | ||||
| 		.child		= unaligned_table | ||||
| 	}, | ||||
| 	{} | ||||
| }; | ||||
| 
 | ||||
| static ctl_table sh64_root[] = { | ||||
| 	{ | ||||
| 		.procname	= "sh64", | ||||
| 		.mode		= 0555, | ||||
| 		.child		= unaligned_root | ||||
| 	}, | ||||
| 	{} | ||||
| }; | ||||
| static struct ctl_table_header *sysctl_header; | ||||
| static int __init init_sysctl(void) | ||||
| static void do_unhandled_exception(int signr, char *str, unsigned long error, | ||||
| 				   struct pt_regs *regs) | ||||
| { | ||||
| 	sysctl_header = register_sysctl_table(sh64_root); | ||||
| 	return 0; | ||||
| 	if (user_mode(regs)) | ||||
| 		force_sig(signr, current); | ||||
| 
 | ||||
| 	die_if_no_fixup(str, regs, error); | ||||
| } | ||||
| 
 | ||||
| __initcall(init_sysctl); | ||||
| #define DO_ERROR(signr, str, name) \ | ||||
| asmlinkage void do_##name(unsigned long error_code, struct pt_regs *regs) \ | ||||
| { \ | ||||
| 	do_unhandled_exception(signr, str, error_code, regs); \ | ||||
| } | ||||
| 
 | ||||
| DO_ERROR(SIGILL,  "illegal slot instruction", illegal_slot_inst) | ||||
| DO_ERROR(SIGSEGV, "address error (exec)", address_error_exec) | ||||
| 
 | ||||
| #if defined(CONFIG_SH64_ID2815_WORKAROUND) | ||||
| 
 | ||||
| #define OPCODE_INVALID      0 | ||||
| #define OPCODE_USER_VALID   1 | ||||
| #define OPCODE_PRIV_VALID   2 | ||||
| 
 | ||||
| /* getcon/putcon - requires checking which control register is referenced. */ | ||||
| #define OPCODE_CTRL_REG     3 | ||||
| 
 | ||||
| /* Table of valid opcodes for SHmedia mode.
 | ||||
|    Form a 10-bit value by concatenating the major/minor opcodes i.e. | ||||
|    opcode[31:26,20:16].  The 6 MSBs of this value index into the following | ||||
|    array.  The 4 LSBs select the bit-pair in the entry (bits 1:0 correspond to | ||||
|    LSBs==4'b0000 etc). */ | ||||
| static unsigned long shmedia_opcode_table[64] = { | ||||
| 	0x55554044,0x54445055,0x15141514,0x14541414,0x00000000,0x10001000,0x01110055,0x04050015, | ||||
| 	0x00000444,0xc0000000,0x44545515,0x40405555,0x55550015,0x10005555,0x55555505,0x04050000, | ||||
| 	0x00000555,0x00000404,0x00040445,0x15151414,0x00000000,0x00000000,0x00000000,0x00000000, | ||||
| 	0x00000055,0x40404444,0x00000404,0xc0009495,0x00000000,0x00000000,0x00000000,0x00000000, | ||||
| 	0x55555555,0x55555555,0x55555555,0x55555555,0x55555555,0x55555555,0x55555555,0x55555555, | ||||
| 	0x55555555,0x55555555,0x55555555,0x55555555,0x55555555,0x55555555,0x55555555,0x55555555, | ||||
| 	0x80005050,0x04005055,0x55555555,0x55555555,0x55555555,0x55555555,0x55555555,0x55555555, | ||||
| 	0x81055554,0x00000404,0x55555555,0x55555555,0x00000000,0x00000000,0x00000000,0x00000000 | ||||
| }; | ||||
| 
 | ||||
| /* Workaround SH5-101 cut2 silicon defect #2815 :
 | ||||
|    in some situations, inter-mode branches from SHcompact -> SHmedia | ||||
|    which should take ITLBMISS or EXECPROT exceptions at the target | ||||
|    falsely take RESINST at the target instead. */ | ||||
| void do_reserved_inst(unsigned long error_code, struct pt_regs *regs) | ||||
| { | ||||
| 	insn_size_t opcode = 0x6ff4fff0; /* guaranteed reserved opcode */ | ||||
| 	unsigned long pc, aligned_pc; | ||||
| 	unsigned long index, shift; | ||||
| 	unsigned long major, minor, combined; | ||||
| 	unsigned long reserved_field; | ||||
| 	int opcode_state; | ||||
| 	int get_user_error; | ||||
| 	int signr = SIGILL; | ||||
| 	char *exception_name = "reserved_instruction"; | ||||
| 
 | ||||
| 	pc = regs->pc; | ||||
| 
 | ||||
| 	/* SHcompact is not handled */ | ||||
| 	if (unlikely((pc & 3) == 0)) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	/* SHmedia : check for defect.  This requires executable vmas
 | ||||
| 	   to be readable too. */ | ||||
| 	aligned_pc = pc & ~3; | ||||
| 	if (!access_ok(VERIFY_READ, aligned_pc, sizeof(insn_size_t))) | ||||
| 		get_user_error = -EFAULT; | ||||
| 	else | ||||
| 		get_user_error = __get_user(opcode, (insn_size_t *)aligned_pc); | ||||
| 
 | ||||
| 	if (get_user_error < 0) { | ||||
| 		/*
 | ||||
| 		 * Error trying to read opcode.  This typically means a | ||||
| 		 * real fault, not a RESINST any more.  So change the | ||||
| 		 * codes. | ||||
| 		 */ | ||||
| 		exception_name = "address error (exec)"; | ||||
| 		signr = SIGSEGV; | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	/* These bits are currently reserved as zero in all valid opcodes */ | ||||
| 	reserved_field = opcode & 0xf; | ||||
| 	if (unlikely(reserved_field)) | ||||
| 		goto out;	/* invalid opcode */ | ||||
| 
 | ||||
| 	major = (opcode >> 26) & 0x3f; | ||||
| 	minor = (opcode >> 16) & 0xf; | ||||
| 	combined = (major << 4) | minor; | ||||
| 	index = major; | ||||
| 	shift = minor << 1; | ||||
| 	opcode_state = (shmedia_opcode_table[index] >> shift) & 0x3; | ||||
| 	switch (opcode_state) { | ||||
| 	case OPCODE_INVALID: | ||||
| 		/* Trap. */ | ||||
| 		break; | ||||
| 	case OPCODE_USER_VALID: | ||||
| 		/*
 | ||||
| 		 * Restart the instruction: the branch to the instruction | ||||
| 		 * will now be from an RTE not from SHcompact so the | ||||
| 		 * silicon defect won't be triggered. | ||||
| 		 */ | ||||
| 		return; | ||||
| 	case OPCODE_PRIV_VALID: | ||||
| 		if (!user_mode(regs)) { | ||||
| 			/*
 | ||||
| 			 * Should only ever get here if a module has | ||||
| 			 * SHcompact code inside it. If so, the same fix | ||||
| 			 * up is needed. | ||||
| 			 */ | ||||
| 			return; /* same reason */ | ||||
| 		} | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * Otherwise, user mode trying to execute a privileged | ||||
| 		 * instruction - fall through to trap. | ||||
| 		 */ | ||||
| 		break; | ||||
| 	case OPCODE_CTRL_REG: | ||||
| 		/* If in privileged mode, return as above. */ | ||||
| 		if (!user_mode(regs)) | ||||
| 			return; | ||||
| 
 | ||||
| 		/* In user mode ... */ | ||||
| 		if (combined == 0x9f) { /* GETCON */ | ||||
| 			unsigned long regno = (opcode >> 20) & 0x3f; | ||||
| 
 | ||||
| 			if (regno >= 62) | ||||
| 				return; | ||||
| 
 | ||||
| 			/* reserved/privileged control register => trap */ | ||||
| 		} else if (combined == 0x1bf) { /* PUTCON */ | ||||
| 			unsigned long regno = (opcode >> 4) & 0x3f; | ||||
| 
 | ||||
| 			if (regno >= 62) | ||||
| 				return; | ||||
| 
 | ||||
| 			/* reserved/privileged control register => trap */ | ||||
| 		} | ||||
| 
 | ||||
| 		break; | ||||
| 	default: | ||||
| 		/* Fall through to trap. */ | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| out: | ||||
| 	do_unhandled_exception(signr, exception_name, error_code, regs); | ||||
| } | ||||
| 
 | ||||
| #else /* CONFIG_SH64_ID2815_WORKAROUND */ | ||||
| 
 | ||||
| /* If the workaround isn't needed, this is just a straightforward reserved
 | ||||
|    instruction */ | ||||
| DO_ERROR(SIGILL, "reserved instruction", reserved_inst) | ||||
| 
 | ||||
| #endif /* CONFIG_SH64_ID2815_WORKAROUND */ | ||||
| 
 | ||||
| /* Called with interrupts disabled */ | ||||
| asmlinkage void do_exception_error(unsigned long ex, struct pt_regs *regs) | ||||
| { | ||||
| 	die_if_kernel("exception", regs, ex); | ||||
| } | ||||
| 
 | ||||
| asmlinkage int do_unknown_trapa(unsigned long scId, struct pt_regs *regs) | ||||
| { | ||||
| 	/* Syscall debug */ | ||||
| 	printk("System call ID error: [0x1#args:8 #syscall:16  0x%lx]\n", scId); | ||||
| 
 | ||||
| 	die_if_kernel("unknown trapa", regs, scId); | ||||
| 
 | ||||
| 	return -ENOSYS; | ||||
| } | ||||
| 
 | ||||
| /* Implement misaligned load/store handling for kernel (and optionally for user
 | ||||
|    mode too).  Limitation : only SHmedia mode code is handled - there is no | ||||
|    handling at all for misaligned accesses occurring in SHcompact code yet. */ | ||||
| 
 | ||||
| asmlinkage void do_address_error_load(unsigned long error_code, struct pt_regs *regs) | ||||
| { | ||||
| 	if (misaligned_fixup(regs) < 0) | ||||
| 		do_unhandled_exception(SIGSEGV, "address error(load)", | ||||
| 				       error_code, regs); | ||||
| } | ||||
| 
 | ||||
| asmlinkage void do_address_error_store(unsigned long error_code, struct pt_regs *regs) | ||||
| { | ||||
| 	if (misaligned_fixup(regs) < 0) | ||||
| 		do_unhandled_exception(SIGSEGV, "address error(store)", | ||||
| 				error_code, regs); | ||||
| } | ||||
| 
 | ||||
| asmlinkage void do_debug_interrupt(unsigned long code, struct pt_regs *regs) | ||||
| { | ||||
| @ -942,10 +802,9 @@ asmlinkage void do_debug_interrupt(unsigned long code, struct pt_regs *regs) | ||||
| 	   of access we make to them - just go direct to their physical | ||||
| 	   addresses. */ | ||||
| 	exp_cause = peek_real_address_q(DM_EXP_CAUSE_PHY); | ||||
| 	if (exp_cause & ~4) { | ||||
| 	if (exp_cause & ~4) | ||||
| 		printk("DM.EXP_CAUSE had unexpected bits set (=%08lx)\n", | ||||
| 			(unsigned long)(exp_cause & 0xffffffff)); | ||||
| 	} | ||||
| 	show_state(); | ||||
| 	/* Clear all DEBUGINT causes */ | ||||
| 	poke_real_address_q(DM_EXP_CAUSE_PHY, 0x0); | ||||
|  | ||||
| @ -10,7 +10,7 @@ | ||||
| #
 | ||||
| 
 | ||||
| # Panic should really be compiled as PIC
 | ||||
| lib-y  := udelay.o dbg.o panic.o memcpy.o memset.o \
 | ||||
| lib-y  := udelay.o panic.o memcpy.o memset.o \
 | ||||
| 	  copy_user_memcpy.o copy_page.o strcpy.o strlen.o | ||||
| 
 | ||||
| # Extracted from libgcc
 | ||||
|  | ||||
| @ -1,248 +0,0 @@ | ||||
| /*--------------------------------------------------------------------------
 | ||||
| -- | ||||
| -- Identity : Linux50 Debug Funcions | ||||
| -- | ||||
| -- File     : arch/sh/lib64/dbg.c | ||||
| -- | ||||
| -- Copyright 2000, 2001 STMicroelectronics Limited. | ||||
| -- Copyright 2004 Richard Curnow (evt_debug etc) | ||||
| -- | ||||
| --------------------------------------------------------------------------*/ | ||||
| #include <linux/types.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/sched.h> | ||||
| #include <linux/mm.h> | ||||
| #include <linux/fs.h> | ||||
| #include <asm/mmu_context.h> | ||||
| 
 | ||||
| typedef u64 regType_t; | ||||
| 
 | ||||
| static regType_t getConfigReg(u64 id) | ||||
| { | ||||
| 	register u64 reg __asm__("r2"); | ||||
| 	asm volatile ("getcfg   %1, 0, %0":"=r" (reg):"r"(id)); | ||||
| 	return (reg); | ||||
| } | ||||
| 
 | ||||
| /* ======================================================================= */ | ||||
| 
 | ||||
| static char *szTab[] = { "4k", "64k", "1M", "512M" }; | ||||
| static char *protTab[] = { "----", | ||||
| 	"---R", | ||||
| 	"--X-", | ||||
| 	"--XR", | ||||
| 	"-W--", | ||||
| 	"-W-R", | ||||
| 	"-WX-", | ||||
| 	"-WXR", | ||||
| 	"U---", | ||||
| 	"U--R", | ||||
| 	"U-X-", | ||||
| 	"U-XR", | ||||
| 	"UW--", | ||||
| 	"UW-R", | ||||
| 	"UWX-", | ||||
| 	"UWXR" | ||||
| }; | ||||
| #define  ITLB_BASE	0x00000000 | ||||
| #define  DTLB_BASE	0x00800000 | ||||
| #define  MAX_TLBs		64 | ||||
| /* PTE High */ | ||||
| #define  GET_VALID(pte)        ((pte) & 0x1) | ||||
| #define  GET_SHARED(pte)       ((pte) & 0x2) | ||||
| #define  GET_ASID(pte)         ((pte >> 2) & 0x0ff) | ||||
| #define  GET_EPN(pte)          ((pte) & 0xfffff000) | ||||
| 
 | ||||
| /* PTE Low */ | ||||
| #define  GET_CBEHAVIOR(pte)    ((pte) & 0x3) | ||||
| #define  GET_PAGE_SIZE(pte)    szTab[((pte >> 3) & 0x3)] | ||||
| #define  GET_PROTECTION(pte)   protTab[((pte >> 6) & 0xf)] | ||||
| #define  GET_PPN(pte)          ((pte) & 0xfffff000) | ||||
| 
 | ||||
| #define PAGE_1K_MASK           0x00000000 | ||||
| #define PAGE_4K_MASK           0x00000010 | ||||
| #define PAGE_64K_MASK          0x00000080 | ||||
| #define MMU_PAGESIZE_MASK      (PAGE_64K_MASK | PAGE_4K_MASK) | ||||
| #define PAGE_1MB_MASK          MMU_PAGESIZE_MASK | ||||
| #define PAGE_1K                (1024) | ||||
| #define PAGE_4K                (1024 * 4) | ||||
| #define PAGE_64K               (1024 * 64) | ||||
| #define PAGE_1MB               (1024 * 1024) | ||||
| 
 | ||||
| #define HOW_TO_READ_TLB_CONTENT  \ | ||||
|        "[ ID]  PPN         EPN        ASID  Share  CB  P.Size   PROT.\n" | ||||
| 
 | ||||
| void print_single_tlb(unsigned long tlb, int single_print) | ||||
| { | ||||
| 	regType_t pteH; | ||||
| 	regType_t pteL; | ||||
| 	unsigned int valid, shared, asid, epn, cb, ppn; | ||||
| 	char *pSize; | ||||
| 	char *pProt; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	   ** in case of single print <single_print> is true, this implies: | ||||
| 	   **   1) print the TLB in any case also if NOT VALID | ||||
| 	   **   2) print out the header | ||||
| 	 */ | ||||
| 
 | ||||
| 	pteH = getConfigReg(tlb); | ||||
| 	valid = GET_VALID(pteH); | ||||
| 	if (single_print) | ||||
| 		printk(HOW_TO_READ_TLB_CONTENT); | ||||
| 	else if (!valid) | ||||
| 		return; | ||||
| 
 | ||||
| 	pteL = getConfigReg(tlb + 1); | ||||
| 
 | ||||
| 	shared = GET_SHARED(pteH); | ||||
| 	asid = GET_ASID(pteH); | ||||
| 	epn = GET_EPN(pteH); | ||||
| 	cb = GET_CBEHAVIOR(pteL); | ||||
| 	pSize = GET_PAGE_SIZE(pteL); | ||||
| 	pProt = GET_PROTECTION(pteL); | ||||
| 	ppn = GET_PPN(pteL); | ||||
| 	printk("[%c%2ld]  0x%08x  0x%08x  %03d   %02x    %02x   %4s    %s\n", | ||||
| 	       ((valid) ? ' ' : 'u'), ((tlb & 0x0ffff) / TLB_STEP), | ||||
| 	       ppn, epn, asid, shared, cb, pSize, pProt); | ||||
| } | ||||
| 
 | ||||
| void print_dtlb(void) | ||||
| { | ||||
| 	int count; | ||||
| 	unsigned long tlb; | ||||
| 
 | ||||
| 	printk(" ================= SH-5 D-TLBs Status ===================\n"); | ||||
| 	printk(HOW_TO_READ_TLB_CONTENT); | ||||
| 	tlb = DTLB_BASE; | ||||
| 	for (count = 0; count < MAX_TLBs; count++, tlb += TLB_STEP) | ||||
| 		print_single_tlb(tlb, 0); | ||||
| 	printk | ||||
| 	    (" =============================================================\n"); | ||||
| } | ||||
| 
 | ||||
| void print_itlb(void) | ||||
| { | ||||
| 	int count; | ||||
| 	unsigned long tlb; | ||||
| 
 | ||||
| 	printk(" ================= SH-5 I-TLBs Status ===================\n"); | ||||
| 	printk(HOW_TO_READ_TLB_CONTENT); | ||||
| 	tlb = ITLB_BASE; | ||||
| 	for (count = 0; count < MAX_TLBs; count++, tlb += TLB_STEP) | ||||
| 		print_single_tlb(tlb, 0); | ||||
| 	printk | ||||
| 	    (" =============================================================\n"); | ||||
| } | ||||
| 
 | ||||
| void show_excp_regs(char *from, int trapnr, int signr, struct pt_regs *regs) | ||||
| { | ||||
| 
 | ||||
| 	unsigned long long ah, al, bh, bl, ch, cl; | ||||
| 
 | ||||
| 	printk("\n"); | ||||
| 	printk("EXCEPTION - %s: task %d; Linux trap # %d; signal = %d\n", | ||||
| 	       ((from) ? from : "???"), current->pid, trapnr, signr); | ||||
| 
 | ||||
| 	asm volatile ("getcon   " __EXPEVT ", %0":"=r"(ah)); | ||||
| 	asm volatile ("getcon   " __EXPEVT ", %0":"=r"(al)); | ||||
| 	ah = (ah) >> 32; | ||||
| 	al = (al) & 0xffffffff; | ||||
| 	asm volatile ("getcon   " __KCR1 ", %0":"=r"(bh)); | ||||
| 	asm volatile ("getcon   " __KCR1 ", %0":"=r"(bl)); | ||||
| 	bh = (bh) >> 32; | ||||
| 	bl = (bl) & 0xffffffff; | ||||
| 	asm volatile ("getcon   " __INTEVT ", %0":"=r"(ch)); | ||||
| 	asm volatile ("getcon   " __INTEVT ", %0":"=r"(cl)); | ||||
| 	ch = (ch) >> 32; | ||||
| 	cl = (cl) & 0xffffffff; | ||||
| 	printk("EXPE: %08Lx%08Lx KCR1: %08Lx%08Lx INTE: %08Lx%08Lx\n", | ||||
| 	       ah, al, bh, bl, ch, cl); | ||||
| 
 | ||||
| 	asm volatile ("getcon   " __PEXPEVT ", %0":"=r"(ah)); | ||||
| 	asm volatile ("getcon   " __PEXPEVT ", %0":"=r"(al)); | ||||
| 	ah = (ah) >> 32; | ||||
| 	al = (al) & 0xffffffff; | ||||
| 	asm volatile ("getcon   " __PSPC ", %0":"=r"(bh)); | ||||
| 	asm volatile ("getcon   " __PSPC ", %0":"=r"(bl)); | ||||
| 	bh = (bh) >> 32; | ||||
| 	bl = (bl) & 0xffffffff; | ||||
| 	asm volatile ("getcon   " __PSSR ", %0":"=r"(ch)); | ||||
| 	asm volatile ("getcon   " __PSSR ", %0":"=r"(cl)); | ||||
| 	ch = (ch) >> 32; | ||||
| 	cl = (cl) & 0xffffffff; | ||||
| 	printk("PEXP: %08Lx%08Lx PSPC: %08Lx%08Lx PSSR: %08Lx%08Lx\n", | ||||
| 	       ah, al, bh, bl, ch, cl); | ||||
| 
 | ||||
| 	ah = (regs->pc) >> 32; | ||||
| 	al = (regs->pc) & 0xffffffff; | ||||
| 	bh = (regs->regs[18]) >> 32; | ||||
| 	bl = (regs->regs[18]) & 0xffffffff; | ||||
| 	ch = (regs->regs[15]) >> 32; | ||||
| 	cl = (regs->regs[15]) & 0xffffffff; | ||||
| 	printk("PC  : %08Lx%08Lx LINK: %08Lx%08Lx SP  : %08Lx%08Lx\n", | ||||
| 	       ah, al, bh, bl, ch, cl); | ||||
| 
 | ||||
| 	ah = (regs->sr) >> 32; | ||||
| 	al = (regs->sr) & 0xffffffff; | ||||
| 	asm volatile ("getcon   " __TEA ", %0":"=r"(bh)); | ||||
| 	asm volatile ("getcon   " __TEA ", %0":"=r"(bl)); | ||||
| 	bh = (bh) >> 32; | ||||
| 	bl = (bl) & 0xffffffff; | ||||
| 	asm volatile ("getcon   " __KCR0 ", %0":"=r"(ch)); | ||||
| 	asm volatile ("getcon   " __KCR0 ", %0":"=r"(cl)); | ||||
| 	ch = (ch) >> 32; | ||||
| 	cl = (cl) & 0xffffffff; | ||||
| 	printk("SR  : %08Lx%08Lx TEA : %08Lx%08Lx KCR0: %08Lx%08Lx\n", | ||||
| 	       ah, al, bh, bl, ch, cl); | ||||
| 
 | ||||
| 	ah = (regs->regs[0]) >> 32; | ||||
| 	al = (regs->regs[0]) & 0xffffffff; | ||||
| 	bh = (regs->regs[1]) >> 32; | ||||
| 	bl = (regs->regs[1]) & 0xffffffff; | ||||
| 	ch = (regs->regs[2]) >> 32; | ||||
| 	cl = (regs->regs[2]) & 0xffffffff; | ||||
| 	printk("R0  : %08Lx%08Lx R1  : %08Lx%08Lx R2  : %08Lx%08Lx\n", | ||||
| 	       ah, al, bh, bl, ch, cl); | ||||
| 
 | ||||
| 	ah = (regs->regs[3]) >> 32; | ||||
| 	al = (regs->regs[3]) & 0xffffffff; | ||||
| 	bh = (regs->regs[4]) >> 32; | ||||
| 	bl = (regs->regs[4]) & 0xffffffff; | ||||
| 	ch = (regs->regs[5]) >> 32; | ||||
| 	cl = (regs->regs[5]) & 0xffffffff; | ||||
| 	printk("R3  : %08Lx%08Lx R4  : %08Lx%08Lx R5  : %08Lx%08Lx\n", | ||||
| 	       ah, al, bh, bl, ch, cl); | ||||
| 
 | ||||
| 	ah = (regs->regs[6]) >> 32; | ||||
| 	al = (regs->regs[6]) & 0xffffffff; | ||||
| 	bh = (regs->regs[7]) >> 32; | ||||
| 	bl = (regs->regs[7]) & 0xffffffff; | ||||
| 	ch = (regs->regs[8]) >> 32; | ||||
| 	cl = (regs->regs[8]) & 0xffffffff; | ||||
| 	printk("R6  : %08Lx%08Lx R7  : %08Lx%08Lx R8  : %08Lx%08Lx\n", | ||||
| 	       ah, al, bh, bl, ch, cl); | ||||
| 
 | ||||
| 	ah = (regs->regs[9]) >> 32; | ||||
| 	al = (regs->regs[9]) & 0xffffffff; | ||||
| 	bh = (regs->regs[10]) >> 32; | ||||
| 	bl = (regs->regs[10]) & 0xffffffff; | ||||
| 	ch = (regs->regs[11]) >> 32; | ||||
| 	cl = (regs->regs[11]) & 0xffffffff; | ||||
| 	printk("R9  : %08Lx%08Lx R10 : %08Lx%08Lx R11 : %08Lx%08Lx\n", | ||||
| 	       ah, al, bh, bl, ch, cl); | ||||
| 	printk("....\n"); | ||||
| 
 | ||||
| 	ah = (regs->tregs[0]) >> 32; | ||||
| 	al = (regs->tregs[0]) & 0xffffffff; | ||||
| 	bh = (regs->tregs[1]) >> 32; | ||||
| 	bl = (regs->tregs[1]) & 0xffffffff; | ||||
| 	ch = (regs->tregs[2]) >> 32; | ||||
| 	cl = (regs->tregs[2]) & 0xffffffff; | ||||
| 	printk("T0  : %08Lx%08Lx T1  : %08Lx%08Lx T2  : %08Lx%08Lx\n", | ||||
| 	       ah, al, bh, bl, ch, cl); | ||||
| 	printk("....\n"); | ||||
| 
 | ||||
| 	print_dtlb(); | ||||
| 	print_itlb(); | ||||
| } | ||||
| @ -17,7 +17,7 @@ | ||||
| /**
 | ||||
|  * sh64_tlb_init - Perform initial setup for the DTLB and ITLB. | ||||
|  */ | ||||
| int __init sh64_tlb_init(void) | ||||
| int __cpuinit sh64_tlb_init(void) | ||||
| { | ||||
| 	/* Assign some sane DTLB defaults */ | ||||
| 	cpu_data->dtlb.entries	= 64; | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| menu "SuperH / SH-Mobile Driver Options" | ||||
| 
 | ||||
| source "drivers/sh/intc/Kconfig" | ||||
| source "drivers/sh/pfc/Kconfig" | ||||
| 
 | ||||
| endmenu | ||||
|  | ||||
| @ -5,6 +5,7 @@ obj-y	:= intc/ | ||||
| 
 | ||||
| obj-$(CONFIG_HAVE_CLK)		+= clk/ | ||||
| obj-$(CONFIG_MAPLE)		+= maple/ | ||||
| obj-$(CONFIG_SH_PFC)		+= pfc/ | ||||
| obj-$(CONFIG_SUPERHYWAY)	+= superhyway/ | ||||
| obj-$(CONFIG_GENERIC_GPIO)	+= pfc.o | ||||
| 
 | ||||
| obj-y				+= pm_runtime.o | ||||
|  | ||||
| @ -14,6 +14,8 @@ | ||||
| #include <linux/io.h> | ||||
| #include <linux/sh_clk.h> | ||||
| 
 | ||||
| #define CPG_CKSTP_BIT	BIT(8) | ||||
| 
 | ||||
| static unsigned int sh_clk_read(struct clk *clk) | ||||
| { | ||||
| 	if (clk->flags & CLK_ENABLE_REG_8BIT) | ||||
| @ -66,71 +68,43 @@ int __init sh_clk_mstp_register(struct clk *clks, int nr) | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Div/mult table lookup helpers | ||||
|  */ | ||||
| static inline struct clk_div_table *clk_to_div_table(struct clk *clk) | ||||
| { | ||||
| 	return clk->priv; | ||||
| } | ||||
| 
 | ||||
| static inline struct clk_div_mult_table *clk_to_div_mult_table(struct clk *clk) | ||||
| { | ||||
| 	return clk_to_div_table(clk)->div_mult_table; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Common div ops | ||||
|  */ | ||||
| static long sh_clk_div_round_rate(struct clk *clk, unsigned long rate) | ||||
| { | ||||
| 	return clk_rate_table_round(clk, clk->freq_table, rate); | ||||
| } | ||||
| 
 | ||||
| static int sh_clk_div6_divisors[64] = { | ||||
| 	1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, | ||||
| 	17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, | ||||
| 	33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, | ||||
| 	49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64 | ||||
| }; | ||||
| 
 | ||||
| static struct clk_div_mult_table sh_clk_div6_table = { | ||||
| 	.divisors = sh_clk_div6_divisors, | ||||
| 	.nr_divisors = ARRAY_SIZE(sh_clk_div6_divisors), | ||||
| }; | ||||
| 
 | ||||
| static unsigned long sh_clk_div6_recalc(struct clk *clk) | ||||
| static unsigned long sh_clk_div_recalc(struct clk *clk) | ||||
| { | ||||
| 	struct clk_div_mult_table *table = &sh_clk_div6_table; | ||||
| 	struct clk_div_mult_table *table = clk_to_div_mult_table(clk); | ||||
| 	unsigned int idx; | ||||
| 
 | ||||
| 	clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, | ||||
| 			     table, NULL); | ||||
| 			     table, clk->arch_flags ? &clk->arch_flags : NULL); | ||||
| 
 | ||||
| 	idx = sh_clk_read(clk) & 0x003f; | ||||
| 	idx = (sh_clk_read(clk) >> clk->enable_bit) & clk->div_mask; | ||||
| 
 | ||||
| 	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 = sh_clk_read(clk) & | ||||
| 		~(((1 << clk->src_width) - 1) << clk->src_shift); | ||||
| 
 | ||||
| 	sh_clk_write(value | (i << clk->src_shift), clk); | ||||
| 
 | ||||
| 	/* Rebuild the frequency table */ | ||||
| 	clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, | ||||
| 			     table, NULL); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int sh_clk_div6_set_rate(struct clk *clk, unsigned long rate) | ||||
| static int sh_clk_div_set_rate(struct clk *clk, unsigned long rate) | ||||
| { | ||||
| 	struct clk_div_table *dt = clk_to_div_table(clk); | ||||
| 	unsigned long value; | ||||
| 	int idx; | ||||
| 
 | ||||
| @ -139,51 +113,53 @@ static int sh_clk_div6_set_rate(struct clk *clk, unsigned long rate) | ||||
| 		return idx; | ||||
| 
 | ||||
| 	value = sh_clk_read(clk); | ||||
| 	value &= ~0x3f; | ||||
| 	value |= idx; | ||||
| 	value &= ~(clk->div_mask << clk->enable_bit); | ||||
| 	value |= (idx << clk->enable_bit); | ||||
| 	sh_clk_write(value, clk); | ||||
| 
 | ||||
| 	/* XXX: Should use a post-change notifier */ | ||||
| 	if (dt->kick) | ||||
| 		dt->kick(clk); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int sh_clk_div6_enable(struct clk *clk) | ||||
| static int sh_clk_div_enable(struct clk *clk) | ||||
| { | ||||
| 	unsigned long value; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = sh_clk_div6_set_rate(clk, clk->rate); | ||||
| 	if (ret == 0) { | ||||
| 		value = sh_clk_read(clk); | ||||
| 		value &= ~0x100; /* clear stop bit to enable clock */ | ||||
| 		sh_clk_write(value, clk); | ||||
| 	} | ||||
| 	return ret; | ||||
| 	sh_clk_write(sh_clk_read(clk) & ~CPG_CKSTP_BIT, clk); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void sh_clk_div6_disable(struct clk *clk) | ||||
| static void sh_clk_div_disable(struct clk *clk) | ||||
| { | ||||
| 	unsigned long value; | ||||
| 	unsigned int val; | ||||
| 
 | ||||
| 	value = sh_clk_read(clk); | ||||
| 	value |= 0x100; /* stop clock */ | ||||
| 	value |= 0x3f; /* VDIV bits must be non-zero, overwrite divider */ | ||||
| 	sh_clk_write(value, clk); | ||||
| 	val = sh_clk_read(clk); | ||||
| 	val |= CPG_CKSTP_BIT; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * div6 clocks require the divisor field to be non-zero or the | ||||
| 	 * above CKSTP toggle silently fails. Ensure that the divisor | ||||
| 	 * array is reset to its initial state on disable. | ||||
| 	 */ | ||||
| 	if (clk->flags & CLK_MASK_DIV_ON_DISABLE) | ||||
| 		val |= clk->div_mask; | ||||
| 
 | ||||
| 	sh_clk_write(val, clk); | ||||
| } | ||||
| 
 | ||||
| static struct sh_clk_ops sh_clk_div6_clk_ops = { | ||||
| 	.recalc		= sh_clk_div6_recalc, | ||||
| static struct sh_clk_ops sh_clk_div_clk_ops = { | ||||
| 	.recalc		= sh_clk_div_recalc, | ||||
| 	.set_rate	= sh_clk_div_set_rate, | ||||
| 	.round_rate	= sh_clk_div_round_rate, | ||||
| 	.set_rate	= sh_clk_div6_set_rate, | ||||
| 	.enable		= sh_clk_div6_enable, | ||||
| 	.disable	= sh_clk_div6_disable, | ||||
| }; | ||||
| 
 | ||||
| static struct sh_clk_ops sh_clk_div6_reparent_clk_ops = { | ||||
| 	.recalc		= sh_clk_div6_recalc, | ||||
| static struct sh_clk_ops sh_clk_div_enable_clk_ops = { | ||||
| 	.recalc		= sh_clk_div_recalc, | ||||
| 	.set_rate	= sh_clk_div_set_rate, | ||||
| 	.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, | ||||
| 	.enable		= sh_clk_div_enable, | ||||
| 	.disable	= sh_clk_div_disable, | ||||
| }; | ||||
| 
 | ||||
| static int __init sh_clk_init_parent(struct clk *clk) | ||||
| @ -218,12 +194,12 @@ static int __init sh_clk_init_parent(struct clk *clk) | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int __init sh_clk_div6_register_ops(struct clk *clks, int nr, | ||||
| 					   struct sh_clk_ops *ops) | ||||
| static int __init sh_clk_div_register_ops(struct clk *clks, int nr, | ||||
| 			struct clk_div_table *table, struct sh_clk_ops *ops) | ||||
| { | ||||
| 	struct clk *clkp; | ||||
| 	void *freq_table; | ||||
| 	int nr_divs = sh_clk_div6_table.nr_divisors; | ||||
| 	int nr_divs = table->div_mult_table->nr_divisors; | ||||
| 	int freq_table_size = sizeof(struct cpufreq_frequency_table); | ||||
| 	int ret = 0; | ||||
| 	int k; | ||||
| @ -231,7 +207,7 @@ static int __init sh_clk_div6_register_ops(struct clk *clks, int nr, | ||||
| 	freq_table_size *= (nr_divs + 1); | ||||
| 	freq_table = kzalloc(freq_table_size * nr, GFP_KERNEL); | ||||
| 	if (!freq_table) { | ||||
| 		pr_err("sh_clk_div6_register: unable to alloc memory\n"); | ||||
| 		pr_err("%s: unable to alloc memory\n", __func__); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 
 | ||||
| @ -239,47 +215,98 @@ static int __init sh_clk_div6_register_ops(struct clk *clks, int nr, | ||||
| 		clkp = clks + k; | ||||
| 
 | ||||
| 		clkp->ops = ops; | ||||
| 		clkp->priv = table; | ||||
| 
 | ||||
| 		clkp->freq_table = freq_table + (k * freq_table_size); | ||||
| 		clkp->freq_table[nr_divs].frequency = CPUFREQ_TABLE_END; | ||||
| 		ret = clk_register(clkp); | ||||
| 		if (ret < 0) | ||||
| 			break; | ||||
| 
 | ||||
| 		ret = sh_clk_init_parent(clkp); | ||||
| 		ret = clk_register(clkp); | ||||
| 		if (ret == 0) | ||||
| 			ret = sh_clk_init_parent(clkp); | ||||
| 	} | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * div6 support | ||||
|  */ | ||||
| static int sh_clk_div6_divisors[64] = { | ||||
| 	1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, | ||||
| 	17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, | ||||
| 	33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, | ||||
| 	49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64 | ||||
| }; | ||||
| 
 | ||||
| static struct clk_div_mult_table div6_div_mult_table = { | ||||
| 	.divisors = sh_clk_div6_divisors, | ||||
| 	.nr_divisors = ARRAY_SIZE(sh_clk_div6_divisors), | ||||
| }; | ||||
| 
 | ||||
| static struct clk_div_table sh_clk_div6_table = { | ||||
| 	.div_mult_table	= &div6_div_mult_table, | ||||
| }; | ||||
| 
 | ||||
| static int sh_clk_div6_set_parent(struct clk *clk, struct clk *parent) | ||||
| { | ||||
| 	struct clk_div_mult_table *table = clk_to_div_mult_table(clk); | ||||
| 	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 = sh_clk_read(clk) & | ||||
| 		~(((1 << clk->src_width) - 1) << clk->src_shift); | ||||
| 
 | ||||
| 	sh_clk_write(value | (i << clk->src_shift), clk); | ||||
| 
 | ||||
| 	/* Rebuild the frequency table */ | ||||
| 	clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, | ||||
| 			     table, NULL); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static struct sh_clk_ops sh_clk_div6_reparent_clk_ops = { | ||||
| 	.recalc		= sh_clk_div_recalc, | ||||
| 	.round_rate	= sh_clk_div_round_rate, | ||||
| 	.set_rate	= sh_clk_div_set_rate, | ||||
| 	.enable		= sh_clk_div_enable, | ||||
| 	.disable	= sh_clk_div_disable, | ||||
| 	.set_parent	= sh_clk_div6_set_parent, | ||||
| }; | ||||
| 
 | ||||
| int __init sh_clk_div6_register(struct clk *clks, int nr) | ||||
| { | ||||
| 	return sh_clk_div6_register_ops(clks, nr, &sh_clk_div6_clk_ops); | ||||
| 	return sh_clk_div_register_ops(clks, nr, &sh_clk_div6_table, | ||||
| 				       &sh_clk_div_enable_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; | ||||
| 	struct clk_div_mult_table *table = d4t->div_mult_table; | ||||
| 	unsigned int idx; | ||||
| 
 | ||||
| 	clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, | ||||
| 			     table, &clk->arch_flags); | ||||
| 
 | ||||
| 	idx = (sh_clk_read(clk) >> clk->enable_bit) & 0x000f; | ||||
| 
 | ||||
| 	return clk->freq_table[idx].frequency; | ||||
| 	return sh_clk_div_register_ops(clks, nr, &sh_clk_div6_table, | ||||
| 				       &sh_clk_div6_reparent_clk_ops); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * div4 support | ||||
|  */ | ||||
| static int sh_clk_div4_set_parent(struct clk *clk, struct clk *parent) | ||||
| { | ||||
| 	struct clk_div4_table *d4t = clk->priv; | ||||
| 	struct clk_div_mult_table *table = d4t->div_mult_table; | ||||
| 	struct clk_div_mult_table *table = clk_to_div_mult_table(clk); | ||||
| 	u32 value; | ||||
| 	int ret; | ||||
| 
 | ||||
| @ -306,107 +333,31 @@ static int sh_clk_div4_set_parent(struct clk *clk, struct clk *parent) | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int sh_clk_div4_set_rate(struct clk *clk, unsigned long rate) | ||||
| { | ||||
| 	struct clk_div4_table *d4t = clk->priv; | ||||
| 	unsigned long value; | ||||
| 	int idx = clk_rate_table_find(clk, clk->freq_table, rate); | ||||
| 	if (idx < 0) | ||||
| 		return idx; | ||||
| 
 | ||||
| 	value = sh_clk_read(clk); | ||||
| 	value &= ~(0xf << clk->enable_bit); | ||||
| 	value |= (idx << clk->enable_bit); | ||||
| 	sh_clk_write(value, clk); | ||||
| 
 | ||||
| 	if (d4t->kick) | ||||
| 		d4t->kick(clk); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int sh_clk_div4_enable(struct clk *clk) | ||||
| { | ||||
| 	sh_clk_write(sh_clk_read(clk) & ~(1 << 8), clk); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void sh_clk_div4_disable(struct clk *clk) | ||||
| { | ||||
| 	sh_clk_write(sh_clk_read(clk) | (1 << 8), clk); | ||||
| } | ||||
| 
 | ||||
| static struct sh_clk_ops sh_clk_div4_clk_ops = { | ||||
| 	.recalc		= sh_clk_div4_recalc, | ||||
| 	.set_rate	= sh_clk_div4_set_rate, | ||||
| 	.round_rate	= sh_clk_div_round_rate, | ||||
| }; | ||||
| 
 | ||||
| static struct sh_clk_ops sh_clk_div4_enable_clk_ops = { | ||||
| 	.recalc		= sh_clk_div4_recalc, | ||||
| 	.set_rate	= sh_clk_div4_set_rate, | ||||
| 	.round_rate	= sh_clk_div_round_rate, | ||||
| 	.enable		= sh_clk_div4_enable, | ||||
| 	.disable	= sh_clk_div4_disable, | ||||
| }; | ||||
| 
 | ||||
| static struct sh_clk_ops sh_clk_div4_reparent_clk_ops = { | ||||
| 	.recalc		= sh_clk_div4_recalc, | ||||
| 	.set_rate	= sh_clk_div4_set_rate, | ||||
| 	.recalc		= sh_clk_div_recalc, | ||||
| 	.set_rate	= sh_clk_div_set_rate, | ||||
| 	.round_rate	= sh_clk_div_round_rate, | ||||
| 	.enable		= sh_clk_div4_enable, | ||||
| 	.disable	= sh_clk_div4_disable, | ||||
| 	.enable		= sh_clk_div_enable, | ||||
| 	.disable	= sh_clk_div_disable, | ||||
| 	.set_parent	= sh_clk_div4_set_parent, | ||||
| }; | ||||
| 
 | ||||
| static int __init sh_clk_div4_register_ops(struct clk *clks, int nr, | ||||
| 			struct clk_div4_table *table, struct sh_clk_ops *ops) | ||||
| { | ||||
| 	struct clk *clkp; | ||||
| 	void *freq_table; | ||||
| 	int nr_divs = table->div_mult_table->nr_divisors; | ||||
| 	int freq_table_size = sizeof(struct cpufreq_frequency_table); | ||||
| 	int ret = 0; | ||||
| 	int k; | ||||
| 
 | ||||
| 	freq_table_size *= (nr_divs + 1); | ||||
| 	freq_table = kzalloc(freq_table_size * nr, GFP_KERNEL); | ||||
| 	if (!freq_table) { | ||||
| 		pr_err("sh_clk_div4_register: unable to alloc memory\n"); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 
 | ||||
| 	for (k = 0; !ret && (k < nr); k++) { | ||||
| 		clkp = clks + k; | ||||
| 
 | ||||
| 		clkp->ops = ops; | ||||
| 		clkp->priv = table; | ||||
| 
 | ||||
| 		clkp->freq_table = freq_table + (k * freq_table_size); | ||||
| 		clkp->freq_table[nr_divs].frequency = CPUFREQ_TABLE_END; | ||||
| 
 | ||||
| 		ret = clk_register(clkp); | ||||
| 	} | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| int __init sh_clk_div4_register(struct clk *clks, int nr, | ||||
| 				struct clk_div4_table *table) | ||||
| { | ||||
| 	return sh_clk_div4_register_ops(clks, nr, table, &sh_clk_div4_clk_ops); | ||||
| 	return sh_clk_div_register_ops(clks, nr, table, &sh_clk_div_clk_ops); | ||||
| } | ||||
| 
 | ||||
| int __init sh_clk_div4_enable_register(struct clk *clks, int nr, | ||||
| 				struct clk_div4_table *table) | ||||
| { | ||||
| 	return sh_clk_div4_register_ops(clks, nr, table, | ||||
| 					&sh_clk_div4_enable_clk_ops); | ||||
| 	return sh_clk_div_register_ops(clks, nr, table, | ||||
| 				       &sh_clk_div_enable_clk_ops); | ||||
| } | ||||
| 
 | ||||
| int __init sh_clk_div4_reparent_register(struct clk *clks, int nr, | ||||
| 				struct clk_div4_table *table) | ||||
| { | ||||
| 	return sh_clk_div4_register_ops(clks, nr, table, | ||||
| 					&sh_clk_div4_reparent_clk_ops); | ||||
| 	return sh_clk_div_register_ops(clks, nr, table, | ||||
| 				       &sh_clk_div4_reparent_clk_ops); | ||||
| } | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| obj-y 	:= access.o chip.o core.o dynamic.o handle.o virq.o | ||||
| obj-y 	:= access.o chip.o core.o handle.o virq.o | ||||
| 
 | ||||
| obj-$(CONFIG_INTC_BALANCING)		+= balancing.o | ||||
| obj-$(CONFIG_INTC_USERIMASK)		+= userimask.o | ||||
|  | ||||
| @ -1,57 +0,0 @@ | ||||
| /*
 | ||||
|  * Dynamic IRQ management | ||||
|  * | ||||
|  * Copyright (C) 2010  Paul Mundt | ||||
|  * | ||||
|  * Modelled after arch/x86/kernel/apic/io_apic.c | ||||
|  * | ||||
|  * 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. | ||||
|  */ | ||||
| #define pr_fmt(fmt) "intc: " fmt | ||||
| 
 | ||||
| #include <linux/irq.h> | ||||
| #include <linux/bitmap.h> | ||||
| #include <linux/spinlock.h> | ||||
| #include <linux/module.h> | ||||
| #include "internals.h" /* only for activate_irq() damage.. */ | ||||
| 
 | ||||
| /*
 | ||||
|  * The IRQ bitmap 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. | ||||
|  */ | ||||
| 
 | ||||
| /*
 | ||||
|  * Dynamic IRQ allocation and deallocation | ||||
|  */ | ||||
| unsigned int create_irq_nr(unsigned int irq_want, int node) | ||||
| { | ||||
| 	int irq = irq_alloc_desc_at(irq_want, node); | ||||
| 	if (irq < 0) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	activate_irq(irq); | ||||
| 	return irq; | ||||
| } | ||||
| 
 | ||||
| int create_irq(void) | ||||
| { | ||||
| 	int irq = irq_alloc_desc(numa_node_id()); | ||||
| 	if (irq >= 0) | ||||
| 		activate_irq(irq); | ||||
| 
 | ||||
| 	return irq; | ||||
| } | ||||
| 
 | ||||
| void destroy_irq(unsigned int irq) | ||||
| { | ||||
| 	irq_free_desc(irq); | ||||
| } | ||||
| @ -219,12 +219,14 @@ restart: | ||||
| 		if (radix_tree_deref_retry(entry)) | ||||
| 			goto restart; | ||||
| 
 | ||||
| 		irq = create_irq(); | ||||
| 		irq = irq_alloc_desc(numa_node_id()); | ||||
| 		if (unlikely(irq < 0)) { | ||||
| 			pr_err("no more free IRQs, bailing..\n"); | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		activate_irq(irq); | ||||
| 
 | ||||
| 		pr_info("Setting up a chained VIRQ from %d -> %d\n", | ||||
| 			irq, entry->pirq); | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										739
									
								
								drivers/sh/pfc.c
									
									
									
									
									
								
							
							
						
						
									
										739
									
								
								drivers/sh/pfc.c
									
									
									
									
									
								
							| @ -1,739 +0,0 @@ | ||||
| /*
 | ||||
|  * 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. | ||||
|  */ | ||||
| #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||||
| 
 | ||||
| #include <linux/errno.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/list.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/clk.h> | ||||
| #include <linux/err.h> | ||||
| #include <linux/io.h> | ||||
| #include <linux/irq.h> | ||||
| #include <linux/bitops.h> | ||||
| #include <linux/gpio.h> | ||||
| #include <linux/slab.h> | ||||
| #include <linux/ioport.h> | ||||
| 
 | ||||
| static void pfc_iounmap(struct pinmux_info *pip) | ||||
| { | ||||
| 	int k; | ||||
| 
 | ||||
| 	for (k = 0; k < pip->num_resources; k++) | ||||
| 		if (pip->window[k].virt) | ||||
| 			iounmap(pip->window[k].virt); | ||||
| 
 | ||||
| 	kfree(pip->window); | ||||
| 	pip->window = NULL; | ||||
| } | ||||
| 
 | ||||
| static int pfc_ioremap(struct pinmux_info *pip) | ||||
| { | ||||
| 	struct resource *res; | ||||
| 	int k; | ||||
| 
 | ||||
| 	if (!pip->num_resources) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	pip->window = kzalloc(pip->num_resources * sizeof(*pip->window), | ||||
| 			      GFP_NOWAIT); | ||||
| 	if (!pip->window) | ||||
| 		goto err1; | ||||
| 
 | ||||
| 	for (k = 0; k < pip->num_resources; k++) { | ||||
| 		res = pip->resource + k; | ||||
| 		WARN_ON(resource_type(res) != IORESOURCE_MEM); | ||||
| 		pip->window[k].phys = res->start; | ||||
| 		pip->window[k].size = resource_size(res); | ||||
| 		pip->window[k].virt = ioremap_nocache(res->start, | ||||
| 							 resource_size(res)); | ||||
| 		if (!pip->window[k].virt) | ||||
| 			goto err2; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| err2: | ||||
| 	pfc_iounmap(pip); | ||||
| err1: | ||||
| 	return -1; | ||||
| } | ||||
| 
 | ||||
| static void __iomem *pfc_phys_to_virt(struct pinmux_info *pip, | ||||
| 				      unsigned long address) | ||||
| { | ||||
| 	struct pfc_window *window; | ||||
| 	int k; | ||||
| 
 | ||||
| 	/* scan through physical windows and convert address */ | ||||
| 	for (k = 0; k < pip->num_resources; k++) { | ||||
| 		window = pip->window + k; | ||||
| 
 | ||||
| 		if (address < window->phys) | ||||
| 			continue; | ||||
| 
 | ||||
| 		if (address >= (window->phys + window->size)) | ||||
| 			continue; | ||||
| 
 | ||||
| 		return window->virt + (address - window->phys); | ||||
| 	} | ||||
| 
 | ||||
| 	/* no windows defined, register must be 1:1 mapped virt:phys */ | ||||
| 	return (void __iomem *)address; | ||||
| } | ||||
| 
 | ||||
| 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(void __iomem *mapped_reg, | ||||
| 				       unsigned long reg_width) | ||||
| { | ||||
| 	switch (reg_width) { | ||||
| 	case 8: | ||||
| 		return ioread8(mapped_reg); | ||||
| 	case 16: | ||||
| 		return ioread16(mapped_reg); | ||||
| 	case 32: | ||||
| 		return ioread32(mapped_reg); | ||||
| 	} | ||||
| 
 | ||||
| 	BUG(); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void gpio_write_raw_reg(void __iomem *mapped_reg, | ||||
| 			       unsigned long reg_width, | ||||
| 			       unsigned long data) | ||||
| { | ||||
| 	switch (reg_width) { | ||||
| 	case 8: | ||||
| 		iowrite8(data, mapped_reg); | ||||
| 		return; | ||||
| 	case 16: | ||||
| 		iowrite16(data, mapped_reg); | ||||
| 		return; | ||||
| 	case 32: | ||||
| 		iowrite32(data, mapped_reg); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	BUG(); | ||||
| } | ||||
| 
 | ||||
| static int gpio_read_bit(struct pinmux_data_reg *dr, | ||||
| 			 unsigned long in_pos) | ||||
| { | ||||
| 	unsigned long pos; | ||||
| 
 | ||||
| 	pos = dr->reg_width - (in_pos + 1); | ||||
| 
 | ||||
| 	pr_debug("read_bit: addr = %lx, pos = %ld, " | ||||
| 		 "r_width = %ld\n", dr->reg, pos, dr->reg_width); | ||||
| 
 | ||||
| 	return (gpio_read_raw_reg(dr->mapped_reg, dr->reg_width) >> pos) & 1; | ||||
| } | ||||
| 
 | ||||
| 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); | ||||
| 
 | ||||
| 	pr_debug("write_bit addr = %lx, value = %d, pos = %ld, " | ||||
| 		 "r_width = %ld\n", | ||||
| 		 dr->reg, !!value, pos, dr->reg_width); | ||||
| 
 | ||||
| 	if (value) | ||||
| 		set_bit(pos, &dr->reg_shadow); | ||||
| 	else | ||||
| 		clear_bit(pos, &dr->reg_shadow); | ||||
| 
 | ||||
| 	gpio_write_raw_reg(dr->mapped_reg, dr->reg_width, dr->reg_shadow); | ||||
| } | ||||
| 
 | ||||
| static void config_reg_helper(struct pinmux_info *gpioc, | ||||
| 			      struct pinmux_cfg_reg *crp, | ||||
| 			      unsigned long in_pos, | ||||
| 			      void __iomem **mapped_regp, | ||||
| 			      unsigned long *maskp, | ||||
| 			      unsigned long *posp) | ||||
| { | ||||
| 	int k; | ||||
| 
 | ||||
| 	*mapped_regp = pfc_phys_to_virt(gpioc, crp->reg); | ||||
| 
 | ||||
| 	if (crp->field_width) { | ||||
| 		*maskp = (1 << crp->field_width) - 1; | ||||
| 		*posp = crp->reg_width - ((in_pos + 1) * crp->field_width); | ||||
| 	} else { | ||||
| 		*maskp = (1 << crp->var_field_width[in_pos]) - 1; | ||||
| 		*posp = crp->reg_width; | ||||
| 		for (k = 0; k <= in_pos; k++) | ||||
| 			*posp -= crp->var_field_width[k]; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static int read_config_reg(struct pinmux_info *gpioc, | ||||
| 			   struct pinmux_cfg_reg *crp, | ||||
| 			   unsigned long field) | ||||
| { | ||||
| 	void __iomem *mapped_reg; | ||||
| 	unsigned long mask, pos; | ||||
| 
 | ||||
| 	config_reg_helper(gpioc, crp, field, &mapped_reg, &mask, &pos); | ||||
| 
 | ||||
| 	pr_debug("read_reg: addr = %lx, field = %ld, " | ||||
| 		 "r_width = %ld, f_width = %ld\n", | ||||
| 		 crp->reg, field, crp->reg_width, crp->field_width); | ||||
| 
 | ||||
| 	return (gpio_read_raw_reg(mapped_reg, crp->reg_width) >> pos) & mask; | ||||
| } | ||||
| 
 | ||||
| static void write_config_reg(struct pinmux_info *gpioc, | ||||
| 			     struct pinmux_cfg_reg *crp, | ||||
| 			     unsigned long field, unsigned long value) | ||||
| { | ||||
| 	void __iomem *mapped_reg; | ||||
| 	unsigned long mask, pos, data; | ||||
| 
 | ||||
| 	config_reg_helper(gpioc, crp, field, &mapped_reg, &mask, &pos); | ||||
| 
 | ||||
| 	pr_debug("write_reg addr = %lx, value = %ld, field = %ld, " | ||||
| 		 "r_width = %ld, f_width = %ld\n", | ||||
| 		 crp->reg, value, field, crp->reg_width, crp->field_width); | ||||
| 
 | ||||
| 	mask = ~(mask << pos); | ||||
| 	value = value << pos; | ||||
| 
 | ||||
| 	data = gpio_read_raw_reg(mapped_reg, crp->reg_width); | ||||
| 	data &= mask; | ||||
| 	data |= value; | ||||
| 
 | ||||
| 	if (gpioc->unlock_reg) | ||||
| 		gpio_write_raw_reg(pfc_phys_to_virt(gpioc, gpioc->unlock_reg), | ||||
| 				   32, ~data); | ||||
| 
 | ||||
| 	gpio_write_raw_reg(mapped_reg, crp->reg_width, data); | ||||
| } | ||||
| 
 | ||||
| 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; | ||||
| 
 | ||||
| 		data_reg->mapped_reg = pfc_phys_to_virt(gpioc, data_reg->reg); | ||||
| 
 | ||||
| 		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->mapped_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 *fieldp, int *valuep, | ||||
| 			  unsigned long **cntp) | ||||
| { | ||||
| 	struct pinmux_cfg_reg *config_reg; | ||||
| 	unsigned long r_width, f_width, curr_width, ncomb; | ||||
| 	int k, m, n, pos, bit_pos; | ||||
| 
 | ||||
| 	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; | ||||
| 
 | ||||
| 		pos = 0; | ||||
| 		m = 0; | ||||
| 		for (bit_pos = 0; bit_pos < r_width; bit_pos += curr_width) { | ||||
| 			if (f_width) | ||||
| 				curr_width = f_width; | ||||
| 			else | ||||
| 				curr_width = config_reg->var_field_width[m]; | ||||
| 
 | ||||
| 			ncomb = 1 << curr_width; | ||||
| 			for (n = 0; n < ncomb; n++) { | ||||
| 				if (config_reg->enum_ids[pos + n] == enum_id) { | ||||
| 					*crp = config_reg; | ||||
| 					*fieldp = m; | ||||
| 					*valuep = n; | ||||
| 					*cntp = &config_reg->cnt[m]; | ||||
| 					return 0; | ||||
| 				} | ||||
| 			} | ||||
| 			pos += ncomb; | ||||
| 			m++; | ||||
| 		} | ||||
| 		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; | ||||
| } | ||||
| 
 | ||||
| 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, field, value; | ||||
| 	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; | ||||
| 	field = 0; | ||||
| 	value = 0; | ||||
| 	while (1) { | ||||
| 		pos = get_gpio_enum_id(gpioc, gpio, pos, &enum_id); | ||||
| 		if (pos <= 0) | ||||
| 			goto out_err; | ||||
| 
 | ||||
| 		if (!enum_id) | ||||
| 			break; | ||||
| 
 | ||||
| 		/* first check if this is a function enum */ | ||||
| 		in_range = enum_in_range(enum_id, &gpioc->function); | ||||
| 		if (!in_range) { | ||||
| 			/* not a function enum */ | ||||
| 			if (range) { | ||||
| 				/*
 | ||||
| 				 * other range exists, so this pin is | ||||
| 				 * a regular GPIO pin that now is being | ||||
| 				 * bound to a specific direction. | ||||
| 				 * | ||||
| 				 * for this case we only allow function enums | ||||
| 				 * and the enums that match the other range. | ||||
| 				 */ | ||||
| 				in_range = enum_in_range(enum_id, range); | ||||
| 
 | ||||
| 				/*
 | ||||
| 				 * special case pass through for fixed | ||||
| 				 * input-only or output-only pins without | ||||
| 				 * function enum register association. | ||||
| 				 */ | ||||
| 				if (in_range && enum_id == range->force) | ||||
| 					continue; | ||||
| 			} else { | ||||
| 				/*
 | ||||
| 				 * no other range exists, so this pin | ||||
| 				 * must then be of the function type. | ||||
| 				 * | ||||
| 				 * allow function type pins to select | ||||
| 				 * any combination of function/in/out | ||||
| 				 * in their MARK lists. | ||||
| 				 */ | ||||
| 				in_range = 1; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if (!in_range) | ||||
| 			continue; | ||||
| 
 | ||||
| 		if (get_config_reg(gpioc, enum_id, &cr, | ||||
| 				   &field, &value, &cntp) != 0) | ||||
| 			goto out_err; | ||||
| 
 | ||||
| 		switch (cfg_mode) { | ||||
| 		case GPIO_CFG_DRYRUN: | ||||
| 			if (!*cntp || | ||||
| 			    (read_config_reg(gpioc, cr, field) != value)) | ||||
| 				continue; | ||||
| 			break; | ||||
| 
 | ||||
| 		case GPIO_CFG_REQ: | ||||
| 			write_config_reg(gpioc, cr, field, value); | ||||
| 			*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) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	return gpio_read_bit(dr, 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); | ||||
| } | ||||
| 
 | ||||
| static int sh_gpio_to_irq(struct gpio_chip *chip, unsigned offset) | ||||
| { | ||||
| 	struct pinmux_info *gpioc = chip_to_pinmux(chip); | ||||
| 	pinmux_enum_t enum_id; | ||||
| 	pinmux_enum_t *enum_ids; | ||||
| 	int i, k, pos; | ||||
| 
 | ||||
| 	pos = 0; | ||||
| 	enum_id = 0; | ||||
| 	while (1) { | ||||
| 		pos = get_gpio_enum_id(gpioc, offset, pos, &enum_id); | ||||
| 		if (pos <= 0 || !enum_id) | ||||
| 			break; | ||||
| 
 | ||||
| 		for (i = 0; i < gpioc->gpio_irq_size; i++) { | ||||
| 			enum_ids = gpioc->gpio_irq[i].enum_ids; | ||||
| 			for (k = 0; enum_ids[k]; k++) { | ||||
| 				if (enum_ids[k] == enum_id) | ||||
| 					return gpioc->gpio_irq[i].irq; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return -ENOSYS; | ||||
| } | ||||
| 
 | ||||
| int register_pinmux(struct pinmux_info *pip) | ||||
| { | ||||
| 	struct gpio_chip *chip = &pip->chip; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	pr_info("%s handling gpio %d -> %d\n", | ||||
| 		pip->name, pip->first_gpio, pip->last_gpio); | ||||
| 
 | ||||
| 	ret = pfc_ioremap(pip); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	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; | ||||
| 	chip->to_irq = sh_gpio_to_irq; | ||||
| 
 | ||||
| 	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; | ||||
| 
 | ||||
| 	ret = gpiochip_add(chip); | ||||
| 	if (ret < 0) | ||||
| 		pfc_iounmap(pip); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| int unregister_pinmux(struct pinmux_info *pip) | ||||
| { | ||||
| 	pr_info("%s deregistering\n", pip->name); | ||||
| 	pfc_iounmap(pip); | ||||
| 	return gpiochip_remove(&pip->chip); | ||||
| } | ||||
							
								
								
									
										26
									
								
								drivers/sh/pfc/Kconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								drivers/sh/pfc/Kconfig
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | ||||
| comment "Pin function controller options" | ||||
| 
 | ||||
| config SH_PFC | ||||
| 	# XXX move off the gpio dependency | ||||
| 	depends on GENERIC_GPIO | ||||
| 	select GPIO_SH_PFC if ARCH_REQUIRE_GPIOLIB | ||||
| 	select PINCTRL_SH_PFC | ||||
| 	def_bool y | ||||
| 
 | ||||
| # | ||||
| # Placeholder for now, rehome to drivers/pinctrl once the PFC APIs | ||||
| # have settled. | ||||
| # | ||||
| config PINCTRL_SH_PFC | ||||
| 	tristate "SuperH PFC pin controller driver" | ||||
| 	depends on SH_PFC | ||||
| 	select PINCTRL | ||||
| 	select PINMUX | ||||
| 	select PINCONF | ||||
| 
 | ||||
| config GPIO_SH_PFC | ||||
| 	tristate "SuperH PFC GPIO support" | ||||
| 	depends on SH_PFC && GPIOLIB | ||||
| 	help | ||||
| 	  This enables support for GPIOs within the SoC's pin function | ||||
| 	  controller. | ||||
							
								
								
									
										3
									
								
								drivers/sh/pfc/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								drivers/sh/pfc/Makefile
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| obj-y				+= core.o | ||||
| obj-$(CONFIG_PINCTRL_SH_PFC)	+= pinctrl.o | ||||
| obj-$(CONFIG_GPIO_SH_PFC)	+= gpio.o | ||||
							
								
								
									
										572
									
								
								drivers/sh/pfc/core.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										572
									
								
								drivers/sh/pfc/core.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,572 @@ | ||||
| /*
 | ||||
|  * SuperH Pin Function Controller support. | ||||
|  * | ||||
|  * Copyright (C) 2008 Magnus Damm | ||||
|  * Copyright (C) 2009 - 2012 Paul Mundt | ||||
|  * | ||||
|  * 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. | ||||
|  */ | ||||
| #define pr_fmt(fmt) "sh_pfc " KBUILD_MODNAME ": " fmt | ||||
| 
 | ||||
| #include <linux/errno.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/sh_pfc.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/err.h> | ||||
| #include <linux/io.h> | ||||
| #include <linux/bitops.h> | ||||
| #include <linux/slab.h> | ||||
| #include <linux/ioport.h> | ||||
| #include <linux/pinctrl/machine.h> | ||||
| 
 | ||||
| static struct sh_pfc *sh_pfc __read_mostly; | ||||
| 
 | ||||
| static inline bool sh_pfc_initialized(void) | ||||
| { | ||||
| 	return !!sh_pfc; | ||||
| } | ||||
| 
 | ||||
| static void pfc_iounmap(struct sh_pfc *pfc) | ||||
| { | ||||
| 	int k; | ||||
| 
 | ||||
| 	for (k = 0; k < pfc->num_resources; k++) | ||||
| 		if (pfc->window[k].virt) | ||||
| 			iounmap(pfc->window[k].virt); | ||||
| 
 | ||||
| 	kfree(pfc->window); | ||||
| 	pfc->window = NULL; | ||||
| } | ||||
| 
 | ||||
| static int pfc_ioremap(struct sh_pfc *pfc) | ||||
| { | ||||
| 	struct resource *res; | ||||
| 	int k; | ||||
| 
 | ||||
| 	if (!pfc->num_resources) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	pfc->window = kzalloc(pfc->num_resources * sizeof(*pfc->window), | ||||
| 			      GFP_NOWAIT); | ||||
| 	if (!pfc->window) | ||||
| 		goto err1; | ||||
| 
 | ||||
| 	for (k = 0; k < pfc->num_resources; k++) { | ||||
| 		res = pfc->resource + k; | ||||
| 		WARN_ON(resource_type(res) != IORESOURCE_MEM); | ||||
| 		pfc->window[k].phys = res->start; | ||||
| 		pfc->window[k].size = resource_size(res); | ||||
| 		pfc->window[k].virt = ioremap_nocache(res->start, | ||||
| 							 resource_size(res)); | ||||
| 		if (!pfc->window[k].virt) | ||||
| 			goto err2; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| err2: | ||||
| 	pfc_iounmap(pfc); | ||||
| err1: | ||||
| 	return -1; | ||||
| } | ||||
| 
 | ||||
| static void __iomem *pfc_phys_to_virt(struct sh_pfc *pfc, | ||||
| 				      unsigned long address) | ||||
| { | ||||
| 	struct pfc_window *window; | ||||
| 	int k; | ||||
| 
 | ||||
| 	/* scan through physical windows and convert address */ | ||||
| 	for (k = 0; k < pfc->num_resources; k++) { | ||||
| 		window = pfc->window + k; | ||||
| 
 | ||||
| 		if (address < window->phys) | ||||
| 			continue; | ||||
| 
 | ||||
| 		if (address >= (window->phys + window->size)) | ||||
| 			continue; | ||||
| 
 | ||||
| 		return window->virt + (address - window->phys); | ||||
| 	} | ||||
| 
 | ||||
| 	/* no windows defined, register must be 1:1 mapped virt:phys */ | ||||
| 	return (void __iomem *)address; | ||||
| } | ||||
| 
 | ||||
| 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(void __iomem *mapped_reg, | ||||
| 				       unsigned long reg_width) | ||||
| { | ||||
| 	switch (reg_width) { | ||||
| 	case 8: | ||||
| 		return ioread8(mapped_reg); | ||||
| 	case 16: | ||||
| 		return ioread16(mapped_reg); | ||||
| 	case 32: | ||||
| 		return ioread32(mapped_reg); | ||||
| 	} | ||||
| 
 | ||||
| 	BUG(); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void gpio_write_raw_reg(void __iomem *mapped_reg, | ||||
| 			       unsigned long reg_width, | ||||
| 			       unsigned long data) | ||||
| { | ||||
| 	switch (reg_width) { | ||||
| 	case 8: | ||||
| 		iowrite8(data, mapped_reg); | ||||
| 		return; | ||||
| 	case 16: | ||||
| 		iowrite16(data, mapped_reg); | ||||
| 		return; | ||||
| 	case 32: | ||||
| 		iowrite32(data, mapped_reg); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	BUG(); | ||||
| } | ||||
| 
 | ||||
| int sh_pfc_read_bit(struct pinmux_data_reg *dr, unsigned long in_pos) | ||||
| { | ||||
| 	unsigned long pos; | ||||
| 
 | ||||
| 	pos = dr->reg_width - (in_pos + 1); | ||||
| 
 | ||||
| 	pr_debug("read_bit: addr = %lx, pos = %ld, " | ||||
| 		 "r_width = %ld\n", dr->reg, pos, dr->reg_width); | ||||
| 
 | ||||
| 	return (gpio_read_raw_reg(dr->mapped_reg, dr->reg_width) >> pos) & 1; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(sh_pfc_read_bit); | ||||
| 
 | ||||
| void sh_pfc_write_bit(struct pinmux_data_reg *dr, unsigned long in_pos, | ||||
| 		      unsigned long value) | ||||
| { | ||||
| 	unsigned long pos; | ||||
| 
 | ||||
| 	pos = dr->reg_width - (in_pos + 1); | ||||
| 
 | ||||
| 	pr_debug("write_bit addr = %lx, value = %d, pos = %ld, " | ||||
| 		 "r_width = %ld\n", | ||||
| 		 dr->reg, !!value, pos, dr->reg_width); | ||||
| 
 | ||||
| 	if (value) | ||||
| 		set_bit(pos, &dr->reg_shadow); | ||||
| 	else | ||||
| 		clear_bit(pos, &dr->reg_shadow); | ||||
| 
 | ||||
| 	gpio_write_raw_reg(dr->mapped_reg, dr->reg_width, dr->reg_shadow); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(sh_pfc_write_bit); | ||||
| 
 | ||||
| static void config_reg_helper(struct sh_pfc *pfc, | ||||
| 			      struct pinmux_cfg_reg *crp, | ||||
| 			      unsigned long in_pos, | ||||
| 			      void __iomem **mapped_regp, | ||||
| 			      unsigned long *maskp, | ||||
| 			      unsigned long *posp) | ||||
| { | ||||
| 	int k; | ||||
| 
 | ||||
| 	*mapped_regp = pfc_phys_to_virt(pfc, crp->reg); | ||||
| 
 | ||||
| 	if (crp->field_width) { | ||||
| 		*maskp = (1 << crp->field_width) - 1; | ||||
| 		*posp = crp->reg_width - ((in_pos + 1) * crp->field_width); | ||||
| 	} else { | ||||
| 		*maskp = (1 << crp->var_field_width[in_pos]) - 1; | ||||
| 		*posp = crp->reg_width; | ||||
| 		for (k = 0; k <= in_pos; k++) | ||||
| 			*posp -= crp->var_field_width[k]; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static int read_config_reg(struct sh_pfc *pfc, | ||||
| 			   struct pinmux_cfg_reg *crp, | ||||
| 			   unsigned long field) | ||||
| { | ||||
| 	void __iomem *mapped_reg; | ||||
| 	unsigned long mask, pos; | ||||
| 
 | ||||
| 	config_reg_helper(pfc, crp, field, &mapped_reg, &mask, &pos); | ||||
| 
 | ||||
| 	pr_debug("read_reg: addr = %lx, field = %ld, " | ||||
| 		 "r_width = %ld, f_width = %ld\n", | ||||
| 		 crp->reg, field, crp->reg_width, crp->field_width); | ||||
| 
 | ||||
| 	return (gpio_read_raw_reg(mapped_reg, crp->reg_width) >> pos) & mask; | ||||
| } | ||||
| 
 | ||||
| static void write_config_reg(struct sh_pfc *pfc, | ||||
| 			     struct pinmux_cfg_reg *crp, | ||||
| 			     unsigned long field, unsigned long value) | ||||
| { | ||||
| 	void __iomem *mapped_reg; | ||||
| 	unsigned long mask, pos, data; | ||||
| 
 | ||||
| 	config_reg_helper(pfc, crp, field, &mapped_reg, &mask, &pos); | ||||
| 
 | ||||
| 	pr_debug("write_reg addr = %lx, value = %ld, field = %ld, " | ||||
| 		 "r_width = %ld, f_width = %ld\n", | ||||
| 		 crp->reg, value, field, crp->reg_width, crp->field_width); | ||||
| 
 | ||||
| 	mask = ~(mask << pos); | ||||
| 	value = value << pos; | ||||
| 
 | ||||
| 	data = gpio_read_raw_reg(mapped_reg, crp->reg_width); | ||||
| 	data &= mask; | ||||
| 	data |= value; | ||||
| 
 | ||||
| 	if (pfc->unlock_reg) | ||||
| 		gpio_write_raw_reg(pfc_phys_to_virt(pfc, pfc->unlock_reg), | ||||
| 				   32, ~data); | ||||
| 
 | ||||
| 	gpio_write_raw_reg(mapped_reg, crp->reg_width, data); | ||||
| } | ||||
| 
 | ||||
| static int setup_data_reg(struct sh_pfc *pfc, unsigned gpio) | ||||
| { | ||||
| 	struct pinmux_gpio *gpiop = &pfc->gpios[gpio]; | ||||
| 	struct pinmux_data_reg *data_reg; | ||||
| 	int k, n; | ||||
| 
 | ||||
| 	if (!enum_in_range(gpiop->enum_id, &pfc->data)) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	k = 0; | ||||
| 	while (1) { | ||||
| 		data_reg = pfc->data_regs + k; | ||||
| 
 | ||||
| 		if (!data_reg->reg_width) | ||||
| 			break; | ||||
| 
 | ||||
| 		data_reg->mapped_reg = pfc_phys_to_virt(pfc, data_reg->reg); | ||||
| 
 | ||||
| 		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 sh_pfc *pfc) | ||||
| { | ||||
| 	struct pinmux_data_reg *drp; | ||||
| 	int k; | ||||
| 
 | ||||
| 	for (k = pfc->first_gpio; k <= pfc->last_gpio; k++) | ||||
| 		setup_data_reg(pfc, k); | ||||
| 
 | ||||
| 	k = 0; | ||||
| 	while (1) { | ||||
| 		drp = pfc->data_regs + k; | ||||
| 
 | ||||
| 		if (!drp->reg_width) | ||||
| 			break; | ||||
| 
 | ||||
| 		drp->reg_shadow = gpio_read_raw_reg(drp->mapped_reg, | ||||
| 						    drp->reg_width); | ||||
| 		k++; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| int sh_pfc_get_data_reg(struct sh_pfc *pfc, unsigned gpio, | ||||
| 			struct pinmux_data_reg **drp, int *bitp) | ||||
| { | ||||
| 	struct pinmux_gpio *gpiop = &pfc->gpios[gpio]; | ||||
| 	int k, n; | ||||
| 
 | ||||
| 	if (!enum_in_range(gpiop->enum_id, &pfc->data)) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	k = (gpiop->flags & PINMUX_FLAG_DREG) >> PINMUX_FLAG_DREG_SHIFT; | ||||
| 	n = (gpiop->flags & PINMUX_FLAG_DBIT) >> PINMUX_FLAG_DBIT_SHIFT; | ||||
| 	*drp = pfc->data_regs + k; | ||||
| 	*bitp = n; | ||||
| 	return 0; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(sh_pfc_get_data_reg); | ||||
| 
 | ||||
| static int get_config_reg(struct sh_pfc *pfc, pinmux_enum_t enum_id, | ||||
| 			  struct pinmux_cfg_reg **crp, | ||||
| 			  int *fieldp, int *valuep, | ||||
| 			  unsigned long **cntp) | ||||
| { | ||||
| 	struct pinmux_cfg_reg *config_reg; | ||||
| 	unsigned long r_width, f_width, curr_width, ncomb; | ||||
| 	int k, m, n, pos, bit_pos; | ||||
| 
 | ||||
| 	k = 0; | ||||
| 	while (1) { | ||||
| 		config_reg = pfc->cfg_regs + k; | ||||
| 
 | ||||
| 		r_width = config_reg->reg_width; | ||||
| 		f_width = config_reg->field_width; | ||||
| 
 | ||||
| 		if (!r_width) | ||||
| 			break; | ||||
| 
 | ||||
| 		pos = 0; | ||||
| 		m = 0; | ||||
| 		for (bit_pos = 0; bit_pos < r_width; bit_pos += curr_width) { | ||||
| 			if (f_width) | ||||
| 				curr_width = f_width; | ||||
| 			else | ||||
| 				curr_width = config_reg->var_field_width[m]; | ||||
| 
 | ||||
| 			ncomb = 1 << curr_width; | ||||
| 			for (n = 0; n < ncomb; n++) { | ||||
| 				if (config_reg->enum_ids[pos + n] == enum_id) { | ||||
| 					*crp = config_reg; | ||||
| 					*fieldp = m; | ||||
| 					*valuep = n; | ||||
| 					*cntp = &config_reg->cnt[m]; | ||||
| 					return 0; | ||||
| 				} | ||||
| 			} | ||||
| 			pos += ncomb; | ||||
| 			m++; | ||||
| 		} | ||||
| 		k++; | ||||
| 	} | ||||
| 
 | ||||
| 	return -1; | ||||
| } | ||||
| 
 | ||||
| int sh_pfc_gpio_to_enum(struct sh_pfc *pfc, unsigned gpio, int pos, | ||||
| 			pinmux_enum_t *enum_idp) | ||||
| { | ||||
| 	pinmux_enum_t enum_id = pfc->gpios[gpio].enum_id; | ||||
| 	pinmux_enum_t *data = pfc->gpio_data; | ||||
| 	int k; | ||||
| 
 | ||||
| 	if (!enum_in_range(enum_id, &pfc->data)) { | ||||
| 		if (!enum_in_range(enum_id, &pfc->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 < pfc->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; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(sh_pfc_gpio_to_enum); | ||||
| 
 | ||||
| int sh_pfc_config_gpio(struct sh_pfc *pfc, 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, field, value; | ||||
| 	unsigned long *cntp; | ||||
| 
 | ||||
| 	switch (pinmux_type) { | ||||
| 
 | ||||
| 	case PINMUX_TYPE_FUNCTION: | ||||
| 		range = NULL; | ||||
| 		break; | ||||
| 
 | ||||
| 	case PINMUX_TYPE_OUTPUT: | ||||
| 		range = &pfc->output; | ||||
| 		break; | ||||
| 
 | ||||
| 	case PINMUX_TYPE_INPUT: | ||||
| 		range = &pfc->input; | ||||
| 		break; | ||||
| 
 | ||||
| 	case PINMUX_TYPE_INPUT_PULLUP: | ||||
| 		range = &pfc->input_pu; | ||||
| 		break; | ||||
| 
 | ||||
| 	case PINMUX_TYPE_INPUT_PULLDOWN: | ||||
| 		range = &pfc->input_pd; | ||||
| 		break; | ||||
| 
 | ||||
| 	default: | ||||
| 		goto out_err; | ||||
| 	} | ||||
| 
 | ||||
| 	pos = 0; | ||||
| 	enum_id = 0; | ||||
| 	field = 0; | ||||
| 	value = 0; | ||||
| 	while (1) { | ||||
| 		pos = sh_pfc_gpio_to_enum(pfc, gpio, pos, &enum_id); | ||||
| 		if (pos <= 0) | ||||
| 			goto out_err; | ||||
| 
 | ||||
| 		if (!enum_id) | ||||
| 			break; | ||||
| 
 | ||||
| 		/* first check if this is a function enum */ | ||||
| 		in_range = enum_in_range(enum_id, &pfc->function); | ||||
| 		if (!in_range) { | ||||
| 			/* not a function enum */ | ||||
| 			if (range) { | ||||
| 				/*
 | ||||
| 				 * other range exists, so this pin is | ||||
| 				 * a regular GPIO pin that now is being | ||||
| 				 * bound to a specific direction. | ||||
| 				 * | ||||
| 				 * for this case we only allow function enums | ||||
| 				 * and the enums that match the other range. | ||||
| 				 */ | ||||
| 				in_range = enum_in_range(enum_id, range); | ||||
| 
 | ||||
| 				/*
 | ||||
| 				 * special case pass through for fixed | ||||
| 				 * input-only or output-only pins without | ||||
| 				 * function enum register association. | ||||
| 				 */ | ||||
| 				if (in_range && enum_id == range->force) | ||||
| 					continue; | ||||
| 			} else { | ||||
| 				/*
 | ||||
| 				 * no other range exists, so this pin | ||||
| 				 * must then be of the function type. | ||||
| 				 * | ||||
| 				 * allow function type pins to select | ||||
| 				 * any combination of function/in/out | ||||
| 				 * in their MARK lists. | ||||
| 				 */ | ||||
| 				in_range = 1; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if (!in_range) | ||||
| 			continue; | ||||
| 
 | ||||
| 		if (get_config_reg(pfc, enum_id, &cr, | ||||
| 				   &field, &value, &cntp) != 0) | ||||
| 			goto out_err; | ||||
| 
 | ||||
| 		switch (cfg_mode) { | ||||
| 		case GPIO_CFG_DRYRUN: | ||||
| 			if (!*cntp || | ||||
| 			    (read_config_reg(pfc, cr, field) != value)) | ||||
| 				continue; | ||||
| 			break; | ||||
| 
 | ||||
| 		case GPIO_CFG_REQ: | ||||
| 			write_config_reg(pfc, cr, field, value); | ||||
| 			*cntp = *cntp + 1; | ||||
| 			break; | ||||
| 
 | ||||
| 		case GPIO_CFG_FREE: | ||||
| 			*cntp = *cntp - 1; | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
|  out_err: | ||||
| 	return -1; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(sh_pfc_config_gpio); | ||||
| 
 | ||||
| int register_sh_pfc(struct sh_pfc *pfc) | ||||
| { | ||||
| 	int (*initroutine)(struct sh_pfc *) = NULL; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Ensure that the type encoding fits | ||||
| 	 */ | ||||
| 	BUILD_BUG_ON(PINMUX_FLAG_TYPE > ((1 << PINMUX_FLAG_DBIT_SHIFT) - 1)); | ||||
| 
 | ||||
| 	if (sh_pfc) | ||||
| 		return -EBUSY; | ||||
| 
 | ||||
| 	ret = pfc_ioremap(pfc); | ||||
| 	if (unlikely(ret < 0)) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	spin_lock_init(&pfc->lock); | ||||
| 
 | ||||
| 	pinctrl_provide_dummies(); | ||||
| 	setup_data_regs(pfc); | ||||
| 
 | ||||
| 	sh_pfc = pfc; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Initialize pinctrl bindings first | ||||
| 	 */ | ||||
| 	initroutine = symbol_request(sh_pfc_register_pinctrl); | ||||
| 	if (initroutine) { | ||||
| 		ret = (*initroutine)(pfc); | ||||
| 		symbol_put_addr(initroutine); | ||||
| 
 | ||||
| 		if (unlikely(ret != 0)) | ||||
| 			goto err; | ||||
| 	} else { | ||||
| 		pr_err("failed to initialize pinctrl bindings\n"); | ||||
| 		goto err; | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Then the GPIO chip | ||||
| 	 */ | ||||
| 	initroutine = symbol_request(sh_pfc_register_gpiochip); | ||||
| 	if (initroutine) { | ||||
| 		ret = (*initroutine)(pfc); | ||||
| 		symbol_put_addr(initroutine); | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * If the GPIO chip fails to come up we still leave the | ||||
| 		 * PFC state as it is, given that there are already | ||||
| 		 * extant users of it that have succeeded by this point. | ||||
| 		 */ | ||||
| 		if (unlikely(ret != 0)) { | ||||
| 			pr_notice("failed to init GPIO chip, ignoring...\n"); | ||||
| 			ret = 0; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	pr_info("%s support registered\n", pfc->name); | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| err: | ||||
| 	pfc_iounmap(pfc); | ||||
| 	sh_pfc = NULL; | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
							
								
								
									
										239
									
								
								drivers/sh/pfc/gpio.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										239
									
								
								drivers/sh/pfc/gpio.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,239 @@ | ||||
| /*
 | ||||
|  * SuperH Pin Function Controller GPIO driver. | ||||
|  * | ||||
|  * Copyright (C) 2008 Magnus Damm | ||||
|  * Copyright (C) 2009 - 2012 Paul Mundt | ||||
|  * | ||||
|  * 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. | ||||
|  */ | ||||
| #define pr_fmt(fmt) "sh_pfc " KBUILD_MODNAME ": " fmt | ||||
| 
 | ||||
| #include <linux/init.h> | ||||
| #include <linux/gpio.h> | ||||
| #include <linux/slab.h> | ||||
| #include <linux/spinlock.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/pinctrl/consumer.h> | ||||
| 
 | ||||
| struct sh_pfc_chip { | ||||
| 	struct sh_pfc		*pfc; | ||||
| 	struct gpio_chip	gpio_chip; | ||||
| }; | ||||
| 
 | ||||
| static struct sh_pfc_chip *gpio_to_pfc_chip(struct gpio_chip *gc) | ||||
| { | ||||
| 	return container_of(gc, struct sh_pfc_chip, gpio_chip); | ||||
| } | ||||
| 
 | ||||
| static struct sh_pfc *gpio_to_pfc(struct gpio_chip *gc) | ||||
| { | ||||
| 	return gpio_to_pfc_chip(gc)->pfc; | ||||
| } | ||||
| 
 | ||||
| static int sh_gpio_request(struct gpio_chip *gc, unsigned offset) | ||||
| { | ||||
| 	return pinctrl_request_gpio(offset); | ||||
| } | ||||
| 
 | ||||
| static void sh_gpio_free(struct gpio_chip *gc, unsigned offset) | ||||
| { | ||||
| 	pinctrl_free_gpio(offset); | ||||
| } | ||||
| 
 | ||||
| static void sh_gpio_set_value(struct sh_pfc *pfc, unsigned gpio, int value) | ||||
| { | ||||
| 	struct pinmux_data_reg *dr = NULL; | ||||
| 	int bit = 0; | ||||
| 
 | ||||
| 	if (!pfc || sh_pfc_get_data_reg(pfc, gpio, &dr, &bit) != 0) | ||||
| 		BUG(); | ||||
| 	else | ||||
| 		sh_pfc_write_bit(dr, bit, value); | ||||
| } | ||||
| 
 | ||||
| static int sh_gpio_get_value(struct sh_pfc *pfc, unsigned gpio) | ||||
| { | ||||
| 	struct pinmux_data_reg *dr = NULL; | ||||
| 	int bit = 0; | ||||
| 
 | ||||
| 	if (!pfc || sh_pfc_get_data_reg(pfc, gpio, &dr, &bit) != 0) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	return sh_pfc_read_bit(dr, bit); | ||||
| } | ||||
| 
 | ||||
| static int sh_gpio_direction_input(struct gpio_chip *gc, unsigned offset) | ||||
| { | ||||
| 	return pinctrl_gpio_direction_input(offset); | ||||
| } | ||||
| 
 | ||||
| static int sh_gpio_direction_output(struct gpio_chip *gc, unsigned offset, | ||||
| 				    int value) | ||||
| { | ||||
| 	sh_gpio_set_value(gpio_to_pfc(gc), offset, value); | ||||
| 
 | ||||
| 	return pinctrl_gpio_direction_output(offset); | ||||
| } | ||||
| 
 | ||||
| static int sh_gpio_get(struct gpio_chip *gc, unsigned offset) | ||||
| { | ||||
| 	return sh_gpio_get_value(gpio_to_pfc(gc), offset); | ||||
| } | ||||
| 
 | ||||
| static void sh_gpio_set(struct gpio_chip *gc, unsigned offset, int value) | ||||
| { | ||||
| 	sh_gpio_set_value(gpio_to_pfc(gc), offset, value); | ||||
| } | ||||
| 
 | ||||
| static int sh_gpio_to_irq(struct gpio_chip *gc, unsigned offset) | ||||
| { | ||||
| 	struct sh_pfc *pfc = gpio_to_pfc(gc); | ||||
| 	pinmux_enum_t enum_id; | ||||
| 	pinmux_enum_t *enum_ids; | ||||
| 	int i, k, pos; | ||||
| 
 | ||||
| 	pos = 0; | ||||
| 	enum_id = 0; | ||||
| 	while (1) { | ||||
| 		pos = sh_pfc_gpio_to_enum(pfc, offset, pos, &enum_id); | ||||
| 		if (pos <= 0 || !enum_id) | ||||
| 			break; | ||||
| 
 | ||||
| 		for (i = 0; i < pfc->gpio_irq_size; i++) { | ||||
| 			enum_ids = pfc->gpio_irq[i].enum_ids; | ||||
| 			for (k = 0; enum_ids[k]; k++) { | ||||
| 				if (enum_ids[k] == enum_id) | ||||
| 					return pfc->gpio_irq[i].irq; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return -ENOSYS; | ||||
| } | ||||
| 
 | ||||
| static void sh_pfc_gpio_setup(struct sh_pfc_chip *chip) | ||||
| { | ||||
| 	struct sh_pfc *pfc = chip->pfc; | ||||
| 	struct gpio_chip *gc = &chip->gpio_chip; | ||||
| 
 | ||||
| 	gc->request = sh_gpio_request; | ||||
| 	gc->free = sh_gpio_free; | ||||
| 	gc->direction_input = sh_gpio_direction_input; | ||||
| 	gc->get = sh_gpio_get; | ||||
| 	gc->direction_output = sh_gpio_direction_output; | ||||
| 	gc->set = sh_gpio_set; | ||||
| 	gc->to_irq = sh_gpio_to_irq; | ||||
| 
 | ||||
| 	WARN_ON(pfc->first_gpio != 0); /* needs testing */ | ||||
| 
 | ||||
| 	gc->label = pfc->name; | ||||
| 	gc->owner = THIS_MODULE; | ||||
| 	gc->base = pfc->first_gpio; | ||||
| 	gc->ngpio = (pfc->last_gpio - pfc->first_gpio) + 1; | ||||
| } | ||||
| 
 | ||||
| int sh_pfc_register_gpiochip(struct sh_pfc *pfc) | ||||
| { | ||||
| 	struct sh_pfc_chip *chip; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	chip = kzalloc(sizeof(struct sh_pfc_chip), GFP_KERNEL); | ||||
| 	if (unlikely(!chip)) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	chip->pfc = pfc; | ||||
| 
 | ||||
| 	sh_pfc_gpio_setup(chip); | ||||
| 
 | ||||
| 	ret = gpiochip_add(&chip->gpio_chip); | ||||
| 	if (unlikely(ret < 0)) | ||||
| 		kfree(chip); | ||||
| 
 | ||||
| 	pr_info("%s handling gpio %d -> %d\n", | ||||
| 		pfc->name, pfc->first_gpio, pfc->last_gpio); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(sh_pfc_register_gpiochip); | ||||
| 
 | ||||
| static int sh_pfc_gpio_match(struct gpio_chip *gc, void *data) | ||||
| { | ||||
| 	return !!strstr(gc->label, data); | ||||
| } | ||||
| 
 | ||||
| static int __devinit sh_pfc_gpio_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	struct sh_pfc_chip *chip; | ||||
| 	struct gpio_chip *gc; | ||||
| 
 | ||||
| 	gc = gpiochip_find("_pfc", sh_pfc_gpio_match); | ||||
| 	if (unlikely(!gc)) { | ||||
| 		pr_err("Cant find gpio chip\n"); | ||||
| 		return -ENODEV; | ||||
| 	} | ||||
| 
 | ||||
| 	chip = gpio_to_pfc_chip(gc); | ||||
| 	platform_set_drvdata(pdev, chip); | ||||
| 
 | ||||
| 	pr_info("attaching to GPIO chip %s\n", chip->pfc->name); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int __devexit sh_pfc_gpio_remove(struct platform_device *pdev) | ||||
| { | ||||
| 	struct sh_pfc_chip *chip = platform_get_drvdata(pdev); | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = gpiochip_remove(&chip->gpio_chip); | ||||
| 	if (unlikely(ret < 0)) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	kfree(chip); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static struct platform_driver sh_pfc_gpio_driver = { | ||||
| 	.probe		= sh_pfc_gpio_probe, | ||||
| 	.remove		= __devexit_p(sh_pfc_gpio_remove), | ||||
| 	.driver		= { | ||||
| 		.name	= KBUILD_MODNAME, | ||||
| 		.owner	= THIS_MODULE, | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| static struct platform_device sh_pfc_gpio_device = { | ||||
| 	.name		= KBUILD_MODNAME, | ||||
| 	.id		= -1, | ||||
| }; | ||||
| 
 | ||||
| static int __init sh_pfc_gpio_init(void) | ||||
| { | ||||
| 	int rc; | ||||
| 
 | ||||
| 	rc = platform_driver_register(&sh_pfc_gpio_driver); | ||||
| 	if (likely(!rc)) { | ||||
| 		rc = platform_device_register(&sh_pfc_gpio_device); | ||||
| 		if (unlikely(rc)) | ||||
| 			platform_driver_unregister(&sh_pfc_gpio_driver); | ||||
| 	} | ||||
| 
 | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| static void __exit sh_pfc_gpio_exit(void) | ||||
| { | ||||
| 	platform_device_unregister(&sh_pfc_gpio_device); | ||||
| 	platform_driver_unregister(&sh_pfc_gpio_driver); | ||||
| } | ||||
| 
 | ||||
| module_init(sh_pfc_gpio_init); | ||||
| module_exit(sh_pfc_gpio_exit); | ||||
| 
 | ||||
| MODULE_AUTHOR("Magnus Damm, Paul Mundt"); | ||||
| MODULE_DESCRIPTION("GPIO driver for SuperH pin function controller"); | ||||
| MODULE_LICENSE("GPL v2"); | ||||
| MODULE_ALIAS("platform:pfc-gpio"); | ||||
							
								
								
									
										530
									
								
								drivers/sh/pfc/pinctrl.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										530
									
								
								drivers/sh/pfc/pinctrl.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,530 @@ | ||||
| /*
 | ||||
|  * SuperH Pin Function Controller pinmux support. | ||||
|  * | ||||
|  * Copyright (C) 2012  Paul Mundt | ||||
|  * | ||||
|  * 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. | ||||
|  */ | ||||
| #define DRV_NAME "pinctrl-sh_pfc" | ||||
| 
 | ||||
| #define pr_fmt(fmt) DRV_NAME " " KBUILD_MODNAME ": " fmt | ||||
| 
 | ||||
| #include <linux/init.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/sh_pfc.h> | ||||
| #include <linux/err.h> | ||||
| #include <linux/slab.h> | ||||
| #include <linux/spinlock.h> | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/pinctrl/consumer.h> | ||||
| #include <linux/pinctrl/pinctrl.h> | ||||
| #include <linux/pinctrl/pinconf.h> | ||||
| #include <linux/pinctrl/pinmux.h> | ||||
| #include <linux/pinctrl/pinconf-generic.h> | ||||
| 
 | ||||
| struct sh_pfc_pinctrl { | ||||
| 	struct pinctrl_dev *pctl; | ||||
| 	struct sh_pfc *pfc; | ||||
| 
 | ||||
| 	struct pinmux_gpio **functions; | ||||
| 	unsigned int nr_functions; | ||||
| 
 | ||||
| 	struct pinctrl_pin_desc *pads; | ||||
| 	unsigned int nr_pads; | ||||
| 
 | ||||
| 	spinlock_t lock; | ||||
| }; | ||||
| 
 | ||||
| static struct sh_pfc_pinctrl *sh_pfc_pmx; | ||||
| 
 | ||||
| static int sh_pfc_get_groups_count(struct pinctrl_dev *pctldev) | ||||
| { | ||||
| 	struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); | ||||
| 
 | ||||
| 	return pmx->nr_pads; | ||||
| } | ||||
| 
 | ||||
| static const char *sh_pfc_get_group_name(struct pinctrl_dev *pctldev, | ||||
| 					 unsigned selector) | ||||
| { | ||||
| 	struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); | ||||
| 
 | ||||
| 	return pmx->pads[selector].name; | ||||
| } | ||||
| 
 | ||||
| static int sh_pfc_get_group_pins(struct pinctrl_dev *pctldev, unsigned group, | ||||
| 				 const unsigned **pins, unsigned *num_pins) | ||||
| { | ||||
| 	struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); | ||||
| 
 | ||||
| 	*pins = &pmx->pads[group].number; | ||||
| 	*num_pins = 1; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void sh_pfc_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, | ||||
| 				unsigned offset) | ||||
| { | ||||
| 	seq_printf(s, "%s", DRV_NAME); | ||||
| } | ||||
| 
 | ||||
| static struct pinctrl_ops sh_pfc_pinctrl_ops = { | ||||
| 	.get_groups_count	= sh_pfc_get_groups_count, | ||||
| 	.get_group_name		= sh_pfc_get_group_name, | ||||
| 	.get_group_pins		= sh_pfc_get_group_pins, | ||||
| 	.pin_dbg_show		= sh_pfc_pin_dbg_show, | ||||
| }; | ||||
| 
 | ||||
| static int sh_pfc_get_functions_count(struct pinctrl_dev *pctldev) | ||||
| { | ||||
| 	struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); | ||||
| 
 | ||||
| 	return pmx->nr_functions; | ||||
| } | ||||
| 
 | ||||
| static const char *sh_pfc_get_function_name(struct pinctrl_dev *pctldev, | ||||
| 					    unsigned selector) | ||||
| { | ||||
| 	struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); | ||||
| 
 | ||||
| 	return pmx->functions[selector]->name; | ||||
| } | ||||
| 
 | ||||
| static int sh_pfc_get_function_groups(struct pinctrl_dev *pctldev, unsigned func, | ||||
| 				      const char * const **groups, | ||||
| 				      unsigned * const num_groups) | ||||
| { | ||||
| 	struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); | ||||
| 
 | ||||
| 	*groups = &pmx->functions[func]->name; | ||||
| 	*num_groups = 1; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int sh_pfc_noop_enable(struct pinctrl_dev *pctldev, unsigned func, | ||||
| 			      unsigned group) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void sh_pfc_noop_disable(struct pinctrl_dev *pctldev, unsigned func, | ||||
| 				unsigned group) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| static inline int sh_pfc_config_function(struct sh_pfc *pfc, unsigned offset) | ||||
| { | ||||
| 	if (sh_pfc_config_gpio(pfc, offset, | ||||
| 			       PINMUX_TYPE_FUNCTION, | ||||
| 			       GPIO_CFG_DRYRUN) != 0) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (sh_pfc_config_gpio(pfc, offset, | ||||
| 			       PINMUX_TYPE_FUNCTION, | ||||
| 			       GPIO_CFG_REQ) != 0) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int sh_pfc_reconfig_pin(struct sh_pfc *pfc, unsigned offset, | ||||
| 			       int new_type) | ||||
| { | ||||
| 	unsigned long flags; | ||||
| 	int pinmux_type; | ||||
| 	int ret = -EINVAL; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&pfc->lock, flags); | ||||
| 
 | ||||
| 	pinmux_type = pfc->gpios[offset].flags & PINMUX_FLAG_TYPE; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * See if the present config needs to first be de-configured. | ||||
| 	 */ | ||||
| 	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: | ||||
| 		sh_pfc_config_gpio(pfc, offset, pinmux_type, GPIO_CFG_FREE); | ||||
| 		break; | ||||
| 	default: | ||||
| 		goto err; | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Dry run | ||||
| 	 */ | ||||
| 	if (sh_pfc_config_gpio(pfc, offset, new_type, | ||||
| 			       GPIO_CFG_DRYRUN) != 0) | ||||
| 		goto err; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Request | ||||
| 	 */ | ||||
| 	if (sh_pfc_config_gpio(pfc, offset, new_type, | ||||
| 			       GPIO_CFG_REQ) != 0) | ||||
| 		goto err; | ||||
| 
 | ||||
| 	pfc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; | ||||
| 	pfc->gpios[offset].flags |= new_type; | ||||
| 
 | ||||
| 	ret = 0; | ||||
| 
 | ||||
| err: | ||||
| 	spin_unlock_irqrestore(&pfc->lock, flags); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static int sh_pfc_gpio_request_enable(struct pinctrl_dev *pctldev, | ||||
| 				      struct pinctrl_gpio_range *range, | ||||
| 				      unsigned offset) | ||||
| { | ||||
| 	struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); | ||||
| 	struct sh_pfc *pfc = pmx->pfc; | ||||
| 	unsigned long flags; | ||||
| 	int ret, pinmux_type; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&pfc->lock, flags); | ||||
| 
 | ||||
| 	pinmux_type = pfc->gpios[offset].flags & PINMUX_FLAG_TYPE; | ||||
| 
 | ||||
| 	switch (pinmux_type) { | ||||
| 	case PINMUX_TYPE_FUNCTION: | ||||
| 		pr_notice_once("Use of GPIO API for function requests is " | ||||
| 			       "deprecated, convert to pinctrl\n"); | ||||
| 		/* handle for now */ | ||||
| 		ret = sh_pfc_config_function(pfc, offset); | ||||
| 		if (unlikely(ret < 0)) | ||||
| 			goto err; | ||||
| 
 | ||||
| 		break; | ||||
| 	case PINMUX_TYPE_GPIO: | ||||
| 		break; | ||||
| 	default: | ||||
| 		pr_err("Unsupported mux type (%d), bailing...\n", pinmux_type); | ||||
| 		return -ENOTSUPP; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = 0; | ||||
| 
 | ||||
| err: | ||||
| 	spin_unlock_irqrestore(&pfc->lock, flags); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static void sh_pfc_gpio_disable_free(struct pinctrl_dev *pctldev, | ||||
| 				     struct pinctrl_gpio_range *range, | ||||
| 				     unsigned offset) | ||||
| { | ||||
| 	struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); | ||||
| 	struct sh_pfc *pfc = pmx->pfc; | ||||
| 	unsigned long flags; | ||||
| 	int pinmux_type; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&pfc->lock, flags); | ||||
| 
 | ||||
| 	pinmux_type = pfc->gpios[offset].flags & PINMUX_FLAG_TYPE; | ||||
| 
 | ||||
| 	sh_pfc_config_gpio(pfc, offset, pinmux_type, GPIO_CFG_FREE); | ||||
| 
 | ||||
| 	spin_unlock_irqrestore(&pfc->lock, flags); | ||||
| } | ||||
| 
 | ||||
| static int sh_pfc_gpio_set_direction(struct pinctrl_dev *pctldev, | ||||
| 				     struct pinctrl_gpio_range *range, | ||||
| 				     unsigned offset, bool input) | ||||
| { | ||||
| 	struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); | ||||
| 	int type = input ? PINMUX_TYPE_INPUT : PINMUX_TYPE_OUTPUT; | ||||
| 
 | ||||
| 	return sh_pfc_reconfig_pin(pmx->pfc, offset, type); | ||||
| } | ||||
| 
 | ||||
| static struct pinmux_ops sh_pfc_pinmux_ops = { | ||||
| 	.get_functions_count	= sh_pfc_get_functions_count, | ||||
| 	.get_function_name	= sh_pfc_get_function_name, | ||||
| 	.get_function_groups	= sh_pfc_get_function_groups, | ||||
| 	.enable			= sh_pfc_noop_enable, | ||||
| 	.disable		= sh_pfc_noop_disable, | ||||
| 	.gpio_request_enable	= sh_pfc_gpio_request_enable, | ||||
| 	.gpio_disable_free	= sh_pfc_gpio_disable_free, | ||||
| 	.gpio_set_direction	= sh_pfc_gpio_set_direction, | ||||
| }; | ||||
| 
 | ||||
| static int sh_pfc_pinconf_get(struct pinctrl_dev *pctldev, unsigned pin, | ||||
| 			      unsigned long *config) | ||||
| { | ||||
| 	struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); | ||||
| 	struct sh_pfc *pfc = pmx->pfc; | ||||
| 
 | ||||
| 	*config = pfc->gpios[pin].flags & PINMUX_FLAG_TYPE; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int sh_pfc_pinconf_set(struct pinctrl_dev *pctldev, unsigned pin, | ||||
| 			      unsigned long config) | ||||
| { | ||||
| 	struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); | ||||
| 	struct sh_pfc *pfc = pmx->pfc; | ||||
| 
 | ||||
| 	/* Validate the new type */ | ||||
| 	if (config >= PINMUX_FLAG_TYPE) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	return sh_pfc_reconfig_pin(pmx->pfc, pin, config); | ||||
| } | ||||
| 
 | ||||
| static void sh_pfc_pinconf_dbg_show(struct pinctrl_dev *pctldev, | ||||
| 				    struct seq_file *s, unsigned pin) | ||||
| { | ||||
| 	const char *pinmux_type_str[] = { | ||||
| 		[PINMUX_TYPE_NONE]		= "none", | ||||
| 		[PINMUX_TYPE_FUNCTION]		= "function", | ||||
| 		[PINMUX_TYPE_GPIO]		= "gpio", | ||||
| 		[PINMUX_TYPE_OUTPUT]		= "output", | ||||
| 		[PINMUX_TYPE_INPUT]		= "input", | ||||
| 		[PINMUX_TYPE_INPUT_PULLUP]	= "input bias pull up", | ||||
| 		[PINMUX_TYPE_INPUT_PULLDOWN]	= "input bias pull down", | ||||
| 	}; | ||||
| 	unsigned long config; | ||||
| 	int rc; | ||||
| 
 | ||||
| 	rc = sh_pfc_pinconf_get(pctldev, pin, &config); | ||||
| 	if (unlikely(rc != 0)) | ||||
| 		return; | ||||
| 
 | ||||
| 	seq_printf(s, " %s", pinmux_type_str[config]); | ||||
| } | ||||
| 
 | ||||
| static struct pinconf_ops sh_pfc_pinconf_ops = { | ||||
| 	.pin_config_get		= sh_pfc_pinconf_get, | ||||
| 	.pin_config_set		= sh_pfc_pinconf_set, | ||||
| 	.pin_config_dbg_show	= sh_pfc_pinconf_dbg_show, | ||||
| }; | ||||
| 
 | ||||
| static struct pinctrl_gpio_range sh_pfc_gpio_range = { | ||||
| 	.name		= DRV_NAME, | ||||
| 	.id		= 0, | ||||
| }; | ||||
| 
 | ||||
| static struct pinctrl_desc sh_pfc_pinctrl_desc = { | ||||
| 	.name		= DRV_NAME, | ||||
| 	.owner		= THIS_MODULE, | ||||
| 	.pctlops	= &sh_pfc_pinctrl_ops, | ||||
| 	.pmxops		= &sh_pfc_pinmux_ops, | ||||
| 	.confops	= &sh_pfc_pinconf_ops, | ||||
| }; | ||||
| 
 | ||||
| int sh_pfc_register_pinctrl(struct sh_pfc *pfc) | ||||
| { | ||||
| 	sh_pfc_pmx = kzalloc(sizeof(struct sh_pfc_pinctrl), GFP_KERNEL); | ||||
| 	if (unlikely(!sh_pfc_pmx)) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	spin_lock_init(&sh_pfc_pmx->lock); | ||||
| 
 | ||||
| 	sh_pfc_pmx->pfc = pfc; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(sh_pfc_register_pinctrl); | ||||
| 
 | ||||
| static inline void __devinit sh_pfc_map_one_gpio(struct sh_pfc *pfc, | ||||
| 						 struct sh_pfc_pinctrl *pmx, | ||||
| 						 struct pinmux_gpio *gpio, | ||||
| 						 unsigned offset) | ||||
| { | ||||
| 	struct pinmux_data_reg *dummy; | ||||
| 	unsigned long flags; | ||||
| 	int bit; | ||||
| 
 | ||||
| 	gpio->flags &= ~PINMUX_FLAG_TYPE; | ||||
| 
 | ||||
| 	if (sh_pfc_get_data_reg(pfc, offset, &dummy, &bit) == 0) | ||||
| 		gpio->flags |= PINMUX_TYPE_GPIO; | ||||
| 	else { | ||||
| 		gpio->flags |= PINMUX_TYPE_FUNCTION; | ||||
| 
 | ||||
| 		spin_lock_irqsave(&pmx->lock, flags); | ||||
| 		pmx->nr_functions++; | ||||
| 		spin_unlock_irqrestore(&pmx->lock, flags); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* pinmux ranges -> pinctrl pin descs */ | ||||
| static int __devinit sh_pfc_map_gpios(struct sh_pfc *pfc, | ||||
| 				      struct sh_pfc_pinctrl *pmx) | ||||
| { | ||||
| 	unsigned long flags; | ||||
| 	int i; | ||||
| 
 | ||||
| 	pmx->nr_pads = pfc->last_gpio - pfc->first_gpio + 1; | ||||
| 
 | ||||
| 	pmx->pads = kmalloc(sizeof(struct pinctrl_pin_desc) * pmx->nr_pads, | ||||
| 			    GFP_KERNEL); | ||||
| 	if (unlikely(!pmx->pads)) { | ||||
| 		pmx->nr_pads = 0; | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 
 | ||||
| 	spin_lock_irqsave(&pfc->lock, flags); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * We don't necessarily have a 1:1 mapping between pin and linux | ||||
| 	 * GPIO number, as the latter maps to the associated enum_id. | ||||
| 	 * Care needs to be taken to translate back to pin space when | ||||
| 	 * dealing with any pin configurations. | ||||
| 	 */ | ||||
| 	for (i = 0; i < pmx->nr_pads; i++) { | ||||
| 		struct pinctrl_pin_desc *pin = pmx->pads + i; | ||||
| 		struct pinmux_gpio *gpio = pfc->gpios + i; | ||||
| 
 | ||||
| 		pin->number = pfc->first_gpio + i; | ||||
| 		pin->name = gpio->name; | ||||
| 
 | ||||
| 		/* XXX */ | ||||
| 		if (unlikely(!gpio->enum_id)) | ||||
| 			continue; | ||||
| 
 | ||||
| 		sh_pfc_map_one_gpio(pfc, pmx, gpio, i); | ||||
| 	} | ||||
| 
 | ||||
| 	spin_unlock_irqrestore(&pfc->lock, flags); | ||||
| 
 | ||||
| 	sh_pfc_pinctrl_desc.pins = pmx->pads; | ||||
| 	sh_pfc_pinctrl_desc.npins = pmx->nr_pads; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int __devinit sh_pfc_map_functions(struct sh_pfc *pfc, | ||||
| 					  struct sh_pfc_pinctrl *pmx) | ||||
| { | ||||
| 	unsigned long flags; | ||||
| 	int i, fn; | ||||
| 
 | ||||
| 	pmx->functions = kzalloc(pmx->nr_functions * sizeof(void *), | ||||
| 				 GFP_KERNEL); | ||||
| 	if (unlikely(!pmx->functions)) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&pmx->lock, flags); | ||||
| 
 | ||||
| 	for (i = fn = 0; i < pmx->nr_pads; i++) { | ||||
| 		struct pinmux_gpio *gpio = pfc->gpios + i; | ||||
| 
 | ||||
| 		if ((gpio->flags & PINMUX_FLAG_TYPE) == PINMUX_TYPE_FUNCTION) | ||||
| 			pmx->functions[fn++] = gpio; | ||||
| 	} | ||||
| 
 | ||||
| 	spin_unlock_irqrestore(&pmx->lock, flags); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int __devinit sh_pfc_pinctrl_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	struct sh_pfc *pfc; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	if (unlikely(!sh_pfc_pmx)) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	pfc = sh_pfc_pmx->pfc; | ||||
| 
 | ||||
| 	ret = sh_pfc_map_gpios(pfc, sh_pfc_pmx); | ||||
| 	if (unlikely(ret != 0)) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	ret = sh_pfc_map_functions(pfc, sh_pfc_pmx); | ||||
| 	if (unlikely(ret != 0)) | ||||
| 		goto free_pads; | ||||
| 
 | ||||
| 	sh_pfc_pmx->pctl = pinctrl_register(&sh_pfc_pinctrl_desc, &pdev->dev, | ||||
| 					    sh_pfc_pmx); | ||||
| 	if (IS_ERR(sh_pfc_pmx->pctl)) { | ||||
| 		ret = PTR_ERR(sh_pfc_pmx->pctl); | ||||
| 		goto free_functions; | ||||
| 	} | ||||
| 
 | ||||
| 	sh_pfc_gpio_range.npins = pfc->last_gpio - pfc->first_gpio + 1; | ||||
| 	sh_pfc_gpio_range.base = pfc->first_gpio; | ||||
| 	sh_pfc_gpio_range.pin_base = pfc->first_gpio; | ||||
| 
 | ||||
| 	pinctrl_add_gpio_range(sh_pfc_pmx->pctl, &sh_pfc_gpio_range); | ||||
| 
 | ||||
| 	platform_set_drvdata(pdev, sh_pfc_pmx); | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| free_functions: | ||||
| 	kfree(sh_pfc_pmx->functions); | ||||
| free_pads: | ||||
| 	kfree(sh_pfc_pmx->pads); | ||||
| 	kfree(sh_pfc_pmx); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int __devexit sh_pfc_pinctrl_remove(struct platform_device *pdev) | ||||
| { | ||||
| 	struct sh_pfc_pinctrl *pmx = platform_get_drvdata(pdev); | ||||
| 
 | ||||
| 	pinctrl_remove_gpio_range(pmx->pctl, &sh_pfc_gpio_range); | ||||
| 	pinctrl_unregister(pmx->pctl); | ||||
| 
 | ||||
| 	platform_set_drvdata(pdev, NULL); | ||||
| 
 | ||||
| 	kfree(sh_pfc_pmx->functions); | ||||
| 	kfree(sh_pfc_pmx->pads); | ||||
| 	kfree(sh_pfc_pmx); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static struct platform_driver sh_pfc_pinctrl_driver = { | ||||
| 	.probe		= sh_pfc_pinctrl_probe, | ||||
| 	.remove		= __devexit_p(sh_pfc_pinctrl_remove), | ||||
| 	.driver		= { | ||||
| 		.name	= DRV_NAME, | ||||
| 		.owner	= THIS_MODULE, | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| static struct platform_device sh_pfc_pinctrl_device = { | ||||
| 	.name		= DRV_NAME, | ||||
| 	.id		= -1, | ||||
| }; | ||||
| 
 | ||||
| static int __init sh_pfc_pinctrl_init(void) | ||||
| { | ||||
| 	int rc; | ||||
| 
 | ||||
| 	rc = platform_driver_register(&sh_pfc_pinctrl_driver); | ||||
| 	if (likely(!rc)) { | ||||
| 		rc = platform_device_register(&sh_pfc_pinctrl_device); | ||||
| 		if (unlikely(rc)) | ||||
| 			platform_driver_unregister(&sh_pfc_pinctrl_driver); | ||||
| 	} | ||||
| 
 | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| static void __exit sh_pfc_pinctrl_exit(void) | ||||
| { | ||||
| 	platform_driver_unregister(&sh_pfc_pinctrl_driver); | ||||
| } | ||||
| 
 | ||||
| subsys_initcall(sh_pfc_pinctrl_init); | ||||
| module_exit(sh_pfc_pinctrl_exit); | ||||
| @ -18,7 +18,6 @@ struct clk_mapping { | ||||
| 	struct kref		ref; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| struct sh_clk_ops { | ||||
| #ifdef CONFIG_SH_CLK_CPG_LEGACY | ||||
| 	void (*init)(struct clk *clk); | ||||
| @ -31,6 +30,10 @@ struct sh_clk_ops { | ||||
| 	long (*round_rate)(struct clk *clk, unsigned long rate); | ||||
| }; | ||||
| 
 | ||||
| #define SH_CLK_DIV_MSK(div)	((1 << (div)) - 1) | ||||
| #define SH_CLK_DIV4_MSK		SH_CLK_DIV_MSK(4) | ||||
| #define SH_CLK_DIV6_MSK		SH_CLK_DIV_MSK(6) | ||||
| 
 | ||||
| struct clk { | ||||
| 	struct list_head	node; | ||||
| 	struct clk		*parent; | ||||
| @ -52,6 +55,7 @@ struct clk { | ||||
| 	unsigned int		enable_bit; | ||||
| 	void __iomem		*mapped_reg; | ||||
| 
 | ||||
| 	unsigned int		div_mask; | ||||
| 	unsigned long		arch_flags; | ||||
| 	void			*priv; | ||||
| 	struct clk_mapping	*mapping; | ||||
| @ -65,6 +69,8 @@ struct clk { | ||||
| #define CLK_ENABLE_REG_16BIT	BIT(2) | ||||
| #define CLK_ENABLE_REG_8BIT	BIT(3) | ||||
| 
 | ||||
| #define CLK_MASK_DIV_ON_DISABLE	BIT(4) | ||||
| 
 | ||||
| #define CLK_ENABLE_REG_MASK	(CLK_ENABLE_REG_32BIT | \ | ||||
| 				 CLK_ENABLE_REG_16BIT | \ | ||||
| 				 CLK_ENABLE_REG_8BIT) | ||||
| @ -146,14 +152,17 @@ static inline int __deprecated sh_clk_mstp32_register(struct clk *clks, int nr) | ||||
| 	.enable_reg = (void __iomem *)_reg,			\ | ||||
| 	.enable_bit = _shift,					\ | ||||
| 	.arch_flags = _div_bitmap,				\ | ||||
| 	.div_mask = SH_CLK_DIV4_MSK,				\ | ||||
| 	.flags = _flags,					\ | ||||
| } | ||||
| 
 | ||||
| struct clk_div4_table { | ||||
| struct clk_div_table { | ||||
| 	struct clk_div_mult_table *div_mult_table; | ||||
| 	void (*kick)(struct clk *clk); | ||||
| }; | ||||
| 
 | ||||
| #define clk_div4_table clk_div_table | ||||
| 
 | ||||
| int sh_clk_div4_register(struct clk *clks, int nr, | ||||
| 			 struct clk_div4_table *table); | ||||
| int sh_clk_div4_enable_register(struct clk *clks, int nr, | ||||
| @ -165,7 +174,9 @@ int sh_clk_div4_reparent_register(struct clk *clks, int nr, | ||||
| 			_num_parents, _src_shift, _src_width)	\ | ||||
| {								\ | ||||
| 	.enable_reg = (void __iomem *)_reg,			\ | ||||
| 	.flags = _flags,					\ | ||||
| 	.enable_bit = 0, /* unused */				\ | ||||
| 	.flags = _flags | CLK_MASK_DIV_ON_DISABLE,		\ | ||||
| 	.div_mask = SH_CLK_DIV6_MSK,				\ | ||||
| 	.parent_table = _parents,				\ | ||||
| 	.parent_num = _num_parents,				\ | ||||
| 	.src_shift = _src_shift,				\ | ||||
| @ -176,7 +187,9 @@ int sh_clk_div4_reparent_register(struct clk *clks, int nr, | ||||
| {								\ | ||||
| 	.parent		= _parent,				\ | ||||
| 	.enable_reg	= (void __iomem *)_reg,			\ | ||||
| 	.flags		= _flags,				\ | ||||
| 	.enable_bit	= 0,	/* unused */			\ | ||||
| 	.div_mask	= SH_CLK_DIV6_MSK,			\ | ||||
| 	.flags		= _flags | CLK_MASK_DIV_ON_DISABLE,	\ | ||||
| } | ||||
| 
 | ||||
| int sh_clk_div6_register(struct clk *clks, int nr); | ||||
|  | ||||
| @ -11,22 +11,24 @@ | ||||
| #ifndef __SH_PFC_H | ||||
| #define __SH_PFC_H | ||||
| 
 | ||||
| #include <linux/stringify.h> | ||||
| #include <asm-generic/gpio.h> | ||||
| 
 | ||||
| typedef unsigned short pinmux_enum_t; | ||||
| typedef unsigned short pinmux_flag_t; | ||||
| 
 | ||||
| #define PINMUX_TYPE_NONE            0 | ||||
| #define PINMUX_TYPE_FUNCTION        1 | ||||
| #define PINMUX_TYPE_GPIO            2 | ||||
| #define PINMUX_TYPE_OUTPUT          3 | ||||
| #define PINMUX_TYPE_INPUT           4 | ||||
| #define PINMUX_TYPE_INPUT_PULLUP    5 | ||||
| #define PINMUX_TYPE_INPUT_PULLDOWN  6 | ||||
| enum { | ||||
| 	PINMUX_TYPE_NONE, | ||||
| 
 | ||||
| #define PINMUX_FLAG_TYPE            (0x7) | ||||
| #define PINMUX_FLAG_WANT_PULLUP     (1 << 3) | ||||
| #define PINMUX_FLAG_WANT_PULLDOWN   (1 << 4) | ||||
| 	PINMUX_TYPE_FUNCTION, | ||||
| 	PINMUX_TYPE_GPIO, | ||||
| 	PINMUX_TYPE_OUTPUT, | ||||
| 	PINMUX_TYPE_INPUT, | ||||
| 	PINMUX_TYPE_INPUT_PULLUP, | ||||
| 	PINMUX_TYPE_INPUT_PULLDOWN, | ||||
| 
 | ||||
| 	PINMUX_FLAG_TYPE,	/* must be last */ | ||||
| }; | ||||
| 
 | ||||
| #define PINMUX_FLAG_DBIT_SHIFT      5 | ||||
| #define PINMUX_FLAG_DBIT            (0x1f << PINMUX_FLAG_DBIT_SHIFT) | ||||
| @ -36,9 +38,12 @@ typedef unsigned short pinmux_flag_t; | ||||
| struct pinmux_gpio { | ||||
| 	pinmux_enum_t enum_id; | ||||
| 	pinmux_flag_t flags; | ||||
| 	const char *name; | ||||
| }; | ||||
| 
 | ||||
| #define PINMUX_GPIO(gpio, data_or_mark) [gpio] = { data_or_mark } | ||||
| #define PINMUX_GPIO(gpio, data_or_mark) \ | ||||
| 	[gpio] = { .name = __stringify(gpio), .enum_id = data_or_mark, .flags = PINMUX_TYPE_NONE } | ||||
| 
 | ||||
| #define PINMUX_DATA(data_or_mark, ids...) data_or_mark, ids, 0 | ||||
| 
 | ||||
| struct pinmux_cfg_reg { | ||||
| @ -89,7 +94,7 @@ struct pfc_window { | ||||
| 	unsigned long size; | ||||
| }; | ||||
| 
 | ||||
| struct pinmux_info { | ||||
| struct sh_pfc { | ||||
| 	char *name; | ||||
| 	pinmux_enum_t reserved_id; | ||||
| 	struct pinmux_range data; | ||||
| @ -112,17 +117,45 @@ struct pinmux_info { | ||||
| 	struct pinmux_irq *gpio_irq; | ||||
| 	unsigned int gpio_irq_size; | ||||
| 
 | ||||
| 	spinlock_t lock; | ||||
| 
 | ||||
| 	struct resource *resource; | ||||
| 	unsigned int num_resources; | ||||
| 	struct pfc_window *window; | ||||
| 
 | ||||
| 	unsigned long unlock_reg; | ||||
| 
 | ||||
| 	struct gpio_chip chip; | ||||
| }; | ||||
| 
 | ||||
| int register_pinmux(struct pinmux_info *pip); | ||||
| int unregister_pinmux(struct pinmux_info *pip); | ||||
| /* XXX compat for now */ | ||||
| #define pinmux_info sh_pfc | ||||
| 
 | ||||
| /* drivers/sh/pfc/gpio.c */ | ||||
| int sh_pfc_register_gpiochip(struct sh_pfc *pfc); | ||||
| 
 | ||||
| /* drivers/sh/pfc/pinctrl.c */ | ||||
| int sh_pfc_register_pinctrl(struct sh_pfc *pfc); | ||||
| 
 | ||||
| /* drivers/sh/pfc/core.c */ | ||||
| int register_sh_pfc(struct sh_pfc *pfc); | ||||
| 
 | ||||
| int sh_pfc_read_bit(struct pinmux_data_reg *dr, unsigned long in_pos); | ||||
| void sh_pfc_write_bit(struct pinmux_data_reg *dr, unsigned long in_pos, | ||||
| 		      unsigned long value); | ||||
| int sh_pfc_get_data_reg(struct sh_pfc *pfc, unsigned gpio, | ||||
| 			struct pinmux_data_reg **drp, int *bitp); | ||||
| int sh_pfc_gpio_to_enum(struct sh_pfc *pfc, unsigned gpio, int pos, | ||||
| 			pinmux_enum_t *enum_idp); | ||||
| int sh_pfc_config_gpio(struct sh_pfc *pfc, unsigned gpio, int pinmux_type, | ||||
| 		       int cfg_mode); | ||||
| 
 | ||||
| /* xxx */ | ||||
| static inline int register_pinmux(struct pinmux_info *pip) | ||||
| { | ||||
| 	struct sh_pfc *pfc = pip; | ||||
| 	return register_sh_pfc(pfc); | ||||
| } | ||||
| 
 | ||||
| enum { GPIO_CFG_DRYRUN, GPIO_CFG_REQ, GPIO_CFG_FREE }; | ||||
| 
 | ||||
| /* helper macro for port */ | ||||
| #define PORT_1(fn, pfx, sfx) fn(pfx, sfx) | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user