Commit 47b170af authored by Linus Torvalds's avatar Linus Torvalds

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.
  ...
parents 83c7f722 9ff561fd
...@@ -60,6 +60,7 @@ config SUPERH32 ...@@ -60,6 +60,7 @@ config SUPERH32
config SUPERH64 config SUPERH64
def_bool ARCH = "sh64" def_bool ARCH = "sh64"
select KALLSYMS
config ARCH_DEFCONFIG config ARCH_DEFCONFIG
string string
......
...@@ -44,6 +44,8 @@ config SH_7721_SOLUTION_ENGINE ...@@ -44,6 +44,8 @@ config SH_7721_SOLUTION_ENGINE
config SH_7722_SOLUTION_ENGINE config SH_7722_SOLUTION_ENGINE
bool "SolutionEngine7722" bool "SolutionEngine7722"
select SOLUTION_ENGINE select SOLUTION_ENGINE
select GENERIC_IRQ_CHIP
select IRQ_DOMAIN
depends on CPU_SUBTYPE_SH7722 depends on CPU_SUBTYPE_SH7722
help help
Select 7722 SolutionEngine if configuring for a Hitachi SH772 Select 7722 SolutionEngine if configuring for a Hitachi SH772
...@@ -80,6 +82,8 @@ config SH_7780_SOLUTION_ENGINE ...@@ -80,6 +82,8 @@ config SH_7780_SOLUTION_ENGINE
config SH_7343_SOLUTION_ENGINE config SH_7343_SOLUTION_ENGINE
bool "SolutionEngine7343" bool "SolutionEngine7343"
select SOLUTION_ENGINE select SOLUTION_ENGINE
select GENERIC_IRQ_CHIP
select IRQ_DOMAIN
depends on CPU_SUBTYPE_SH7343 depends on CPU_SUBTYPE_SH7343
help help
Select 7343 SolutionEngine if configuring for a Hitachi Select 7343 SolutionEngine if configuring for a Hitachi
...@@ -295,6 +299,7 @@ config SH_X3PROTO ...@@ -295,6 +299,7 @@ config SH_X3PROTO
bool "SH-X3 Prototype board" bool "SH-X3 Prototype board"
depends on CPU_SUBTYPE_SHX3 depends on CPU_SUBTYPE_SHX3
select NO_IOPORT if !PCI select NO_IOPORT if !PCI
select IRQ_DOMAIN
config SH_MAGIC_PANEL_R2 config SH_MAGIC_PANEL_R2
bool "Magic Panel R2" bool "Magic Panel R2"
......
...@@ -8,10 +8,11 @@ ...@@ -8,10 +8,11 @@
* This file is part of the LinuxDC project (www.linuxdc.org) * This file is part of the LinuxDC project (www.linuxdc.org)
* Released under the terms of the GNU GPL v2.0 * Released under the terms of the GNU GPL v2.0
*/ */
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/io.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> #include <mach/sysasic.h>
/* /*
...@@ -141,26 +142,15 @@ int systemasic_irq_demux(int irq) ...@@ -141,26 +142,15 @@ int systemasic_irq_demux(int irq)
void systemasic_irq_init(void) 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)) { irq_base = irq_alloc_descs(HW_EVENT_IRQ_BASE, HW_EVENT_IRQ_BASE,
pr_err("%s: got irq %d but wanted %d, bailing.\n", HW_EVENT_IRQ_MAX - HW_EVENT_IRQ_BASE, -1);
__func__, irq, i); if (IS_ERR_VALUE(irq_base)) {
destroy_irq(irq); pr_err("%s: failed hooking irqs\n", __func__);
return; return;
} }
for (i = HW_EVENT_IRQ_BASE; i < HW_EVENT_IRQ_MAX; i++)
irq_set_chip_and_handler(i, &systemasic_int, handle_level_irq); irq_set_chip_and_handler(i, &systemasic_int, handle_level_irq);
}
} }
/* /*
* linux/arch/sh/boards/se/7343/irq.c * Hitachi UL SolutionEngine 7343 FPGA IRQ Support.
* *
* Copyright (C) 2008 Yoshihiro Shimoda * 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 * Copyright (C) 2007 Nobuhiro Iwamatsu
* *
* This file is subject to the terms and conditions of the GNU General Public * 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 * License. See the file "COPYING" in the main directory of this archive
* for more details. * 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/init.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/irqdomain.h>
#include <linux/io.h> #include <linux/io.h>
#include <asm/sizes.h>
#include <mach-se/mach/se7343.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) static void __iomem *se7343_irq_regs;
{ struct irq_domain *se7343_irq_domain;
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) static void se7343_irq_demux(unsigned int irq, struct irq_desc *desc)
{ {
unsigned int bit = (unsigned int)irq_data_get_irq_chip_data(data); struct irq_data *data = irq_get_irq_data(irq);
__raw_writew(__raw_readw(PA_CPLD_IMSK) & ~(1 << bit), PA_CPLD_IMSK); struct irq_chip *chip = irq_data_get_irq_chip(data);
} unsigned long mask;
int bit;
static struct irq_chip se7343_irq_chip __read_mostly = { chip->irq_mask_ack(data);
.name = "SE7343-FPGA",
.irq_mask = disable_se7343_irq,
.irq_unmask = enable_se7343_irq,
};
static void se7343_irq_demux(unsigned int irq, struct irq_desc *desc) mask = ioread16(se7343_irq_regs + PA_CPLD_ST_REG);
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)
{ {
unsigned short intv = __raw_readw(PA_CPLD_ST); int i;
unsigned int ext_irq = 0;
intv &= (1 << SE7343_FPGA_IRQ_NR) - 1; 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 (; intv; intv >>= 1, ext_irq++) { for (i = 0; i < SE7343_FPGA_IRQ_NR; i++) {
if (!(intv & 1)) int irq = irq_create_mapping(se7343_irq_domain, i);
continue;
generic_handle_irq(se7343_fpga_irq[ext_irq]); if (unlikely(irq == 0)) {
printk("Failed to allocate IRQ %d\n", i);
return;
}
} }
} }
/* static void __init se7343_gc_init(void)
* Initialize IRQ setting
*/
void __init init_7343se_IRQ(void)
{ {
int i, irq; struct irq_chip_generic *gc;
struct irq_chip_type *ct;
unsigned int irq_base;
__raw_writew(0, PA_CPLD_IMSK); /* disable all irqs */ irq_base = irq_linear_revmap(se7343_irq_domain, 0);
__raw_writew(0x2000, 0xb03fffec); /* mrshpc irq enable */
for (i = 0; i < SE7343_FPGA_IRQ_NR; i++) { gc = irq_alloc_generic_chip(DRV_NAME, 1, irq_base, se7343_irq_regs,
irq = create_irq(); handle_level_irq);
if (irq < 0) if (unlikely(!gc))
return; return;
se7343_fpga_irq[i] = irq;
irq_set_chip_and_handler_name(se7343_fpga_irq[i], ct = gc->chip_types;
&se7343_irq_chip, ct->chip.irq_mask = irq_gc_mask_set_bit;
handle_level_irq, ct->chip.irq_unmask = irq_gc_mask_clr_bit;
"level");
irq_set_chip_data(se7343_fpga_irq[i], (void *)i); 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_chained_handler(IRQ0_IRQ, se7343_irq_demux);
irq_set_irq_type(IRQ0_IRQ, IRQ_TYPE_LEVEL_LOW); irq_set_irq_type(IRQ0_IRQ, IRQ_TYPE_LEVEL_LOW);
irq_set_chained_handler(IRQ1_IRQ, se7343_irq_demux); irq_set_chained_handler(IRQ1_IRQ, se7343_irq_demux);
irq_set_irq_type(IRQ1_IRQ, IRQ_TYPE_LEVEL_LOW); irq_set_irq_type(IRQ1_IRQ, IRQ_TYPE_LEVEL_LOW);
irq_set_chained_handler(IRQ4_IRQ, se7343_irq_demux); irq_set_chained_handler(IRQ4_IRQ, se7343_irq_demux);
irq_set_irq_type(IRQ4_IRQ, IRQ_TYPE_LEVEL_LOW); irq_set_irq_type(IRQ4_IRQ, IRQ_TYPE_LEVEL_LOW);
irq_set_chained_handler(IRQ5_IRQ, se7343_irq_demux); irq_set_chained_handler(IRQ5_IRQ, se7343_irq_demux);
irq_set_irq_type(IRQ5_IRQ, IRQ_TYPE_LEVEL_LOW); irq_set_irq_type(IRQ5_IRQ, IRQ_TYPE_LEVEL_LOW);
} }
/*
* Initialize IRQ setting
*/
void __init init_7343se_IRQ(void)
{
se7343_irq_regs = ioremap(PA_CPLD_BASE_ADDR, SZ_16);
if (unlikely(!se7343_irq_regs)) {
pr_err("Failed to remap CPLD\n");
return;
}
/*
* 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 @@ ...@@ -5,6 +5,7 @@
#include <linux/serial_reg.h> #include <linux/serial_reg.h>
#include <linux/usb/isp116x.h> #include <linux/usb/isp116x.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/irqdomain.h>
#include <asm/machvec.h> #include <asm/machvec.h>
#include <mach-se/mach/se7343.h> #include <mach-se/mach/se7343.h>
#include <asm/heartbeat.h> #include <asm/heartbeat.h>
...@@ -145,11 +146,12 @@ static struct platform_device *sh7343se_platform_devices[] __initdata = { ...@@ -145,11 +146,12 @@ static struct platform_device *sh7343se_platform_devices[] __initdata = {
static int __init sh7343se_devices_setup(void) static int __init sh7343se_devices_setup(void)
{ {
/* Wire-up dynamic vectors */ /* Wire-up dynamic vectors */
serial_platform_data[0].irq = se7343_fpga_irq[SE7343_FPGA_IRQ_UARTA]; serial_platform_data[0].irq = irq_find_mapping(se7343_irq_domain,
serial_platform_data[1].irq = se7343_fpga_irq[SE7343_FPGA_IRQ_UARTB]; 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 = 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, return platform_add_devices(sh7343se_platform_devices,
ARRAY_SIZE(sh7343se_platform_devices)); ARRAY_SIZE(sh7343se_platform_devices));
......
/* /*
* linux/arch/sh/boards/se/7722/irq.c * Hitachi UL SolutionEngine 7722 FPGA IRQ Support.
* *
* Copyright (C) 2007 Nobuhiro Iwamatsu * Copyright (C) 2007 Nobuhiro Iwamatsu
* * Copyright (C) 2012 Paul Mundt
* Hitachi UL SolutionEngine 7722 Support.
* *
* This file is subject to the terms and conditions of the GNU General Public * 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 * License. See the file "COPYING" in the main directory of this archive
* for more details. * 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/init.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <asm/irq.h> #include <linux/irqdomain.h>
#include <asm/io.h> #include <linux/io.h>
#include <linux/err.h>
#include <asm/sizes.h>
#include <mach-se/mach/se7722.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) static void __iomem *se7722_irq_regs;
{ struct irq_domain *se7722_irq_domain;
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) static void se7722_irq_demux(unsigned int irq, struct irq_desc *desc)
{ {
unsigned int bit = (unsigned int)irq_data_get_irq_chip_data(data); struct irq_data *data = irq_get_irq_data(irq);
__raw_writew(__raw_readw(IRQ01_MASK) & ~(1 << bit), IRQ01_MASK); struct irq_chip *chip = irq_data_get_irq_chip(data);
} unsigned long mask;
int bit;
static struct irq_chip se7722_irq_chip __read_mostly = { chip->irq_mask_ack(data);
.name = "SE7722-FPGA",
.irq_mask = disable_se7722_irq,
.irq_unmask = enable_se7722_irq,
};
static void se7722_irq_demux(unsigned int irq, struct irq_desc *desc) mask = ioread16(se7722_irq_regs + IRQ01_STS_REG);
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)
{ {
unsigned short intv = __raw_readw(IRQ01_STS); int i;
unsigned int ext_irq = 0;
intv &= (1 << SE7722_FPGA_IRQ_NR) - 1; 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 (; intv; intv >>= 1, ext_irq++) { for (i = 0; i < SE7722_FPGA_IRQ_NR; i++) {
if (!(intv & 1)) int irq = irq_create_mapping(se7722_irq_domain, i);
continue;
generic_handle_irq(se7722_fpga_irq[ext_irq]); if (unlikely(irq == 0)) {
printk("Failed to allocate IRQ %d\n", i);
return;
}
} }
} }
/* static void __init se7722_gc_init(void)
* Initialize IRQ setting
*/
void __init init_se7722_IRQ(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 */ irq_base = irq_linear_revmap(se7722_irq_domain, 0);
__raw_writew(0x2000, 0xb03fffec); /* mrshpc irq enable */
for (i = 0; i < SE7722_FPGA_IRQ_NR; i++) { gc = irq_alloc_generic_chip(DRV_NAME, 1, irq_base, se7722_irq_regs,
irq = create_irq(); handle_level_irq);
if (irq < 0) if (unlikely(!gc))
return; return;
se7722_fpga_irq[i] = irq;
irq_set_chip_and_handler_name(se7722_fpga_irq[i], ct = gc->chip_types;
&se7722_irq_chip, ct->chip.irq_mask = irq_gc_mask_set_bit;
handle_level_irq, ct->chip.irq_unmask = irq_gc_mask_clr_bit;
"level");
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_chained_handler(IRQ0_IRQ, se7722_irq_demux);
irq_set_irq_type(IRQ0_IRQ, IRQ_TYPE_LEVEL_LOW); irq_set_irq_type(IRQ0_IRQ, IRQ_TYPE_LEVEL_LOW);
...@@ -81,3 +98,25 @@ void __init init_se7722_IRQ(void) ...@@ -81,3 +98,25 @@ void __init init_se7722_IRQ(void)
irq_set_chained_handler(IRQ1_IRQ, se7722_irq_demux); irq_set_chained_handler(IRQ1_IRQ, se7722_irq_demux);
irq_set_irq_type(IRQ1_IRQ, IRQ_TYPE_LEVEL_LOW); 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 @@ ...@@ -2,6 +2,7 @@
* linux/arch/sh/boards/se/7722/setup.c * linux/arch/sh/boards/se/7722/setup.c
* *
* Copyright (C) 2007 Nobuhiro Iwamatsu * Copyright (C) 2007 Nobuhiro Iwamatsu
* Copyright (C) 2012 Paul Mundt
* *
* Hitachi UL SolutionEngine 7722 Support. * Hitachi UL SolutionEngine 7722 Support.
* *
...@@ -15,6 +16,7 @@ ...@@ -15,6 +16,7 @@
#include <linux/ata_platform.h> #include <linux/ata_platform.h>
#include <linux/input.h> #include <linux/input.h>
#include <linux/input/sh_keysc.h> #include <linux/input/sh_keysc.h>
#include <linux/irqdomain.h>
#include <linux/smc91x.h> #include <linux/smc91x.h>
#include <linux/sh_intc.h> #include <linux/sh_intc.h>
#include <mach-se/mach/se7722.h> #include <mach-se/mach/se7722.h>
...@@ -143,10 +145,10 @@ static int __init se7722_devices_setup(void) ...@@ -143,10 +145,10 @@ static int __init se7722_devices_setup(void)
/* Wire-up dynamic vectors */ /* Wire-up dynamic vectors */
cf_ide_resources[2].start = cf_ide_resources[2].end = 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 = 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)); return platform_add_devices(se7722_devices, ARRAY_SIZE(se7722_devices));
} }
......
...@@ -17,8 +17,10 @@ ...@@ -17,8 +17,10 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <asm/irq.h> #include <linux/export.h>
#include <asm/io.h> #include <linux/topology.h>
#include <linux/io.h>
#include <linux/err.h>
#include <mach-se/mach/se7724.h> #include <mach-se/mach/se7724.h>
struct fpga_irq { struct fpga_irq {
...@@ -111,7 +113,7 @@ static void se7724_irq_demux(unsigned int irq, struct irq_desc *desc) ...@@ -111,7 +113,7 @@ static void se7724_irq_demux(unsigned int irq, struct irq_desc *desc)
*/ */
void __init init_se7724_IRQ(void) 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, IRQ0_MR); /* mask all */
__raw_writew(0xffff, IRQ1_MR); /* mask all */ __raw_writew(0xffff, IRQ1_MR); /* mask all */
...@@ -121,28 +123,16 @@ void __init init_se7724_IRQ(void) ...@@ -121,28 +123,16 @@ void __init init_se7724_IRQ(void)
__raw_writew(0x0000, IRQ2_SR); /* clear irq */ __raw_writew(0x0000, IRQ2_SR); /* clear irq */
__raw_writew(0x002a, IRQ_MODE); /* set irq type */ __raw_writew(0x002a, IRQ_MODE); /* set irq type */
for (i = 0; i < SE7724_FPGA_IRQ_NR; i++) { irq_base = irq_alloc_descs(SE7724_FPGA_IRQ_BASE, SE7724_FPGA_IRQ_BASE,
int irq, wanted; SE7724_FPGA_IRQ_NR, numa_node_id());
if (IS_ERR_VALUE(irq_base)) {
wanted = SE7724_FPGA_IRQ_BASE + i; pr_err("%s: failed hooking irqs for FPGA\n", __func__);
return;
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, 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"); handle_level_irq, "level");
}
irq_set_chained_handler(IRQ0_IRQ, se7724_irq_demux); irq_set_chained_handler(IRQ0_IRQ, se7724_irq_demux);
irq_set_irq_type(IRQ0_IRQ, IRQ_TYPE_LEVEL_LOW); irq_set_irq_type(IRQ0_IRQ, IRQ_TYPE_LEVEL_LOW);
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* *
* Renesas SH-X3 Prototype Baseboard GPIO Support. * 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 * 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 * License. See the file "COPYING" in the main directory of this archive
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/irqdomain.h>
#include <linux/io.h> #include <linux/io.h>
#include <mach/ilsel.h> #include <mach/ilsel.h>
#include <mach/hardware.h> #include <mach/hardware.h>
...@@ -26,7 +27,7 @@ ...@@ -26,7 +27,7 @@
#define KEYDETR 0xb81c0004 #define KEYDETR 0xb81c0004
static DEFINE_SPINLOCK(x3proto_gpio_lock); 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) 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) ...@@ -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) 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) 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) ...@@ -62,9 +70,8 @@ static void x3proto_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
chip->irq_mask_ack(data); chip->irq_mask_ack(data);
mask = __raw_readw(KEYDETR); mask = __raw_readw(KEYDETR);
for_each_set_bit(pin, &mask, NR_BASEBOARD_GPIOS) 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); chip->irq_unmask(data);
} }
...@@ -78,10 +85,23 @@ struct gpio_chip x3proto_gpio_chip = { ...@@ -78,10 +85,23 @@ struct gpio_chip x3proto_gpio_chip = {
.ngpio = NR_BASEBOARD_GPIOS, .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 __init x3proto_gpio_setup(void)
{ {
int ilsel; int ilsel, ret;
int ret, i;
ilsel = ilsel_enable(ILSEL_KEY); ilsel = ilsel_enable(ILSEL_KEY);
if (unlikely(ilsel < 0)) if (unlikely(ilsel < 0))
...@@ -91,21 +111,10 @@ int __init x3proto_gpio_setup(void) ...@@ -91,21 +111,10 @@ int __init x3proto_gpio_setup(void)
if (unlikely(ret)) if (unlikely(ret))
goto err_gpio; goto err_gpio;
for (i = 0; i < NR_BASEBOARD_GPIOS; i++) { x3proto_irq_domain = irq_domain_add_linear(NULL, NR_BASEBOARD_GPIOS,
unsigned long flags; &x3proto_gpio_irq_ops, NULL);
int irq = create_irq(); if (unlikely(!x3proto_irq_domain))
goto err_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);
}
pr_info("registering '%s' support, handling GPIOs %u -> %u, " pr_info("registering '%s' support, handling GPIOs %u -> %u, "
"bound to IRQ %u\n", "bound to IRQ %u\n",
...@@ -119,10 +128,6 @@ int __init x3proto_gpio_setup(void) ...@@ -119,10 +128,6 @@ int __init x3proto_gpio_setup(void)
return 0; return 0;
err_irq: 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); ret = gpiochip_remove(&x3proto_gpio_chip);
if (unlikely(ret)) if (unlikely(ret))
pr_err("Failed deregistering GPIO\n"); pr_err("Failed deregistering GPIO\n");
......
...@@ -73,10 +73,7 @@ static void hd64461_irq_demux(unsigned int irq, struct irq_desc *desc) ...@@ -73,10 +73,7 @@ static void hd64461_irq_demux(unsigned int irq, struct irq_desc *desc)
int __init setup_hd64461(void) int __init setup_hd64461(void)
{ {
int i, nid = cpu_to_node(boot_cpu_data); int irq_base, i;
if (!MACH_HD64461)
return 0;
printk(KERN_INFO printk(KERN_INFO
"HD64461 configured at 0x%x on irq %d(mapped into %d to %d)\n", "HD64461 configured at 0x%x on irq %d(mapped into %d to %d)\n",
...@@ -89,28 +86,16 @@ int __init setup_hd64461(void) ...@@ -89,28 +86,16 @@ int __init setup_hd64461(void)
#endif #endif
__raw_writew(0xffff, HD64461_NIMR); __raw_writew(0xffff, HD64461_NIMR);
/* IRQ 80 -> 95 belongs to HD64461 */ irq_base = irq_alloc_descs(HD64461_IRQBASE, HD64461_IRQBASE, 16, -1);
for (i = HD64461_IRQBASE; i < HD64461_IRQBASE + 16; i++) { if (IS_ERR_VALUE(irq_base)) {
unsigned int irq; pr_err("%s: failed hooking irqs for HD64461\n", __func__);
return irq_base;
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);
} }
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_chained_handler(CONFIG_HD64461_IRQ, hd64461_irq_demux);
irq_set_irq_type(CONFIG_HD64461_IRQ, IRQ_TYPE_LEVEL_LOW); irq_set_irq_type(CONFIG_HD64461_IRQ, IRQ_TYPE_LEVEL_LOW);
......
...@@ -110,6 +110,10 @@ do { \ ...@@ -110,6 +110,10 @@ do { \
#include <asm-generic/bug.h> #include <asm-generic/bug.h>
struct pt_regs; 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(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 */ #endif /* __ASM_SH_BUG_H */
...@@ -10,6 +10,8 @@ enum die_val { ...@@ -10,6 +10,8 @@ enum die_val {
DIE_SSTEP, DIE_SSTEP,
}; };
/* arch/sh/kernel/dumpstack.c */
extern void printk_address(unsigned long address, int reliable); 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 */ #endif /* __ASM_SH_KDEBUG_H */
...@@ -50,9 +50,6 @@ ...@@ -50,9 +50,6 @@
#define PA_LED 0xb0C00000 /* LED */ #define PA_LED 0xb0C00000 /* LED */
#define LED_SHIFT 0 #define LED_SHIFT 0
#define PA_DIPSW 0xb0900000 /* Dip switch 31 */ #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 */ /* Area 5 */
#define PA_EXT5 0x14000000 #define PA_EXT5 0x14000000
#define PA_EXT5_SIZE 0x04000000 #define PA_EXT5_SIZE 0x04000000
...@@ -135,8 +132,10 @@ ...@@ -135,8 +132,10 @@
#define SE7343_FPGA_IRQ_NR 12 #define SE7343_FPGA_IRQ_NR 12
struct irq_domain;
/* arch/sh/boards/se/7343/irq.c */ /* 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); void init_7343se_IRQ(void);
......
...@@ -81,12 +81,6 @@ ...@@ -81,12 +81,6 @@
#define IRQ0_IRQ evt2irq(0x600) #define IRQ0_IRQ evt2irq(0x600)
#define IRQ1_IRQ evt2irq(0x620) #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_USB 0 /* IRQ0 */
#define SE7722_FPGA_IRQ_SMC 1 /* IRQ0 */ #define SE7722_FPGA_IRQ_SMC 1 /* IRQ0 */
#define SE7722_FPGA_IRQ_MRSHPC0 2 /* IRQ1 */ #define SE7722_FPGA_IRQ_MRSHPC0 2 /* IRQ1 */
...@@ -95,8 +89,10 @@ ...@@ -95,8 +89,10 @@
#define SE7722_FPGA_IRQ_MRSHPC3 5 /* IRQ1 */ #define SE7722_FPGA_IRQ_MRSHPC3 5 /* IRQ1 */
#define SE7722_FPGA_IRQ_NR 6 #define SE7722_FPGA_IRQ_NR 6
struct irq_domain;
/* arch/sh/boards/se/7722/irq.c */ /* 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); void init_se7722_IRQ(void);
......
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
#include <asm/ptrace.h> #include <asm/ptrace.h>
#include <asm/processor.h> #include <asm/processor.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/unwinder.h>
#include <asm/stacktrace.h>
static u8 regcache[63]; static u8 regcache[63];
...@@ -199,8 +201,11 @@ static int lookup_prev_stack_frame(unsigned long fp, unsigned long pc, ...@@ -199,8 +201,11 @@ static int lookup_prev_stack_frame(unsigned long fp, unsigned long pc,
return 0; 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; static struct pt_regs here_regs;
extern const char syscall_ret; extern const char syscall_ret;
...@@ -208,17 +213,19 @@ extern const char ret_from_syscall; ...@@ -208,17 +213,19 @@ extern const char ret_from_syscall;
extern const char ret_from_exception; extern const char ret_from_exception;
extern const char ret_from_irq; 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) && if ((fp >= __MEMORY_START) &&
((fp & 7) == 0)) { ((fp & 7) == 0))
sh64_unwind_inner((struct pt_regs *) fp); 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; unsigned long pc, fp;
int ofs = 0; int ofs = 0;
...@@ -232,29 +239,29 @@ static void sh64_unwind_inner(struct pt_regs *regs) ...@@ -232,29 +239,29 @@ static void sh64_unwind_inner(struct pt_regs *regs)
int cond; int cond;
unsigned long next_fp, next_pc; unsigned long next_fp, next_pc;
if (pc == ((unsigned long) &syscall_ret & ~1)) { if (pc == ((unsigned long)&syscall_ret & ~1)) {
printk("SYSCALL\n"); printk("SYSCALL\n");
unwind_nested(pc,fp); unwind_nested(ops, data, pc, fp);
return; return;
} }
if (pc == ((unsigned long) &ret_from_syscall & ~1)) { if (pc == ((unsigned long)&ret_from_syscall & ~1)) {
printk("SYSCALL (PREEMPTED)\n"); printk("SYSCALL (PREEMPTED)\n");
unwind_nested(pc,fp); unwind_nested(ops, data, pc, fp);
return; return;
} }
/* In this case, the PC is discovered by lookup_prev_stack_frame but /* In this case, the PC is discovered by lookup_prev_stack_frame but
it has 4 taken off it to look like the 'caller' */ 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"); printk("EXCEPTION\n");
unwind_nested(pc,fp); unwind_nested(ops, data, pc, fp);
return; return;
} }
if (pc == ((unsigned long) &ret_from_irq & ~1)) { if (pc == ((unsigned long)&ret_from_irq & ~1)) {
printk("IRQ\n"); printk("IRQ\n");
unwind_nested(pc,fp); unwind_nested(ops, data, pc, fp);
return; return;
} }
...@@ -263,8 +270,7 @@ static void sh64_unwind_inner(struct pt_regs *regs) ...@@ -263,8 +270,7 @@ static void sh64_unwind_inner(struct pt_regs *regs)
pc -= ofs; pc -= ofs;
printk("[<%08lx>] ", pc); ops->address(data, pc, 1);
print_symbol("%s\n", pc);
if (first_pass) { if (first_pass) {
/* If the innermost frame is a leaf function, it's /* If the innermost frame is a leaf function, it's
...@@ -287,10 +293,13 @@ static void sh64_unwind_inner(struct pt_regs *regs) ...@@ -287,10 +293,13 @@ static void sh64_unwind_inner(struct pt_regs *regs)
} }
printk("\n"); 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) { if (!regs) {
/* /*
...@@ -320,7 +329,17 @@ void sh64_unwind(struct pt_regs *regs) ...@@ -320,7 +329,17 @@ void sh64_unwind(struct pt_regs *regs)
); );
} }
printk("\nCall Trace:\n"); sh64_unwind_inner(ops, data, regs);
sh64_unwind_inner(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 @@ ...@@ -2,13 +2,48 @@
* Copyright (C) 1991, 1992 Linus Torvalds * Copyright (C) 1991, 1992 Linus Torvalds
* Copyright (C) 2000, 2001, 2002 Andi Kleen, SuSE Labs * Copyright (C) 2000, 2001, 2002 Andi Kleen, SuSE Labs
* Copyright (C) 2009 Matt Fleming * 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/kallsyms.h>
#include <linux/ftrace.h> #include <linux/ftrace.h>
#include <linux/debug_locks.h> #include <linux/debug_locks.h>
#include <linux/kdebug.h>
#include <linux/export.h>
#include <linux/uaccess.h>
#include <asm/unwinder.h> #include <asm/unwinder.h>
#include <asm/stacktrace.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) void printk_address(unsigned long address, int reliable)
{ {
printk(" [<%p>] %s%pS\n", (void *) address, printk(" [<%p>] %s%pS\n", (void *) address,
...@@ -106,3 +141,26 @@ void show_trace(struct task_struct *tsk, unsigned long *sp, ...@@ -106,3 +141,26 @@ void show_trace(struct task_struct *tsk, unsigned long *sp,
debug_show_held_locks(tsk); 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) ...@@ -231,16 +231,6 @@ void __init init_IRQ(void)
irq_ctx_init(smp_processor_id()); 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 #ifdef CONFIG_HOTPLUG_CPU
static void route_irq(struct irq_data *data, unsigned int irq, unsigned int cpu) static void route_irq(struct irq_data *data, unsigned int irq, unsigned int cpu)
{ {
......
...@@ -6,9 +6,80 @@ ...@@ -6,9 +6,80 @@
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/hardirq.h> #include <linux/hardirq.h>
#include <linux/kernel.h>
#include <linux/kexec.h>
#include <linux/module.h>
#include <asm/unwinder.h> #include <asm/unwinder.h>
#include <asm/traps.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 #ifdef CONFIG_GENERIC_BUG
static void handle_BUG(struct pt_regs *regs) static void handle_BUG(struct pt_regs *regs)
{ {
......
...@@ -16,13 +16,11 @@ ...@@ -16,13 +16,11 @@
#include <linux/hardirq.h> #include <linux/hardirq.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/module.h>
#include <linux/kallsyms.h> #include <linux/kallsyms.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/bug.h> #include <linux/bug.h>
#include <linux/debug_locks.h> #include <linux/debug_locks.h>
#include <linux/kdebug.h> #include <linux/kdebug.h>
#include <linux/kexec.h>
#include <linux/limits.h> #include <linux/limits.h>
#include <linux/sysfs.h> #include <linux/sysfs.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
...@@ -48,102 +46,6 @@ ...@@ -48,102 +46,6 @@
#define TRAP_ILLEGAL_SLOT_INST 13 #define TRAP_ILLEGAL_SLOT_INST 13
#endif #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) static inline void sign_extend(unsigned int count, unsigned char *dst)
{ {
#ifdef __LITTLE_ENDIAN__ #ifdef __LITTLE_ENDIAN__
...@@ -900,26 +802,3 @@ void __init trap_init(void) ...@@ -900,26 +802,3 @@ void __init trap_init(void)
set_exception_table_vec(TRAP_UBC, breakpoint_trap_handler); set_exception_table_vec(TRAP_UBC, breakpoint_trap_handler);
#endif #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 @@ ...@@ -27,283 +27,25 @@
#include <linux/perf_event.h> #include <linux/perf_event.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/io.h> #include <asm/io.h>
#include <linux/atomic.h> #include <asm/alignment.h>
#include <asm/processor.h> #include <asm/processor.h>
#include <asm/pgtable.h> #include <asm/pgtable.h>
#include <asm/fpu.h> #include <asm/fpu.h>
#undef DEBUG_EXCEPTION static int read_opcode(reg_size_t pc, insn_size_t *result_opcode, int from_user_mode)
#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)
{ {
int get_user_error; int get_user_error;
unsigned long aligned_pc; unsigned long aligned_pc;
unsigned long opcode; insn_size_t opcode;
if ((pc & 3) == 1) { if ((pc & 3) == 1) {
/* SHmedia */ /* SHmedia */
aligned_pc = pc & ~3; aligned_pc = pc & ~3;
if (from_user_mode) { 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; get_user_error = -EFAULT;
} else { } 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; *result_opcode = opcode;
} }
return get_user_error; return get_user_error;
...@@ -311,7 +53,7 @@ static int read_opcode(unsigned long long pc, unsigned long *result_opcode, int ...@@ -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 /* If the fault was in the kernel, we can either read
* this directly, or if not, we fault. * this directly, or if not, we fault.
*/ */
*result_opcode = *(unsigned long *) aligned_pc; *result_opcode = *(insn_size_t *)aligned_pc;
return 0; return 0;
} }
} else if ((pc & 1) == 0) { } else if ((pc & 1) == 0) {
...@@ -337,17 +79,23 @@ static int address_is_sign_extended(__u64 a) ...@@ -337,17 +79,23 @@ static int address_is_sign_extended(__u64 a)
#endif #endif
} }
/* return -1 for fault, 0 for OK */
static int generate_and_check_address(struct pt_regs *regs, static int generate_and_check_address(struct pt_regs *regs,
__u32 opcode, insn_size_t opcode,
int displacement_not_indexed, int displacement_not_indexed,
int width_shift, int width_shift,
__u64 *address) __u64 *address)
{ {
/* return -1 for fault, 0 for OK */
__u64 base_address, addr; __u64 base_address, addr;
int basereg; 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; basereg = (opcode >> 20) & 0x3f;
base_address = regs->regs[basereg]; base_address = regs->regs[basereg];
if (displacement_not_indexed) { if (displacement_not_indexed) {
...@@ -364,28 +112,28 @@ static int generate_and_check_address(struct pt_regs *regs, ...@@ -364,28 +112,28 @@ static int generate_and_check_address(struct pt_regs *regs,
} }
/* Check sign extended */ /* Check sign extended */
if (!address_is_sign_extended(addr)) { if (!address_is_sign_extended(addr))
return -1; return -1;
}
/* Check accessible. For misaligned access in the kernel, assume the /* Check accessible. For misaligned access in the kernel, assume the
address is always accessible (and if not, just fault when the address is always accessible (and if not, just fault when the
load/store gets done.) */ load/store gets done.) */
if (user_mode(regs)) { if (user_mode(regs)) {
if (addr >= TASK_SIZE) { inc_unaligned_user_access();
if (addr >= TASK_SIZE)
return -1; return -1;
} } else
/* Do access_ok check later - it depends on whether it's a load or a store. */ inc_unaligned_kernel_access();
}
*address = addr; *address = addr;
perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, addr);
unaligned_fixups_notify(current, opcode, regs);
return 0; 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) static void misaligned_kernel_word_load(__u64 address, int do_sign_extend, __u64 *result)
{ {
unsigned short x; unsigned short x;
...@@ -415,7 +163,7 @@ static void misaligned_kernel_word_store(__u64 address, __u64 value) ...@@ -415,7 +163,7 @@ static void misaligned_kernel_word_store(__u64 address, __u64 value)
} }
static int misaligned_load(struct pt_regs *regs, static int misaligned_load(struct pt_regs *regs,
__u32 opcode, insn_size_t opcode,
int displacement_not_indexed, int displacement_not_indexed,
int width_shift, int width_shift,
int do_sign_extend) int do_sign_extend)
...@@ -427,11 +175,8 @@ static int misaligned_load(struct pt_regs *regs, ...@@ -427,11 +175,8 @@ static int misaligned_load(struct pt_regs *regs,
error = generate_and_check_address(regs, opcode, error = generate_and_check_address(regs, opcode,
displacement_not_indexed, width_shift, &address); displacement_not_indexed, width_shift, &address);
if (error < 0) { if (error < 0)
return error; return error;
}
perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, 1, regs, address);
destreg = (opcode >> 4) & 0x3f; destreg = (opcode >> 4) & 0x3f;
if (user_mode(regs)) { if (user_mode(regs)) {
...@@ -490,11 +235,10 @@ static int misaligned_load(struct pt_regs *regs, ...@@ -490,11 +235,10 @@ static int misaligned_load(struct pt_regs *regs,
} }
return 0; return 0;
} }
static int misaligned_store(struct pt_regs *regs, static int misaligned_store(struct pt_regs *regs,
__u32 opcode, insn_size_t opcode,
int displacement_not_indexed, int displacement_not_indexed,
int width_shift) int width_shift)
{ {
...@@ -505,11 +249,8 @@ static int misaligned_store(struct pt_regs *regs, ...@@ -505,11 +249,8 @@ static int misaligned_store(struct pt_regs *regs,
error = generate_and_check_address(regs, opcode, error = generate_and_check_address(regs, opcode,
displacement_not_indexed, width_shift, &address); displacement_not_indexed, width_shift, &address);
if (error < 0) { if (error < 0)
return error; return error;
}
perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, 1, regs, address);
srcreg = (opcode >> 4) & 0x3f; srcreg = (opcode >> 4) & 0x3f;
if (user_mode(regs)) { if (user_mode(regs)) {
...@@ -563,13 +304,12 @@ static int misaligned_store(struct pt_regs *regs, ...@@ -563,13 +304,12 @@ static int misaligned_store(struct pt_regs *regs,
} }
return 0; return 0;
} }
/* Never need to fix up misaligned FPU accesses within the kernel since that's a real /* Never need to fix up misaligned FPU accesses within the kernel since that's a real
error. */ error. */
static int misaligned_fpu_load(struct pt_regs *regs, static int misaligned_fpu_load(struct pt_regs *regs,
__u32 opcode, insn_size_t opcode,
int displacement_not_indexed, int displacement_not_indexed,
int width_shift, int width_shift,
int do_paired_load) int do_paired_load)
...@@ -581,11 +321,8 @@ static int misaligned_fpu_load(struct pt_regs *regs, ...@@ -581,11 +321,8 @@ static int misaligned_fpu_load(struct pt_regs *regs,
error = generate_and_check_address(regs, opcode, error = generate_and_check_address(regs, opcode,
displacement_not_indexed, width_shift, &address); displacement_not_indexed, width_shift, &address);
if (error < 0) { if (error < 0)
return error; return error;
}
perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, address);
destreg = (opcode >> 4) & 0x3f; destreg = (opcode >> 4) & 0x3f;
if (user_mode(regs)) { if (user_mode(regs)) {
...@@ -641,12 +378,10 @@ static int misaligned_fpu_load(struct pt_regs *regs, ...@@ -641,12 +378,10 @@ static int misaligned_fpu_load(struct pt_regs *regs,
die ("Misaligned FPU load inside kernel", regs, 0); die ("Misaligned FPU load inside kernel", regs, 0);
return -1; return -1;
} }
} }
static int misaligned_fpu_store(struct pt_regs *regs, static int misaligned_fpu_store(struct pt_regs *regs,
__u32 opcode, insn_size_t opcode,
int displacement_not_indexed, int displacement_not_indexed,
int width_shift, int width_shift,
int do_paired_load) int do_paired_load)
...@@ -658,11 +393,8 @@ static int misaligned_fpu_store(struct pt_regs *regs, ...@@ -658,11 +393,8 @@ static int misaligned_fpu_store(struct pt_regs *regs,
error = generate_and_check_address(regs, opcode, error = generate_and_check_address(regs, opcode,
displacement_not_indexed, width_shift, &address); displacement_not_indexed, width_shift, &address);
if (error < 0) { if (error < 0)
return error; return error;
}
perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, address);
srcreg = (opcode >> 4) & 0x3f; srcreg = (opcode >> 4) & 0x3f;
if (user_mode(regs)) { if (user_mode(regs)) {
...@@ -723,11 +455,13 @@ static int misaligned_fpu_store(struct pt_regs *regs, ...@@ -723,11 +455,13 @@ static int misaligned_fpu_store(struct pt_regs *regs,
static int misaligned_fixup(struct pt_regs *regs) static int misaligned_fixup(struct pt_regs *regs)
{ {
unsigned long opcode; insn_size_t opcode;
int error; int error;
int major, minor; 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; return -1;
error = read_opcode(regs->pc, &opcode, user_mode(regs)); error = read_opcode(regs->pc, &opcode, user_mode(regs));
...@@ -737,23 +471,6 @@ static int misaligned_fixup(struct pt_regs *regs) ...@@ -737,23 +471,6 @@ static int misaligned_fixup(struct pt_regs *regs)
major = (opcode >> 26) & 0x3f; major = (opcode >> 26) & 0x3f;
minor = (opcode >> 16) & 0xf; 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) { switch (major) {
case (0x84>>2): /* LD.W */ case (0x84>>2): /* LD.W */
error = misaligned_load(regs, opcode, 1, 1, 1); error = misaligned_load(regs, opcode, 1, 1, 1);
...@@ -878,59 +595,202 @@ static int misaligned_fixup(struct pt_regs *regs) ...@@ -878,59 +595,202 @@ static int misaligned_fixup(struct pt_regs *regs)
regs->pc += 4; /* Skip the instruction that's just been emulated */ regs->pc += 4; /* Skip the instruction that's just been emulated */
return 0; return 0;
} }
}
static void do_unhandled_exception(int signr, char *str, unsigned long error,
struct pt_regs *regs)
{
if (user_mode(regs))
force_sig(signr, current);
die_if_no_fixup(str, regs, error);
} }
static ctl_table unaligned_table[] = { #define DO_ERROR(signr, str, name) \
{ asmlinkage void do_##name(unsigned long error_code, struct pt_regs *regs) \
.procname = "kernel_reports", { \
.data = &kernel_mode_unaligned_fixup_count, do_unhandled_exception(signr, str, error_code, regs); \
.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[] = { DO_ERROR(SIGILL, "illegal slot instruction", illegal_slot_inst)
{ DO_ERROR(SIGSEGV, "address error (exec)", address_error_exec)
.procname = "unaligned_fixup",
.mode = 0555, #if defined(CONFIG_SH64_ID2815_WORKAROUND)
.child = unaligned_table
}, #define OPCODE_INVALID 0
{} #define OPCODE_USER_VALID 1
}; #define OPCODE_PRIV_VALID 2
static ctl_table sh64_root[] = { /* getcon/putcon - requires checking which control register is referenced. */
{ #define OPCODE_CTRL_REG 3
.procname = "sh64",
.mode = 0555, /* Table of valid opcodes for SHmedia mode.
.child = unaligned_root 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
}; };
static struct ctl_table_header *sysctl_header;
static int __init init_sysctl(void) /* 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)
{ {
sysctl_header = register_sysctl_table(sh64_root); insn_size_t opcode = 0x6ff4fff0; /* guaranteed reserved opcode */
return 0; 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);
} }
__initcall(init_sysctl); #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) 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) ...@@ -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 of access we make to them - just go direct to their physical
addresses. */ addresses. */
exp_cause = peek_real_address_q(DM_EXP_CAUSE_PHY); 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", printk("DM.EXP_CAUSE had unexpected bits set (=%08lx)\n",
(unsigned long)(exp_cause & 0xffffffff)); (unsigned long)(exp_cause & 0xffffffff));
}
show_state(); show_state();
/* Clear all DEBUGINT causes */ /* Clear all DEBUGINT causes */
poke_real_address_q(DM_EXP_CAUSE_PHY, 0x0); poke_real_address_q(DM_EXP_CAUSE_PHY, 0x0);
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
# #
# Panic should really be compiled as PIC # 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 copy_user_memcpy.o copy_page.o strcpy.o strlen.o
# Extracted from libgcc # Extracted from libgcc
......
/*--------------------------------------------------------------------------
--
-- 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 @@ ...@@ -17,7 +17,7 @@
/** /**
* sh64_tlb_init - Perform initial setup for the DTLB and ITLB. * 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 */ /* Assign some sane DTLB defaults */
cpu_data->dtlb.entries = 64; cpu_data->dtlb.entries = 64;
......
menu "SuperH / SH-Mobile Driver Options" menu "SuperH / SH-Mobile Driver Options"
source "drivers/sh/intc/Kconfig" source "drivers/sh/intc/Kconfig"
source "drivers/sh/pfc/Kconfig"
endmenu endmenu
...@@ -5,6 +5,7 @@ obj-y := intc/ ...@@ -5,6 +5,7 @@ obj-y := intc/
obj-$(CONFIG_HAVE_CLK) += clk/ obj-$(CONFIG_HAVE_CLK) += clk/
obj-$(CONFIG_MAPLE) += maple/ obj-$(CONFIG_MAPLE) += maple/
obj-$(CONFIG_SH_PFC) += pfc/
obj-$(CONFIG_SUPERHYWAY) += superhyway/ obj-$(CONFIG_SUPERHYWAY) += superhyway/
obj-$(CONFIG_GENERIC_GPIO) += pfc.o
obj-y += pm_runtime.o obj-y += pm_runtime.o
...@@ -14,6 +14,8 @@ ...@@ -14,6 +14,8 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/sh_clk.h> #include <linux/sh_clk.h>
#define CPG_CKSTP_BIT BIT(8)
static unsigned int sh_clk_read(struct clk *clk) static unsigned int sh_clk_read(struct clk *clk)
{ {
if (clk->flags & CLK_ENABLE_REG_8BIT) if (clk->flags & CLK_ENABLE_REG_8BIT)
...@@ -66,71 +68,43 @@ int __init sh_clk_mstp_register(struct clk *clks, int nr) ...@@ -66,71 +68,43 @@ int __init sh_clk_mstp_register(struct clk *clks, int nr)
return ret; return ret;
} }
static long sh_clk_div_round_rate(struct clk *clk, unsigned long rate) /*
* Div/mult table lookup helpers
*/
static inline struct clk_div_table *clk_to_div_table(struct clk *clk)
{ {
return clk_rate_table_round(clk, clk->freq_table, rate); return clk->priv;
} }
static int sh_clk_div6_divisors[64] = { static inline struct clk_div_mult_table *clk_to_div_mult_table(struct clk *clk)
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, return clk_to_div_table(clk)->div_mult_table;
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, * Common div ops
.nr_divisors = ARRAY_SIZE(sh_clk_div6_divisors), */
}; static long sh_clk_div_round_rate(struct clk *clk, unsigned long rate)
{
return clk_rate_table_round(clk, clk->freq_table, rate);
}
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; unsigned int idx;
clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, 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; return clk->freq_table[idx].frequency;
} }
static int sh_clk_div6_set_parent(struct clk *clk, struct clk *parent) static int sh_clk_div_set_rate(struct clk *clk, unsigned long rate)
{
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)
{ {
struct clk_div_table *dt = clk_to_div_table(clk);
unsigned long value; unsigned long value;
int idx; int idx;
...@@ -139,51 +113,53 @@ static int sh_clk_div6_set_rate(struct clk *clk, unsigned long rate) ...@@ -139,51 +113,53 @@ static int sh_clk_div6_set_rate(struct clk *clk, unsigned long rate)
return idx; return idx;
value = sh_clk_read(clk); value = sh_clk_read(clk);
value &= ~0x3f; value &= ~(clk->div_mask << clk->enable_bit);
value |= idx; value |= (idx << clk->enable_bit);
sh_clk_write(value, clk); sh_clk_write(value, clk);
/* XXX: Should use a post-change notifier */
if (dt->kick)
dt->kick(clk);
return 0; return 0;
} }
static int sh_clk_div6_enable(struct clk *clk) static int sh_clk_div_enable(struct clk *clk)
{ {
unsigned long value; sh_clk_write(sh_clk_read(clk) & ~CPG_CKSTP_BIT, clk);
int ret; return 0;
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;
} }
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); val = sh_clk_read(clk);
value |= 0x100; /* stop clock */ val |= CPG_CKSTP_BIT;
value |= 0x3f; /* VDIV bits must be non-zero, overwrite divider */
sh_clk_write(value, clk); /*
* 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 = { static struct sh_clk_ops sh_clk_div_clk_ops = {
.recalc = sh_clk_div6_recalc, .recalc = sh_clk_div_recalc,
.set_rate = sh_clk_div_set_rate,
.round_rate = sh_clk_div_round_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 = { static struct sh_clk_ops sh_clk_div_enable_clk_ops = {
.recalc = sh_clk_div6_recalc, .recalc = sh_clk_div_recalc,
.set_rate = sh_clk_div_set_rate,
.round_rate = sh_clk_div_round_rate, .round_rate = sh_clk_div_round_rate,
.set_rate = sh_clk_div6_set_rate, .enable = sh_clk_div_enable,
.enable = sh_clk_div6_enable, .disable = sh_clk_div_disable,
.disable = sh_clk_div6_disable,
.set_parent = sh_clk_div6_set_parent,
}; };
static int __init sh_clk_init_parent(struct clk *clk) static int __init sh_clk_init_parent(struct clk *clk)
...@@ -218,12 +194,12 @@ 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; return 0;
} }
static int __init sh_clk_div6_register_ops(struct clk *clks, int nr, static int __init sh_clk_div_register_ops(struct clk *clks, int nr,
struct sh_clk_ops *ops) struct clk_div_table *table, struct sh_clk_ops *ops)
{ {
struct clk *clkp; struct clk *clkp;
void *freq_table; 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 freq_table_size = sizeof(struct cpufreq_frequency_table);
int ret = 0; int ret = 0;
int k; int k;
...@@ -231,7 +207,7 @@ static int __init sh_clk_div6_register_ops(struct clk *clks, int nr, ...@@ -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_size *= (nr_divs + 1);
freq_table = kzalloc(freq_table_size * nr, GFP_KERNEL); freq_table = kzalloc(freq_table_size * nr, GFP_KERNEL);
if (!freq_table) { 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; return -ENOMEM;
} }
...@@ -239,47 +215,98 @@ static int __init sh_clk_div6_register_ops(struct clk *clks, int nr, ...@@ -239,47 +215,98 @@ static int __init sh_clk_div6_register_ops(struct clk *clks, int nr,
clkp = clks + k; clkp = clks + k;
clkp->ops = ops; clkp->ops = ops;
clkp->priv = table;
clkp->freq_table = freq_table + (k * freq_table_size); clkp->freq_table = freq_table + (k * freq_table_size);
clkp->freq_table[nr_divs].frequency = CPUFREQ_TABLE_END; 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; return ret;
} }
int __init sh_clk_div6_register(struct clk *clks, int nr) /*
{ * div6 support
return sh_clk_div6_register_ops(clks, nr, &sh_clk_div6_clk_ops); */
} 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
};
int __init sh_clk_div6_reparent_register(struct clk *clks, int nr) static struct clk_div_mult_table div6_div_mult_table = {
{ .divisors = sh_clk_div6_divisors,
return sh_clk_div6_register_ops(clks, nr, .nr_divisors = ARRAY_SIZE(sh_clk_div6_divisors),
&sh_clk_div6_reparent_clk_ops); };
}
static unsigned long sh_clk_div4_recalc(struct clk *clk) 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_div4_table *d4t = clk->priv; struct clk_div_mult_table *table = clk_to_div_mult_table(clk);
struct clk_div_mult_table *table = d4t->div_mult_table; u32 value;
unsigned int idx; 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, clk_rate_table_build(clk, clk->freq_table, table->nr_divisors,
table, &clk->arch_flags); table, NULL);
idx = (sh_clk_read(clk) >> clk->enable_bit) & 0x000f; return 0;
}
return clk->freq_table[idx].frequency; 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_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_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) 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 = clk_to_div_mult_table(clk);
struct clk_div_mult_table *table = d4t->div_mult_table;
u32 value; u32 value;
int ret; int ret;
...@@ -306,107 +333,31 @@ static int sh_clk_div4_set_parent(struct clk *clk, struct clk *parent) ...@@ -306,107 +333,31 @@ static int sh_clk_div4_set_parent(struct clk *clk, struct clk *parent)
return 0; 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 = { static struct sh_clk_ops sh_clk_div4_reparent_clk_ops = {
.recalc = sh_clk_div4_recalc, .recalc = sh_clk_div_recalc,
.set_rate = sh_clk_div4_set_rate, .set_rate = sh_clk_div_set_rate,
.round_rate = sh_clk_div_round_rate, .round_rate = sh_clk_div_round_rate,
.enable = sh_clk_div4_enable, .enable = sh_clk_div_enable,
.disable = sh_clk_div4_disable, .disable = sh_clk_div_disable,
.set_parent = sh_clk_div4_set_parent, .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, int __init sh_clk_div4_register(struct clk *clks, int nr,
struct clk_div4_table *table) 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, int __init sh_clk_div4_enable_register(struct clk *clks, int nr,
struct clk_div4_table *table) struct clk_div4_table *table)
{ {
return sh_clk_div4_register_ops(clks, nr, table, return sh_clk_div_register_ops(clks, nr, table,
&sh_clk_div4_enable_clk_ops); &sh_clk_div_enable_clk_ops);
} }
int __init sh_clk_div4_reparent_register(struct clk *clks, int nr, int __init sh_clk_div4_reparent_register(struct clk *clks, int nr,
struct clk_div4_table *table) struct clk_div4_table *table)
{ {
return sh_clk_div4_register_ops(clks, nr, table, return sh_clk_div_register_ops(clks, nr, table,
&sh_clk_div4_reparent_clk_ops); &sh_clk_div4_reparent_clk_ops);
} }
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_BALANCING) += balancing.o
obj-$(CONFIG_INTC_USERIMASK) += userimask.o obj-$(CONFIG_INTC_USERIMASK) += userimask.o
......
/*
* 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 @@ static void __init intc_subgroup_map(struct intc_desc_int *d) ...@@ -219,12 +219,14 @@ static void __init intc_subgroup_map(struct intc_desc_int *d)
if (radix_tree_deref_retry(entry)) if (radix_tree_deref_retry(entry))
goto restart; goto restart;
irq = create_irq(); irq = irq_alloc_desc(numa_node_id());
if (unlikely(irq < 0)) { if (unlikely(irq < 0)) {
pr_err("no more free IRQs, bailing..\n"); pr_err("no more free IRQs, bailing..\n");
break; break;
} }
activate_irq(irq);
pr_info("Setting up a chained VIRQ from %d -> %d\n", pr_info("Setting up a chained VIRQ from %d -> %d\n",
irq, entry->pirq); irq, entry->pirq);
......
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.
obj-y += core.o
obj-$(CONFIG_PINCTRL_SH_PFC) += pinctrl.o
obj-$(CONFIG_GPIO_SH_PFC) += gpio.o
/* /*
* Pinmuxed GPIO support for SuperH. * SuperH Pin Function Controller support.
* *
* Copyright (C) 2008 Magnus Damm * 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 * 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 * License. See the file "COPYING" in the main directory of this archive
* for more details. * for more details.
*/ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define pr_fmt(fmt) "sh_pfc " KBUILD_MODNAME ": " fmt
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/list.h> #include <linux/sh_pfc.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/clk.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/irq.h>
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/gpio.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/pinctrl/machine.h>
static void pfc_iounmap(struct pinmux_info *pip) 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; int k;
for (k = 0; k < pip->num_resources; k++) for (k = 0; k < pfc->num_resources; k++)
if (pip->window[k].virt) if (pfc->window[k].virt)
iounmap(pip->window[k].virt); iounmap(pfc->window[k].virt);
kfree(pip->window); kfree(pfc->window);
pip->window = NULL; pfc->window = NULL;
} }
static int pfc_ioremap(struct pinmux_info *pip) static int pfc_ioremap(struct sh_pfc *pfc)
{ {
struct resource *res; struct resource *res;
int k; int k;
if (!pip->num_resources) if (!pfc->num_resources)
return 0; return 0;
pip->window = kzalloc(pip->num_resources * sizeof(*pip->window), pfc->window = kzalloc(pfc->num_resources * sizeof(*pfc->window),
GFP_NOWAIT); GFP_NOWAIT);
if (!pip->window) if (!pfc->window)
goto err1; goto err1;
for (k = 0; k < pip->num_resources; k++) { for (k = 0; k < pfc->num_resources; k++) {
res = pip->resource + k; res = pfc->resource + k;
WARN_ON(resource_type(res) != IORESOURCE_MEM); WARN_ON(resource_type(res) != IORESOURCE_MEM);
pip->window[k].phys = res->start; pfc->window[k].phys = res->start;
pip->window[k].size = resource_size(res); pfc->window[k].size = resource_size(res);
pip->window[k].virt = ioremap_nocache(res->start, pfc->window[k].virt = ioremap_nocache(res->start,
resource_size(res)); resource_size(res));
if (!pip->window[k].virt) if (!pfc->window[k].virt)
goto err2; goto err2;
} }
return 0; return 0;
err2: err2:
pfc_iounmap(pip); pfc_iounmap(pfc);
err1: err1:
return -1; return -1;
} }
static void __iomem *pfc_phys_to_virt(struct pinmux_info *pip, static void __iomem *pfc_phys_to_virt(struct sh_pfc *pfc,
unsigned long address) unsigned long address)
{ {
struct pfc_window *window; struct pfc_window *window;
int k; int k;
/* scan through physical windows and convert address */ /* scan through physical windows and convert address */
for (k = 0; k < pip->num_resources; k++) { for (k = 0; k < pfc->num_resources; k++) {
window = pip->window + k; window = pfc->window + k;
if (address < window->phys) if (address < window->phys)
continue; continue;
...@@ -135,8 +141,7 @@ static void gpio_write_raw_reg(void __iomem *mapped_reg, ...@@ -135,8 +141,7 @@ static void gpio_write_raw_reg(void __iomem *mapped_reg,
BUG(); BUG();
} }
static int gpio_read_bit(struct pinmux_data_reg *dr, int sh_pfc_read_bit(struct pinmux_data_reg *dr, unsigned long in_pos)
unsigned long in_pos)
{ {
unsigned long pos; unsigned long pos;
...@@ -147,9 +152,10 @@ static int gpio_read_bit(struct pinmux_data_reg *dr, ...@@ -147,9 +152,10 @@ static int gpio_read_bit(struct pinmux_data_reg *dr,
return (gpio_read_raw_reg(dr->mapped_reg, dr->reg_width) >> pos) & 1; return (gpio_read_raw_reg(dr->mapped_reg, dr->reg_width) >> pos) & 1;
} }
EXPORT_SYMBOL_GPL(sh_pfc_read_bit);
static void gpio_write_bit(struct pinmux_data_reg *dr, void sh_pfc_write_bit(struct pinmux_data_reg *dr, unsigned long in_pos,
unsigned long in_pos, unsigned long value) unsigned long value)
{ {
unsigned long pos; unsigned long pos;
...@@ -166,8 +172,9 @@ static void gpio_write_bit(struct pinmux_data_reg *dr, ...@@ -166,8 +172,9 @@ static void gpio_write_bit(struct pinmux_data_reg *dr,
gpio_write_raw_reg(dr->mapped_reg, dr->reg_width, 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 pinmux_info *gpioc, static void config_reg_helper(struct sh_pfc *pfc,
struct pinmux_cfg_reg *crp, struct pinmux_cfg_reg *crp,
unsigned long in_pos, unsigned long in_pos,
void __iomem **mapped_regp, void __iomem **mapped_regp,
...@@ -176,7 +183,7 @@ static void config_reg_helper(struct pinmux_info *gpioc, ...@@ -176,7 +183,7 @@ static void config_reg_helper(struct pinmux_info *gpioc,
{ {
int k; int k;
*mapped_regp = pfc_phys_to_virt(gpioc, crp->reg); *mapped_regp = pfc_phys_to_virt(pfc, crp->reg);
if (crp->field_width) { if (crp->field_width) {
*maskp = (1 << crp->field_width) - 1; *maskp = (1 << crp->field_width) - 1;
...@@ -189,14 +196,14 @@ static void config_reg_helper(struct pinmux_info *gpioc, ...@@ -189,14 +196,14 @@ static void config_reg_helper(struct pinmux_info *gpioc,
} }
} }
static int read_config_reg(struct pinmux_info *gpioc, static int read_config_reg(struct sh_pfc *pfc,
struct pinmux_cfg_reg *crp, struct pinmux_cfg_reg *crp,
unsigned long field) unsigned long field)
{ {
void __iomem *mapped_reg; void __iomem *mapped_reg;
unsigned long mask, pos; unsigned long mask, pos;
config_reg_helper(gpioc, crp, field, &mapped_reg, &mask, &pos); config_reg_helper(pfc, crp, field, &mapped_reg, &mask, &pos);
pr_debug("read_reg: addr = %lx, field = %ld, " pr_debug("read_reg: addr = %lx, field = %ld, "
"r_width = %ld, f_width = %ld\n", "r_width = %ld, f_width = %ld\n",
...@@ -205,14 +212,14 @@ static int read_config_reg(struct pinmux_info *gpioc, ...@@ -205,14 +212,14 @@ static int read_config_reg(struct pinmux_info *gpioc,
return (gpio_read_raw_reg(mapped_reg, crp->reg_width) >> pos) & mask; return (gpio_read_raw_reg(mapped_reg, crp->reg_width) >> pos) & mask;
} }
static void write_config_reg(struct pinmux_info *gpioc, static void write_config_reg(struct sh_pfc *pfc,
struct pinmux_cfg_reg *crp, struct pinmux_cfg_reg *crp,
unsigned long field, unsigned long value) unsigned long field, unsigned long value)
{ {
void __iomem *mapped_reg; void __iomem *mapped_reg;
unsigned long mask, pos, data; unsigned long mask, pos, data;
config_reg_helper(gpioc, crp, field, &mapped_reg, &mask, &pos); config_reg_helper(pfc, crp, field, &mapped_reg, &mask, &pos);
pr_debug("write_reg addr = %lx, value = %ld, field = %ld, " pr_debug("write_reg addr = %lx, value = %ld, field = %ld, "
"r_width = %ld, f_width = %ld\n", "r_width = %ld, f_width = %ld\n",
...@@ -225,30 +232,30 @@ static void write_config_reg(struct pinmux_info *gpioc, ...@@ -225,30 +232,30 @@ static void write_config_reg(struct pinmux_info *gpioc,
data &= mask; data &= mask;
data |= value; data |= value;
if (gpioc->unlock_reg) if (pfc->unlock_reg)
gpio_write_raw_reg(pfc_phys_to_virt(gpioc, gpioc->unlock_reg), gpio_write_raw_reg(pfc_phys_to_virt(pfc, pfc->unlock_reg),
32, ~data); 32, ~data);
gpio_write_raw_reg(mapped_reg, crp->reg_width, data); gpio_write_raw_reg(mapped_reg, crp->reg_width, data);
} }
static int setup_data_reg(struct pinmux_info *gpioc, unsigned gpio) static int setup_data_reg(struct sh_pfc *pfc, unsigned gpio)
{ {
struct pinmux_gpio *gpiop = &gpioc->gpios[gpio]; struct pinmux_gpio *gpiop = &pfc->gpios[gpio];
struct pinmux_data_reg *data_reg; struct pinmux_data_reg *data_reg;
int k, n; int k, n;
if (!enum_in_range(gpiop->enum_id, &gpioc->data)) if (!enum_in_range(gpiop->enum_id, &pfc->data))
return -1; return -1;
k = 0; k = 0;
while (1) { while (1) {
data_reg = gpioc->data_regs + k; data_reg = pfc->data_regs + k;
if (!data_reg->reg_width) if (!data_reg->reg_width)
break; break;
data_reg->mapped_reg = pfc_phys_to_virt(gpioc, data_reg->reg); data_reg->mapped_reg = pfc_phys_to_virt(pfc, data_reg->reg);
for (n = 0; n < data_reg->reg_width; n++) { for (n = 0; n < data_reg->reg_width; n++) {
if (data_reg->enum_ids[n] == gpiop->enum_id) { if (data_reg->enum_ids[n] == gpiop->enum_id) {
...@@ -267,17 +274,17 @@ static int setup_data_reg(struct pinmux_info *gpioc, unsigned gpio) ...@@ -267,17 +274,17 @@ static int setup_data_reg(struct pinmux_info *gpioc, unsigned gpio)
return -1; return -1;
} }
static void setup_data_regs(struct pinmux_info *gpioc) static void setup_data_regs(struct sh_pfc *pfc)
{ {
struct pinmux_data_reg *drp; struct pinmux_data_reg *drp;
int k; int k;
for (k = gpioc->first_gpio; k <= gpioc->last_gpio; k++) for (k = pfc->first_gpio; k <= pfc->last_gpio; k++)
setup_data_reg(gpioc, k); setup_data_reg(pfc, k);
k = 0; k = 0;
while (1) { while (1) {
drp = gpioc->data_regs + k; drp = pfc->data_regs + k;
if (!drp->reg_width) if (!drp->reg_width)
break; break;
...@@ -288,23 +295,24 @@ static void setup_data_regs(struct pinmux_info *gpioc) ...@@ -288,23 +295,24 @@ static void setup_data_regs(struct pinmux_info *gpioc)
} }
} }
static int get_data_reg(struct pinmux_info *gpioc, unsigned gpio, int sh_pfc_get_data_reg(struct sh_pfc *pfc, unsigned gpio,
struct pinmux_data_reg **drp, int *bitp) struct pinmux_data_reg **drp, int *bitp)
{ {
struct pinmux_gpio *gpiop = &gpioc->gpios[gpio]; struct pinmux_gpio *gpiop = &pfc->gpios[gpio];
int k, n; int k, n;
if (!enum_in_range(gpiop->enum_id, &gpioc->data)) if (!enum_in_range(gpiop->enum_id, &pfc->data))
return -1; return -1;
k = (gpiop->flags & PINMUX_FLAG_DREG) >> PINMUX_FLAG_DREG_SHIFT; k = (gpiop->flags & PINMUX_FLAG_DREG) >> PINMUX_FLAG_DREG_SHIFT;
n = (gpiop->flags & PINMUX_FLAG_DBIT) >> PINMUX_FLAG_DBIT_SHIFT; n = (gpiop->flags & PINMUX_FLAG_DBIT) >> PINMUX_FLAG_DBIT_SHIFT;
*drp = gpioc->data_regs + k; *drp = pfc->data_regs + k;
*bitp = n; *bitp = n;
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(sh_pfc_get_data_reg);
static int get_config_reg(struct pinmux_info *gpioc, pinmux_enum_t enum_id, static int get_config_reg(struct sh_pfc *pfc, pinmux_enum_t enum_id,
struct pinmux_cfg_reg **crp, struct pinmux_cfg_reg **crp,
int *fieldp, int *valuep, int *fieldp, int *valuep,
unsigned long **cntp) unsigned long **cntp)
...@@ -315,7 +323,7 @@ static int get_config_reg(struct pinmux_info *gpioc, pinmux_enum_t enum_id, ...@@ -315,7 +323,7 @@ static int get_config_reg(struct pinmux_info *gpioc, pinmux_enum_t enum_id,
k = 0; k = 0;
while (1) { while (1) {
config_reg = gpioc->cfg_regs + k; config_reg = pfc->cfg_regs + k;
r_width = config_reg->reg_width; r_width = config_reg->reg_width;
f_width = config_reg->field_width; f_width = config_reg->field_width;
...@@ -350,15 +358,15 @@ static int get_config_reg(struct pinmux_info *gpioc, pinmux_enum_t enum_id, ...@@ -350,15 +358,15 @@ static int get_config_reg(struct pinmux_info *gpioc, pinmux_enum_t enum_id,
return -1; return -1;
} }
static int get_gpio_enum_id(struct pinmux_info *gpioc, unsigned gpio, int sh_pfc_gpio_to_enum(struct sh_pfc *pfc, unsigned gpio, int pos,
int pos, pinmux_enum_t *enum_idp) pinmux_enum_t *enum_idp)
{ {
pinmux_enum_t enum_id = gpioc->gpios[gpio].enum_id; pinmux_enum_t enum_id = pfc->gpios[gpio].enum_id;
pinmux_enum_t *data = gpioc->gpio_data; pinmux_enum_t *data = pfc->gpio_data;
int k; int k;
if (!enum_in_range(enum_id, &gpioc->data)) { if (!enum_in_range(enum_id, &pfc->data)) {
if (!enum_in_range(enum_id, &gpioc->mark)) { if (!enum_in_range(enum_id, &pfc->mark)) {
pr_err("non data/mark enum_id for gpio %d\n", gpio); pr_err("non data/mark enum_id for gpio %d\n", gpio);
return -1; return -1;
} }
...@@ -369,7 +377,7 @@ static int get_gpio_enum_id(struct pinmux_info *gpioc, unsigned gpio, ...@@ -369,7 +377,7 @@ static int get_gpio_enum_id(struct pinmux_info *gpioc, unsigned gpio,
return pos + 1; return pos + 1;
} }
for (k = 0; k < gpioc->gpio_data_size; k++) { for (k = 0; k < pfc->gpio_data_size; k++) {
if (data[k] == enum_id) { if (data[k] == enum_id) {
*enum_idp = data[k + 1]; *enum_idp = data[k + 1];
return k + 1; return k + 1;
...@@ -379,11 +387,10 @@ static int get_gpio_enum_id(struct pinmux_info *gpioc, unsigned gpio, ...@@ -379,11 +387,10 @@ static int get_gpio_enum_id(struct pinmux_info *gpioc, unsigned gpio,
pr_err("cannot locate data/mark enum_id for gpio %d\n", gpio); pr_err("cannot locate data/mark enum_id for gpio %d\n", gpio);
return -1; return -1;
} }
EXPORT_SYMBOL_GPL(sh_pfc_gpio_to_enum);
enum { GPIO_CFG_DRYRUN, GPIO_CFG_REQ, GPIO_CFG_FREE }; int sh_pfc_config_gpio(struct sh_pfc *pfc, unsigned gpio, int pinmux_type,
int cfg_mode)
static int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio,
int pinmux_type, int cfg_mode)
{ {
struct pinmux_cfg_reg *cr = NULL; struct pinmux_cfg_reg *cr = NULL;
pinmux_enum_t enum_id; pinmux_enum_t enum_id;
...@@ -398,19 +405,19 @@ static int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio, ...@@ -398,19 +405,19 @@ static int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio,
break; break;
case PINMUX_TYPE_OUTPUT: case PINMUX_TYPE_OUTPUT:
range = &gpioc->output; range = &pfc->output;
break; break;
case PINMUX_TYPE_INPUT: case PINMUX_TYPE_INPUT:
range = &gpioc->input; range = &pfc->input;
break; break;
case PINMUX_TYPE_INPUT_PULLUP: case PINMUX_TYPE_INPUT_PULLUP:
range = &gpioc->input_pu; range = &pfc->input_pu;
break; break;
case PINMUX_TYPE_INPUT_PULLDOWN: case PINMUX_TYPE_INPUT_PULLDOWN:
range = &gpioc->input_pd; range = &pfc->input_pd;
break; break;
default: default:
...@@ -422,7 +429,7 @@ static int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio, ...@@ -422,7 +429,7 @@ static int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio,
field = 0; field = 0;
value = 0; value = 0;
while (1) { while (1) {
pos = get_gpio_enum_id(gpioc, gpio, pos, &enum_id); pos = sh_pfc_gpio_to_enum(pfc, gpio, pos, &enum_id);
if (pos <= 0) if (pos <= 0)
goto out_err; goto out_err;
...@@ -430,7 +437,7 @@ static int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio, ...@@ -430,7 +437,7 @@ static int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio,
break; break;
/* first check if this is a function enum */ /* first check if this is a function enum */
in_range = enum_in_range(enum_id, &gpioc->function); in_range = enum_in_range(enum_id, &pfc->function);
if (!in_range) { if (!in_range) {
/* not a function enum */ /* not a function enum */
if (range) { if (range) {
...@@ -467,19 +474,19 @@ static int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio, ...@@ -467,19 +474,19 @@ static int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio,
if (!in_range) if (!in_range)
continue; continue;
if (get_config_reg(gpioc, enum_id, &cr, if (get_config_reg(pfc, enum_id, &cr,
&field, &value, &cntp) != 0) &field, &value, &cntp) != 0)
goto out_err; goto out_err;
switch (cfg_mode) { switch (cfg_mode) {
case GPIO_CFG_DRYRUN: case GPIO_CFG_DRYRUN:
if (!*cntp || if (!*cntp ||
(read_config_reg(gpioc, cr, field) != value)) (read_config_reg(pfc, cr, field) != value))
continue; continue;
break; break;
case GPIO_CFG_REQ: case GPIO_CFG_REQ:
write_config_reg(gpioc, cr, field, value); write_config_reg(pfc, cr, field, value);
*cntp = *cntp + 1; *cntp = *cntp + 1;
break; break;
...@@ -493,247 +500,73 @@ static int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio, ...@@ -493,247 +500,73 @@ static int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio,
out_err: out_err:
return -1; return -1;
} }
EXPORT_SYMBOL_GPL(sh_pfc_config_gpio);
static DEFINE_SPINLOCK(gpio_lock); int register_sh_pfc(struct sh_pfc *pfc)
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); int (*initroutine)(struct sh_pfc *) = NULL;
unsigned long flags;
int ret; int ret;
sh_gpio_set_value(gpioc, offset, value); /*
spin_lock_irqsave(&gpio_lock, flags); * Ensure that the type encoding fits
ret = pinmux_direction(gpioc, offset, PINMUX_TYPE_OUTPUT); */
spin_unlock_irqrestore(&gpio_lock, flags); BUILD_BUG_ON(PINMUX_FLAG_TYPE > ((1 << PINMUX_FLAG_DBIT_SHIFT) - 1));
return ret;
}
static int sh_gpio_get_value(struct pinmux_info *gpioc, unsigned gpio) if (sh_pfc)
{ return -EBUSY;
struct pinmux_data_reg *dr = NULL;
int bit = 0;
if (!gpioc || get_data_reg(gpioc, gpio, &dr, &bit) != 0) ret = pfc_ioremap(pfc);
return -EINVAL; if (unlikely(ret < 0))
return ret;
return gpio_read_bit(dr, bit); spin_lock_init(&pfc->lock);
}
static int sh_gpio_get(struct gpio_chip *chip, unsigned offset) pinctrl_provide_dummies();
{ setup_data_regs(pfc);
return sh_gpio_get_value(chip_to_pinmux(chip), offset);
}
static void sh_gpio_set(struct gpio_chip *chip, unsigned offset, int value) sh_pfc = pfc;
{
sh_gpio_set_value(chip_to_pinmux(chip), offset, value);
}
static int sh_gpio_to_irq(struct gpio_chip *chip, unsigned offset) /*
{ * Initialize pinctrl bindings first
struct pinmux_info *gpioc = chip_to_pinmux(chip); */
pinmux_enum_t enum_id; initroutine = symbol_request(sh_pfc_register_pinctrl);
pinmux_enum_t *enum_ids; if (initroutine) {
int i, k, pos; ret = (*initroutine)(pfc);
symbol_put_addr(initroutine);
pos = 0; if (unlikely(ret != 0))
enum_id = 0; goto err;
while (1) { } else {
pos = get_gpio_enum_id(gpioc, offset, pos, &enum_id); pr_err("failed to initialize pinctrl bindings\n");
if (pos <= 0 || !enum_id) goto err;
break; }
for (i = 0; i < gpioc->gpio_irq_size; i++) { /*
enum_ids = gpioc->gpio_irq[i].enum_ids; * Then the GPIO chip
for (k = 0; enum_ids[k]; k++) { */
if (enum_ids[k] == enum_id) initroutine = symbol_request(sh_pfc_register_gpiochip);
return gpioc->gpio_irq[i].irq; 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;
} }
} }
return -ENOSYS; pr_info("%s support registered\n", pfc->name);
}
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; return 0;
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); err:
if (ret < 0) pfc_iounmap(pfc);
pfc_iounmap(pip); sh_pfc = NULL;
return ret; return ret;
} }
int unregister_pinmux(struct pinmux_info *pip)
{
pr_info("%s deregistering\n", pip->name);
pfc_iounmap(pip);
return gpiochip_remove(&pip->chip);
}
/*
* 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");
/*
* 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 { ...@@ -18,7 +18,6 @@ struct clk_mapping {
struct kref ref; struct kref ref;
}; };
struct sh_clk_ops { struct sh_clk_ops {
#ifdef CONFIG_SH_CLK_CPG_LEGACY #ifdef CONFIG_SH_CLK_CPG_LEGACY
void (*init)(struct clk *clk); void (*init)(struct clk *clk);
...@@ -31,6 +30,10 @@ struct sh_clk_ops { ...@@ -31,6 +30,10 @@ struct sh_clk_ops {
long (*round_rate)(struct clk *clk, unsigned long rate); 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 clk {
struct list_head node; struct list_head node;
struct clk *parent; struct clk *parent;
...@@ -52,6 +55,7 @@ struct clk { ...@@ -52,6 +55,7 @@ struct clk {
unsigned int enable_bit; unsigned int enable_bit;
void __iomem *mapped_reg; void __iomem *mapped_reg;
unsigned int div_mask;
unsigned long arch_flags; unsigned long arch_flags;
void *priv; void *priv;
struct clk_mapping *mapping; struct clk_mapping *mapping;
...@@ -65,6 +69,8 @@ struct clk { ...@@ -65,6 +69,8 @@ struct clk {
#define CLK_ENABLE_REG_16BIT BIT(2) #define CLK_ENABLE_REG_16BIT BIT(2)
#define CLK_ENABLE_REG_8BIT BIT(3) #define CLK_ENABLE_REG_8BIT BIT(3)
#define CLK_MASK_DIV_ON_DISABLE BIT(4)
#define CLK_ENABLE_REG_MASK (CLK_ENABLE_REG_32BIT | \ #define CLK_ENABLE_REG_MASK (CLK_ENABLE_REG_32BIT | \
CLK_ENABLE_REG_16BIT | \ CLK_ENABLE_REG_16BIT | \
CLK_ENABLE_REG_8BIT) CLK_ENABLE_REG_8BIT)
...@@ -146,14 +152,17 @@ static inline int __deprecated sh_clk_mstp32_register(struct clk *clks, int nr) ...@@ -146,14 +152,17 @@ static inline int __deprecated sh_clk_mstp32_register(struct clk *clks, int nr)
.enable_reg = (void __iomem *)_reg, \ .enable_reg = (void __iomem *)_reg, \
.enable_bit = _shift, \ .enable_bit = _shift, \
.arch_flags = _div_bitmap, \ .arch_flags = _div_bitmap, \
.div_mask = SH_CLK_DIV4_MSK, \
.flags = _flags, \ .flags = _flags, \
} }
struct clk_div4_table { struct clk_div_table {
struct clk_div_mult_table *div_mult_table; struct clk_div_mult_table *div_mult_table;
void (*kick)(struct clk *clk); void (*kick)(struct clk *clk);
}; };
#define clk_div4_table clk_div_table
int sh_clk_div4_register(struct clk *clks, int nr, int sh_clk_div4_register(struct clk *clks, int nr,
struct clk_div4_table *table); struct clk_div4_table *table);
int sh_clk_div4_enable_register(struct clk *clks, int nr, 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, ...@@ -165,7 +174,9 @@ int sh_clk_div4_reparent_register(struct clk *clks, int nr,
_num_parents, _src_shift, _src_width) \ _num_parents, _src_shift, _src_width) \
{ \ { \
.enable_reg = (void __iomem *)_reg, \ .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_table = _parents, \
.parent_num = _num_parents, \ .parent_num = _num_parents, \
.src_shift = _src_shift, \ .src_shift = _src_shift, \
...@@ -176,7 +187,9 @@ int sh_clk_div4_reparent_register(struct clk *clks, int nr, ...@@ -176,7 +187,9 @@ int sh_clk_div4_reparent_register(struct clk *clks, int nr,
{ \ { \
.parent = _parent, \ .parent = _parent, \
.enable_reg = (void __iomem *)_reg, \ .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); int sh_clk_div6_register(struct clk *clks, int nr);
......
...@@ -11,22 +11,24 @@ ...@@ -11,22 +11,24 @@
#ifndef __SH_PFC_H #ifndef __SH_PFC_H
#define __SH_PFC_H #define __SH_PFC_H
#include <linux/stringify.h>
#include <asm-generic/gpio.h> #include <asm-generic/gpio.h>
typedef unsigned short pinmux_enum_t; typedef unsigned short pinmux_enum_t;
typedef unsigned short pinmux_flag_t; typedef unsigned short pinmux_flag_t;
#define PINMUX_TYPE_NONE 0 enum {
#define PINMUX_TYPE_FUNCTION 1 PINMUX_TYPE_NONE,
#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
#define PINMUX_FLAG_TYPE (0x7) PINMUX_TYPE_FUNCTION,
#define PINMUX_FLAG_WANT_PULLUP (1 << 3) PINMUX_TYPE_GPIO,
#define PINMUX_FLAG_WANT_PULLDOWN (1 << 4) 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_SHIFT 5
#define PINMUX_FLAG_DBIT (0x1f << PINMUX_FLAG_DBIT_SHIFT) #define PINMUX_FLAG_DBIT (0x1f << PINMUX_FLAG_DBIT_SHIFT)
...@@ -36,9 +38,12 @@ typedef unsigned short pinmux_flag_t; ...@@ -36,9 +38,12 @@ typedef unsigned short pinmux_flag_t;
struct pinmux_gpio { struct pinmux_gpio {
pinmux_enum_t enum_id; pinmux_enum_t enum_id;
pinmux_flag_t flags; 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 #define PINMUX_DATA(data_or_mark, ids...) data_or_mark, ids, 0
struct pinmux_cfg_reg { struct pinmux_cfg_reg {
...@@ -89,7 +94,7 @@ struct pfc_window { ...@@ -89,7 +94,7 @@ struct pfc_window {
unsigned long size; unsigned long size;
}; };
struct pinmux_info { struct sh_pfc {
char *name; char *name;
pinmux_enum_t reserved_id; pinmux_enum_t reserved_id;
struct pinmux_range data; struct pinmux_range data;
...@@ -112,17 +117,45 @@ struct pinmux_info { ...@@ -112,17 +117,45 @@ struct pinmux_info {
struct pinmux_irq *gpio_irq; struct pinmux_irq *gpio_irq;
unsigned int gpio_irq_size; unsigned int gpio_irq_size;
spinlock_t lock;
struct resource *resource; struct resource *resource;
unsigned int num_resources; unsigned int num_resources;
struct pfc_window *window; struct pfc_window *window;
unsigned long unlock_reg; unsigned long unlock_reg;
struct gpio_chip chip;
}; };
int register_pinmux(struct pinmux_info *pip); /* XXX compat for now */
int unregister_pinmux(struct pinmux_info *pip); #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 */ /* helper macro for port */
#define PORT_1(fn, pfx, sfx) fn(pfx, sfx) #define PORT_1(fn, pfx, sfx) fn(pfx, sfx)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment