From e1d0be616186906ddc2a23bdf643c02551feebf0 Mon Sep 17 00:00:00 2001 From: Yoshinori Sato Date: Wed, 28 Jan 2015 02:53:55 +0900 Subject: sh-sci: Add h8300 SCI Signed-off-by: Yoshinori Sato Acked-by: Geert Uytterhoeven --- Documentation/devicetree/bindings/serial/renesas,sci-serial.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt b/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt index ae73bb0e9ad9..7534d46e9ad8 100644 --- a/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt +++ b/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt @@ -29,6 +29,7 @@ Required properties: - "renesas,scifa" for generic SCIFA compatible UART. - "renesas,scifb" for generic SCIFB compatible UART. - "renesas,hscif" for generic HSCIF compatible UART. + - "renesas,sci" for generic SCI compatible UART. When compatible with the generic version, nodes must list the SoC-specific version corresponding to the platform first followed by the -- cgit v1.2.3 From d8b0bdb444ed0e2bd5f9d3ea7412cb90bdec58cf Mon Sep 17 00:00:00 2001 From: Yoshinori Sato Date: Mon, 11 May 2015 02:31:32 +0900 Subject: h8300: kernel startup Signed-off-by: Yoshinori Sato --- Documentation/devicetree/bindings/h8300/cpu.txt | 13 ++ .../memory-controllers/renesas,h8300-bsc.txt | 12 + arch/h8300/kernel/setup.c | 255 +++++++++++++++++++++ 3 files changed, 280 insertions(+) create mode 100644 Documentation/devicetree/bindings/h8300/cpu.txt create mode 100644 Documentation/devicetree/bindings/memory-controllers/renesas,h8300-bsc.txt create mode 100644 arch/h8300/kernel/setup.c (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/h8300/cpu.txt b/Documentation/devicetree/bindings/h8300/cpu.txt new file mode 100644 index 000000000000..70cd58608f4b --- /dev/null +++ b/Documentation/devicetree/bindings/h8300/cpu.txt @@ -0,0 +1,13 @@ +* H8/300 CPU bindings + +Required properties: + +- compatible: Compatible property value should be "renesas,h8300". +- clock-frequency: Contains the clock frequency for CPU, in Hz. + +Example: + + cpu@0 { + compatible = "renesas,h8300"; + clock-frequency = <20000000>; + }; diff --git a/Documentation/devicetree/bindings/memory-controllers/renesas,h8300-bsc.txt b/Documentation/devicetree/bindings/memory-controllers/renesas,h8300-bsc.txt new file mode 100644 index 000000000000..cdf406c902e2 --- /dev/null +++ b/Documentation/devicetree/bindings/memory-controllers/renesas,h8300-bsc.txt @@ -0,0 +1,12 @@ +* H8/300 bus controller + +Required properties: + - compatible: Must be "renesas,h8300-bsc". + - reg: Base address and length of BSC registers. + +Example. + bsc: memory-controller@fee01e { + compatible = "renesas,h8300h-bsc", "renesas,h8300-bsc"; + reg = <0xfee01e 8>; + }; + diff --git a/arch/h8300/kernel/setup.c b/arch/h8300/kernel/setup.c new file mode 100644 index 000000000000..0fd1fe65c0b8 --- /dev/null +++ b/arch/h8300/kernel/setup.c @@ -0,0 +1,255 @@ +/* + * linux/arch/h8300/kernel/setup.c + * + * Copyright (C) 2001-2014 Yoshinori Sato + */ + +/* + * This file handles the architecture-dependent parts of system setup + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#if defined(CONFIG_CPU_H8300H) +#define CPU "H8/300H" +#elif defined(CONFIG_CPU_H8S) +#define CPU "H8S" +#else +#define CPU "Unknown" +#endif + +unsigned long memory_start; +unsigned long memory_end; +EXPORT_SYMBOL(memory_end); +static unsigned long freq; +extern char __dtb_start[]; + +#ifdef CONFIG_VT +struct screen_info screen_info; +#endif + +char __initdata command_line[COMMAND_LINE_SIZE]; + +void sim_console_register(void); + +void __init h8300_fdt_init(void *fdt, char *bootargs) +{ + if (!fdt) + fdt = __dtb_start; + else + strcpy(command_line, bootargs); + + early_init_dt_scan(fdt); + memblock_allow_resize(); +} + +static void __init bootmem_init(void) +{ + int bootmap_size; + unsigned long ram_start_pfn; + unsigned long free_ram_start_pfn; + unsigned long ram_end_pfn; + struct memblock_region *region; + + memory_end = memory_start = 0; + + /* Find main memory where is the kernel */ + for_each_memblock(memory, region) { + memory_start = region->base; + memory_end = region->base + region->size; + } + + if (!memory_end) + panic("No memory!"); + + ram_start_pfn = PFN_UP(memory_start); + /* free_ram_start_pfn is first page after kernel */ + free_ram_start_pfn = PFN_UP(__pa(_end)); + ram_end_pfn = PFN_DOWN(memblock_end_of_DRAM()); + + max_pfn = ram_end_pfn; + + /* + * give all the memory to the bootmap allocator, tell it to put the + * boot mem_map at the start of memory + */ + bootmap_size = init_bootmem_node(NODE_DATA(0), + free_ram_start_pfn, + 0, + ram_end_pfn); + /* + * free the usable memory, we have to make sure we do not free + * the bootmem bitmap so we then reserve it after freeing it :-) + */ + free_bootmem(PFN_PHYS(free_ram_start_pfn), + (ram_end_pfn - free_ram_start_pfn) << PAGE_SHIFT); + reserve_bootmem(PFN_PHYS(free_ram_start_pfn), bootmap_size, + BOOTMEM_DEFAULT); + + for_each_memblock(reserved, region) { + reserve_bootmem(region->base, region->size, BOOTMEM_DEFAULT); + } +} + +void __init setup_arch(char **cmdline_p) +{ + unflatten_and_copy_device_tree(); + + init_mm.start_code = (unsigned long) _stext; + init_mm.end_code = (unsigned long) _etext; + init_mm.end_data = (unsigned long) _edata; + init_mm.brk = (unsigned long) 0; + + pr_notice("\r\n\nuClinux " CPU "\n"); + pr_notice("Flat model support (C) 1998,1999 Kenneth Albanowski, D. Jeff Dionne\n"); + + if (*command_line) + strcpy(boot_command_line, command_line); + *cmdline_p = boot_command_line; + + parse_early_param(); + + bootmem_init(); +#if defined(CONFIG_H8300H_SIM) || defined(CONFIG_H8S_SIM) + sim_console_register(); +#endif + + early_platform_driver_probe("earlyprintk", 1, 0); + /* + * get kmalloc into gear + */ + paging_init(); +} + +/* + * Get CPU information for use by the procfs. + */ + +static int show_cpuinfo(struct seq_file *m, void *v) +{ + char *cpu; + + cpu = CPU; + + seq_printf(m, "CPU:\t\t%s\n" + "Clock:\t\t%lu.%1luMHz\n" + "BogoMips:\t%lu.%02lu\n" + "Calibration:\t%lu loops\n", + cpu, + freq/1000, freq%1000, + (loops_per_jiffy*HZ)/500000, + ((loops_per_jiffy*HZ)/5000)%100, + (loops_per_jiffy*HZ)); + + return 0; +} + +static void *c_start(struct seq_file *m, loff_t *pos) +{ + return *pos < num_possible_cpus() ? + ((void *) 0x12345678) : NULL; +} + +static void *c_next(struct seq_file *m, void *v, loff_t *pos) +{ + ++*pos; + return c_start(m, pos); +} + +static void c_stop(struct seq_file *m, void *v) +{ +} + +const struct seq_operations cpuinfo_op = { + .start = c_start, + .next = c_next, + .stop = c_stop, + .show = show_cpuinfo, +}; + +static int __init device_probe(void) +{ + of_platform_populate(NULL, NULL, NULL, NULL); + + return 0; +} + +device_initcall(device_probe); + +#if defined(CONFIG_CPU_H8300H) +#define get_wait(base, addr) ({ \ + int baddr; \ + baddr = ((addr) / 0x200000 * 2); \ + w *= (ctrl_inw((unsigned long)(base) + 2) & (3 << baddr)) + 1; \ + }) +#endif +#if defined(CONFIG_CPU_H8S) +#define get_wait(base, addr) ({ \ + int baddr; \ + baddr = ((addr) / 0x200000 * 16); \ + w *= (ctrl_inl((unsigned long)(base) + 2) & (7 << baddr)) + 1; \ + }) +#endif + +static __init int access_timing(void) +{ + struct device_node *bsc; + void __iomem *base; + unsigned long addr = (unsigned long)&__delay; + int bit = 1 << (addr / 0x200000); + int w; + + bsc = of_find_compatible_node(NULL, NULL, "renesas,h8300-bsc"); + base = of_iomap(bsc, 0); + w = (ctrl_inb((unsigned long)base + 0) & bit)?2:1; + if (ctrl_inb((unsigned long)base + 1) & bit) + w *= get_wait(base, addr); + else + w *= 2; + return w * 3 / 2; +} + +void __init calibrate_delay(void) +{ + struct device_node *cpu; + int freq; + + cpu = of_find_compatible_node(NULL, NULL, "renesas,h8300"); + of_property_read_s32(cpu, "clock-frequency", &freq); + loops_per_jiffy = freq / HZ / (access_timing() * 2); + pr_cont("%lu.%02lu BogoMIPS (lpj=%lu)\n", + loops_per_jiffy / (500000 / HZ), + (loops_per_jiffy / (5000 / HZ)) % 100, loops_per_jiffy); +} + + +void __init time_init(void) +{ + of_clk_init(NULL); +} -- cgit v1.2.3 From 7b5bb891a6e44bd18bd8661ede2e09ccae258ef5 Mon Sep 17 00:00:00 2001 From: Yoshinori Sato Date: Fri, 8 May 2015 23:31:57 +0900 Subject: h8300: clock driver Signed-off-by: Yoshinori Sato --- .../bindings/clock/renesas,h8300-div-clock.txt | 24 ++++ .../bindings/clock/renesas,h8s2678-pll-clock.txt | 23 ++++ drivers/clk/Makefile | 1 + drivers/clk/h8300/Makefile | 2 + drivers/clk/h8300/clk-div.c | 53 ++++++++ drivers/clk/h8300/clk-h8s2678.c | 147 +++++++++++++++++++++ 6 files changed, 250 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/renesas,h8300-div-clock.txt create mode 100644 Documentation/devicetree/bindings/clock/renesas,h8s2678-pll-clock.txt create mode 100644 drivers/clk/h8300/Makefile create mode 100644 drivers/clk/h8300/clk-div.c create mode 100644 drivers/clk/h8300/clk-h8s2678.c (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/clock/renesas,h8300-div-clock.txt b/Documentation/devicetree/bindings/clock/renesas,h8300-div-clock.txt new file mode 100644 index 000000000000..36c2b528245c --- /dev/null +++ b/Documentation/devicetree/bindings/clock/renesas,h8300-div-clock.txt @@ -0,0 +1,24 @@ +* Renesas H8/300 divider clock + +Required Properties: + + - compatible: Must be "renesas,sh73a0-h8300-div-clock" + + - clocks: Reference to the parent clocks ("extal1" and "extal2") + + - #clock-cells: Must be 1 + + - reg: Base address and length of the divide rate selector + + - renesas,width: bit width of selector + +Example +------- + + cclk: cclk { + compatible = "renesas,h8300-div-clock"; + clocks = <&xclk>; + #clock-cells = <0>; + reg = <0xfee01b 2>; + renesas,width = <2>; + }; diff --git a/Documentation/devicetree/bindings/clock/renesas,h8s2678-pll-clock.txt b/Documentation/devicetree/bindings/clock/renesas,h8s2678-pll-clock.txt new file mode 100644 index 000000000000..500cdadbceb7 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/renesas,h8s2678-pll-clock.txt @@ -0,0 +1,23 @@ +Renesas H8S2678 PLL clock + +This device is Clock multiplyer + +Required Properties: + + - compatible: Must be "renesas,h8s2678-pll-clock" + + - clocks: Reference to the parent clocks + + - #clock-cells: Must be 0 + + - reg: Two rate selector (Multiply / Divide) register address + +Example +------- + + pllclk: pllclk { + compatible = "renesas,h8s2678-pll-clock"; + clocks = <&xclk>; + #clock-cells = <0>; + reg = <0xfee03b 2>, <0xfee045 2>; + }; diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 3d00c25382c5..9df871d53c6e 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -73,3 +73,4 @@ obj-$(CONFIG_ARCH_U8500) += ux500/ obj-$(CONFIG_COMMON_CLK_VERSATILE) += versatile/ obj-$(CONFIG_X86) += x86/ obj-$(CONFIG_ARCH_ZYNQ) += zynq/ +obj-$(CONFIG_H8300) += h8300/ diff --git a/drivers/clk/h8300/Makefile b/drivers/clk/h8300/Makefile new file mode 100644 index 000000000000..b86427c31fca --- /dev/null +++ b/drivers/clk/h8300/Makefile @@ -0,0 +1,2 @@ +obj-y += clk-div.o +obj-$(CONFIG_H8S2678) += clk-h8s2678.o diff --git a/drivers/clk/h8300/clk-div.c b/drivers/clk/h8300/clk-div.c new file mode 100644 index 000000000000..56f9eba91b83 --- /dev/null +++ b/drivers/clk/h8300/clk-div.c @@ -0,0 +1,53 @@ +/* + * H8/300 divide clock driver + * + * Copyright 2015 Yoshinori Sato + */ + +#include +#include +#include +#include +#include +#include + +static DEFINE_SPINLOCK(clklock); + +static void __init h8300_div_clk_setup(struct device_node *node) +{ + unsigned int num_parents; + struct clk *clk; + const char *clk_name = node->name; + const char *parent_name; + void __iomem *divcr = NULL; + int width; + + num_parents = of_clk_get_parent_count(node); + if (num_parents < 1) { + pr_err("%s: no parent found", clk_name); + return; + } + + divcr = of_iomap(node, 0); + if (divcr == NULL) { + pr_err("%s: failed to map divide register", clk_name); + goto error; + } + + parent_name = of_clk_get_parent_name(node, 0); + of_property_read_u32(node, "renesas,width", &width); + clk = clk_register_divider(NULL, clk_name, parent_name, + CLK_SET_RATE_GATE, divcr, 0, width, + CLK_DIVIDER_POWER_OF_TWO, &clklock); + if (!IS_ERR(clk)) { + of_clk_add_provider(node, of_clk_src_simple_get, clk); + return; + } + pr_err("%s: failed to register %s div clock (%ld)\n", + __func__, clk_name, PTR_ERR(clk)); +error: + if (divcr) + iounmap(divcr); +} + +CLK_OF_DECLARE(h8300_div_clk, "renesas,h8300-div-clock", h8300_div_clk_setup); diff --git a/drivers/clk/h8300/clk-h8s2678.c b/drivers/clk/h8300/clk-h8s2678.c new file mode 100644 index 000000000000..4de7ee534bfc --- /dev/null +++ b/drivers/clk/h8300/clk-h8s2678.c @@ -0,0 +1,147 @@ +/* + * H8S2678 clock driver + * + * Copyright 2015 Yoshinori Sato + */ + +#include +#include +#include +#include +#include +#include + +static DEFINE_SPINLOCK(clklock); + +#define MAX_FREQ 33333333 +#define MIN_FREQ 8000000 + +struct pll_clock { + struct clk_hw hw; + void __iomem *sckcr; + void __iomem *pllcr; +}; + +#define to_pll_clock(_hw) container_of(_hw, struct pll_clock, hw) + +static unsigned long pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct pll_clock *pll_clock = to_pll_clock(hw); + int mul = 1 << (ctrl_inb((unsigned long)pll_clock->pllcr) & 3); + + return parent_rate * mul; +} + +static long pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + int i, m = -1; + long offset[3]; + + if (rate > MAX_FREQ) + rate = MAX_FREQ; + if (rate < MIN_FREQ) + rate = MIN_FREQ; + + for (i = 0; i < 3; i++) + offset[i] = abs(rate - (*prate * (1 << i))); + for (i = 0; i < 3; i++) + if (m < 0) + m = i; + else + m = (offset[i] < offset[m])?i:m; + + return *prate * (1 << m); +} + +static int pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + int pll; + unsigned char val; + unsigned long flags; + struct pll_clock *pll_clock = to_pll_clock(hw); + + pll = ((rate / parent_rate) / 2) & 0x03; + spin_lock_irqsave(&clklock, flags); + val = ctrl_inb((unsigned long)pll_clock->sckcr); + val |= 0x08; + ctrl_outb(val, (unsigned long)pll_clock->sckcr); + val = ctrl_inb((unsigned long)pll_clock->pllcr); + val &= ~0x03; + val |= pll; + ctrl_outb(val, (unsigned long)pll_clock->pllcr); + spin_unlock_irqrestore(&clklock, flags); + return 0; +} + +static const struct clk_ops pll_ops = { + .recalc_rate = pll_recalc_rate, + .round_rate = pll_round_rate, + .set_rate = pll_set_rate, +}; + +static void __init h8s2678_pll_clk_setup(struct device_node *node) +{ + unsigned int num_parents; + struct clk *clk; + const char *clk_name = node->name; + const char *parent_name; + struct pll_clock *pll_clock; + struct clk_init_data init; + + num_parents = of_clk_get_parent_count(node); + if (num_parents < 1) { + pr_err("%s: no parent found", clk_name); + return; + } + + + pll_clock = kzalloc(sizeof(struct pll_clock), GFP_KERNEL); + if (!pll_clock) { + pr_err("%s: failed to alloc memory", clk_name); + return; + } + + pll_clock->sckcr = of_iomap(node, 0); + if (pll_clock->sckcr == NULL) { + pr_err("%s: failed to map divide register", clk_name); + goto error; + } + + pll_clock->pllcr = of_iomap(node, 1); + if (pll_clock->pllcr == NULL) { + pr_err("%s: failed to map multiply register", clk_name); + goto error; + } + + parent_name = of_clk_get_parent_name(node, 0); + init.name = clk_name; + init.ops = &pll_ops; + init.flags = CLK_IS_BASIC; + init.parent_names = &parent_name; + init.num_parents = 1; + pll_clock->hw.init = &init; + + clk = clk_register(NULL, &pll_clock->hw); + if (IS_ERR(clk)) + kfree(pll_clock); + if (!IS_ERR(clk)) { + of_clk_add_provider(node, of_clk_src_simple_get, clk); + return; + } + pr_err("%s: failed to register %s div clock (%ld)\n", + __func__, clk_name, PTR_ERR(clk)); +error: + if (pll_clock) { + if (pll_clock->sckcr) + iounmap(pll_clock->sckcr); + if (pll_clock->pllcr) + iounmap(pll_clock->pllcr); + kfree(pll_clock); + } +} + +CLK_OF_DECLARE(h8s2678_div_clk, "renesas,h8s2678-pll-clock", + h8s2678_pll_clk_setup); -- cgit v1.2.3 From 618b902d8c098f2fff188119da7b3184c4bc5483 Mon Sep 17 00:00:00 2001 From: Yoshinori Sato Date: Wed, 28 Jan 2015 02:52:42 +0900 Subject: h8300: clocksource h8300_timer8: 8bit clockevent device h8300_timer16 / h8300_tpu: 16bit clocksource Signed-off-by: Yoshinori Sato --- .../bindings/timer/renesas,16bit-timer.txt | 25 ++ .../bindings/timer/renesas,8bit-timer.txt | 25 ++ .../devicetree/bindings/timer/renesas,tpu.txt | 21 ++ drivers/clocksource/Kconfig | 7 + drivers/clocksource/Makefile | 3 + drivers/clocksource/h8300_timer16.c | 254 +++++++++++++++++ drivers/clocksource/h8300_timer8.c | 313 +++++++++++++++++++++ drivers/clocksource/h8300_tpu.c | 207 ++++++++++++++ 8 files changed, 855 insertions(+) create mode 100644 Documentation/devicetree/bindings/timer/renesas,16bit-timer.txt create mode 100644 Documentation/devicetree/bindings/timer/renesas,8bit-timer.txt create mode 100644 Documentation/devicetree/bindings/timer/renesas,tpu.txt create mode 100644 drivers/clocksource/h8300_timer16.c create mode 100644 drivers/clocksource/h8300_timer8.c create mode 100644 drivers/clocksource/h8300_tpu.c (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/timer/renesas,16bit-timer.txt b/Documentation/devicetree/bindings/timer/renesas,16bit-timer.txt new file mode 100644 index 000000000000..e8792447a199 --- /dev/null +++ b/Documentation/devicetree/bindings/timer/renesas,16bit-timer.txt @@ -0,0 +1,25 @@ +* Renesas H8/300 16bit timer + +The 16bit timer is a 16bit timer/counter with configurable clock inputs and +programmable compare match. + +Required Properties: + + - compatible: must contain "renesas,16bit-timer" + - reg: base address and length of the registers block for the timer module. + - interrupts: interrupt-specifier for the timer, IMIA + - clocks: a list of phandle, one for each entry in clock-names. + - clock-names: must contain "peripheral_clk" for the functional clock. + - renesas,channel: timer channel number. + +Example: + + timer16: timer@ffff68 { + compatible = "reneas,16bit-timer"; + reg = <0xffff68 8>, <0xffff60 8>; + interrupts = <24>; + renesas,channel = <0>; + clocks = <&pclk>; + clock-names = "peripheral_clk"; + }; + diff --git a/Documentation/devicetree/bindings/timer/renesas,8bit-timer.txt b/Documentation/devicetree/bindings/timer/renesas,8bit-timer.txt new file mode 100644 index 000000000000..9dca3759a0f0 --- /dev/null +++ b/Documentation/devicetree/bindings/timer/renesas,8bit-timer.txt @@ -0,0 +1,25 @@ +* Renesas H8/300 8bit timer + +The 8bit timer is a 8bit timer/counter with configurable clock inputs and +programmable compare match. + +This implement only supported cascade mode. + +Required Properties: + + - compatible: must contain "renesas,8bit-timer" + - reg: base address and length of the registers block for the timer module. + - interrupts: interrupt-specifier for the timer, CMIA and TOVI + - clocks: a list of phandle, one for each entry in clock-names. + - clock-names: must contain "fck" for the functional clock. + +Example: + + timer8_0: timer@ffff80 { + compatible = "renesas,8bit-timer"; + reg = <0xffff80 10>; + interrupts = <36>; + clocks = <&fclk>; + clock-names = "fck"; + }; + diff --git a/Documentation/devicetree/bindings/timer/renesas,tpu.txt b/Documentation/devicetree/bindings/timer/renesas,tpu.txt new file mode 100644 index 000000000000..f8b25897fb31 --- /dev/null +++ b/Documentation/devicetree/bindings/timer/renesas,tpu.txt @@ -0,0 +1,21 @@ +* Renesas H8/300 Timer Pluse Unit + +The TPU is a 16bit timer/counter with configurable clock inputs and +programmable compare match. +This implementation support only cascade mode. + +Required Properties: + + - compatible: must contain "renesas,tpu" + - reg: base address and length of the registers block in 2 channel. + - clocks: a list of phandle, one for each entry in clock-names. + - clock-names: must contain "peripheral_clk" for the functional clock. + + +Example: + tpu: tpu@ffffe0 { + compatible = "renesas,tpu"; + reg = <0xffffe0 16>, <0xfffff0 12>; + clocks = <&pclk>; + clock-names = "peripheral_clk"; + }; diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 51d7865fdddb..acce7cfb3cee 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -258,4 +258,11 @@ config CLKSRC_PXA help This enables OST0 support available on PXA and SA-11x0 platforms. + +config H8300_TMR16 + bool + +config H8300_TPU + bool + endmenu diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 5b85f6adb258..8288c4eea802 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -52,3 +52,6 @@ obj-$(CONFIG_ARCH_INTEGRATOR_AP) += timer-integrator-ap.o obj-$(CONFIG_CLKSRC_VERSATILE) += versatile.o obj-$(CONFIG_CLKSRC_MIPS_GIC) += mips-gic-timer.o obj-$(CONFIG_ASM9260_TIMER) += asm9260_timer.o +obj-$(CONFIG_H8300) += h8300_timer8.o +obj-$(CONFIG_H8300_TMR16) += h8300_timer16.o +obj-$(CONFIG_H8300_TPU) += h8300_tpu.o diff --git a/drivers/clocksource/h8300_timer16.c b/drivers/clocksource/h8300_timer16.c new file mode 100644 index 000000000000..82941c1e9e33 --- /dev/null +++ b/drivers/clocksource/h8300_timer16.c @@ -0,0 +1,254 @@ +/* + * H8/300 16bit Timer driver + * + * Copyright 2015 Yoshinori Sato + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define TSTR 0 +#define TSNC 1 +#define TMDR 2 +#define TOLR 3 +#define TISRA 4 +#define TISRB 5 +#define TISRC 6 + +#define TCR 0 +#define TIOR 1 +#define TCNT 2 +#define GRA 4 +#define GRB 6 + +#define FLAG_REPROGRAM (1 << 0) +#define FLAG_SKIPEVENT (1 << 1) +#define FLAG_IRQCONTEXT (1 << 2) +#define FLAG_STARTED (1 << 3) + +#define ONESHOT 0 +#define PERIODIC 1 + +#define RELATIVE 0 +#define ABSOLUTE 1 + +struct timer16_priv { + struct platform_device *pdev; + struct clocksource cs; + struct irqaction irqaction; + unsigned long total_cycles; + unsigned long mapbase; + unsigned long mapcommon; + unsigned long flags; + unsigned short gra; + unsigned short cs_enabled; + unsigned char enb; + unsigned char imfa; + unsigned char imiea; + unsigned char ovf; + raw_spinlock_t lock; + struct clk *clk; +}; + +static unsigned long timer16_get_counter(struct timer16_priv *p) +{ + unsigned long v1, v2, v3; + int o1, o2; + + o1 = ctrl_inb(p->mapcommon + TISRC) & p->ovf; + + /* Make sure the timer value is stable. Stolen from acpi_pm.c */ + do { + o2 = o1; + v1 = ctrl_inw(p->mapbase + TCNT); + v2 = ctrl_inw(p->mapbase + TCNT); + v3 = ctrl_inw(p->mapbase + TCNT); + o1 = ctrl_inb(p->mapcommon + TISRC) & p->ovf; + } while (unlikely((o1 != o2) || (v1 > v2 && v1 < v3) + || (v2 > v3 && v2 < v1) || (v3 > v1 && v3 < v2))); + + v2 |= 0x10000; + return v2; +} + + +static irqreturn_t timer16_interrupt(int irq, void *dev_id) +{ + struct timer16_priv *p = (struct timer16_priv *)dev_id; + + ctrl_outb(ctrl_inb(p->mapcommon + TISRA) & ~p->imfa, + p->mapcommon + TISRA); + p->total_cycles += 0x10000; + + return IRQ_HANDLED; +} + +static inline struct timer16_priv *cs_to_priv(struct clocksource *cs) +{ + return container_of(cs, struct timer16_priv, cs); +} + +static cycle_t timer16_clocksource_read(struct clocksource *cs) +{ + struct timer16_priv *p = cs_to_priv(cs); + unsigned long flags, raw; + unsigned long value; + + raw_spin_lock_irqsave(&p->lock, flags); + value = p->total_cycles; + raw = timer16_get_counter(p); + raw_spin_unlock_irqrestore(&p->lock, flags); + + return value + raw; +} + +static int timer16_enable(struct clocksource *cs) +{ + struct timer16_priv *p = cs_to_priv(cs); + + WARN_ON(p->cs_enabled); + + p->total_cycles = 0; + ctrl_outw(0x0000, p->mapbase + TCNT); + ctrl_outb(0x83, p->mapbase + TCR); + ctrl_outb(ctrl_inb(p->mapcommon + TSTR) | p->enb, + p->mapcommon + TSTR); + + p->cs_enabled = true; + return 0; +} + +static void timer16_disable(struct clocksource *cs) +{ + struct timer16_priv *p = cs_to_priv(cs); + + WARN_ON(!p->cs_enabled); + + ctrl_outb(ctrl_inb(p->mapcommon + TSTR) & ~p->enb, + p->mapcommon + TSTR); + + p->cs_enabled = false; +} + +#define REG_CH 0 +#define REG_COMM 1 + +static int timer16_setup(struct timer16_priv *p, struct platform_device *pdev) +{ + struct resource *res[2]; + int ret, irq; + unsigned int ch; + + memset(p, 0, sizeof(*p)); + p->pdev = pdev; + + res[REG_CH] = platform_get_resource(p->pdev, + IORESOURCE_MEM, REG_CH); + res[REG_COMM] = platform_get_resource(p->pdev, + IORESOURCE_MEM, REG_COMM); + if (!res[REG_CH] || !res[REG_COMM]) { + dev_err(&p->pdev->dev, "failed to get I/O memory\n"); + return -ENXIO; + } + irq = platform_get_irq(p->pdev, 0); + if (irq < 0) { + dev_err(&p->pdev->dev, "failed to get irq\n"); + return irq; + } + + p->clk = clk_get(&p->pdev->dev, "fck"); + if (IS_ERR(p->clk)) { + dev_err(&p->pdev->dev, "can't get clk\n"); + return PTR_ERR(p->clk); + } + of_property_read_u32(p->pdev->dev.of_node, "renesas,channel", &ch); + + p->pdev = pdev; + p->mapbase = res[REG_CH]->start; + p->mapcommon = res[REG_COMM]->start; + p->enb = 1 << ch; + p->imfa = 1 << ch; + p->imiea = 1 << (4 + ch); + p->cs.name = pdev->name; + p->cs.rating = 200; + p->cs.read = timer16_clocksource_read; + p->cs.enable = timer16_enable; + p->cs.disable = timer16_disable; + p->cs.mask = CLOCKSOURCE_MASK(sizeof(unsigned long) * 8); + p->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS; + + ret = request_irq(irq, timer16_interrupt, + IRQF_TIMER, pdev->name, p); + if (ret < 0) { + dev_err(&p->pdev->dev, "failed to request irq %d\n", irq); + return ret; + } + + clocksource_register_hz(&p->cs, clk_get_rate(p->clk) / 8); + + return 0; +} + +static int timer16_probe(struct platform_device *pdev) +{ + struct timer16_priv *p = platform_get_drvdata(pdev); + + if (p) { + dev_info(&pdev->dev, "kept as earlytimer\n"); + return 0; + } + + p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL); + if (!p) + return -ENOMEM; + + return timer16_setup(p, pdev); +} + +static int timer16_remove(struct platform_device *pdev) +{ + return -EBUSY; +} + +static const struct of_device_id timer16_of_table[] = { + { .compatible = "renesas,16bit-timer" }, + { } +}; +static struct platform_driver timer16_driver = { + .probe = timer16_probe, + .remove = timer16_remove, + .driver = { + .name = "h8300h-16timer", + .of_match_table = of_match_ptr(timer16_of_table), + } +}; + +static int __init timer16_init(void) +{ + return platform_driver_register(&timer16_driver); +} + +static void __exit timer16_exit(void) +{ + platform_driver_unregister(&timer16_driver); +} + +subsys_initcall(timer16_init); +module_exit(timer16_exit); +MODULE_AUTHOR("Yoshinori Sato"); +MODULE_DESCRIPTION("H8/300H 16bit Timer Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clocksource/h8300_timer8.c b/drivers/clocksource/h8300_timer8.c new file mode 100644 index 000000000000..1c6f8b935a7b --- /dev/null +++ b/drivers/clocksource/h8300_timer8.c @@ -0,0 +1,313 @@ +/* + * linux/arch/h8300/kernel/cpu/timer/timer8.c + * + * Yoshinori Sato + * + * 8bit Timer driver + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define _8TCR 0 +#define _8TCSR 2 +#define TCORA 4 +#define TCORB 6 +#define _8TCNT 8 + +#define FLAG_REPROGRAM (1 << 0) +#define FLAG_SKIPEVENT (1 << 1) +#define FLAG_IRQCONTEXT (1 << 2) +#define FLAG_STARTED (1 << 3) + +#define ONESHOT 0 +#define PERIODIC 1 + +#define RELATIVE 0 +#define ABSOLUTE 1 + +struct timer8_priv { + struct platform_device *pdev; + struct clock_event_device ced; + struct irqaction irqaction; + unsigned long mapbase; + raw_spinlock_t lock; + unsigned long flags; + unsigned int rate; + unsigned int tcora; + struct clk *pclk; +}; + +static unsigned long timer8_get_counter(struct timer8_priv *p) +{ + unsigned long v1, v2, v3; + int o1, o2; + + o1 = ctrl_inb(p->mapbase + _8TCSR) & 0x20; + + /* Make sure the timer value is stable. Stolen from acpi_pm.c */ + do { + o2 = o1; + v1 = ctrl_inw(p->mapbase + _8TCNT); + v2 = ctrl_inw(p->mapbase + _8TCNT); + v3 = ctrl_inw(p->mapbase + _8TCNT); + o1 = ctrl_inb(p->mapbase + _8TCSR) & 0x20; + } while (unlikely((o1 != o2) || (v1 > v2 && v1 < v3) + || (v2 > v3 && v2 < v1) || (v3 > v1 && v3 < v2))); + + v2 |= o1 << 10; + return v2; +} + +static irqreturn_t timer8_interrupt(int irq, void *dev_id) +{ + struct timer8_priv *p = dev_id; + + ctrl_outb(ctrl_inb(p->mapbase + _8TCSR) & ~0x40, + p->mapbase + _8TCSR); + p->flags |= FLAG_IRQCONTEXT; + ctrl_outw(p->tcora, p->mapbase + TCORA); + if (!(p->flags & FLAG_SKIPEVENT)) { + if (p->ced.mode == CLOCK_EVT_MODE_ONESHOT) + ctrl_outw(0x0000, p->mapbase + _8TCR); + p->ced.event_handler(&p->ced); + } + p->flags &= ~(FLAG_SKIPEVENT | FLAG_IRQCONTEXT); + + return IRQ_HANDLED; +} + +static void timer8_set_next(struct timer8_priv *p, unsigned long delta) +{ + unsigned long flags; + unsigned long now; + + raw_spin_lock_irqsave(&p->lock, flags); + if (delta >= 0x10000) + dev_warn(&p->pdev->dev, "delta out of range\n"); + now = timer8_get_counter(p); + p->tcora = delta; + ctrl_outb(ctrl_inb(p->mapbase + _8TCR) | 0x40, p->mapbase + _8TCR); + if (delta > now) + ctrl_outw(delta, p->mapbase + TCORA); + else + ctrl_outw(now + 1, p->mapbase + TCORA); + + raw_spin_unlock_irqrestore(&p->lock, flags); +} + +static int timer8_enable(struct timer8_priv *p) +{ + p->rate = clk_get_rate(p->pclk) / 64; + ctrl_outw(0xffff, p->mapbase + TCORA); + ctrl_outw(0x0000, p->mapbase + _8TCNT); + ctrl_outw(0x0c02, p->mapbase + _8TCR); + + return 0; +} + +static int timer8_start(struct timer8_priv *p) +{ + int ret = 0; + unsigned long flags; + + raw_spin_lock_irqsave(&p->lock, flags); + + if (!(p->flags & FLAG_STARTED)) + ret = timer8_enable(p); + + if (ret) + goto out; + p->flags |= FLAG_STARTED; + + out: + raw_spin_unlock_irqrestore(&p->lock, flags); + + return ret; +} + +static void timer8_stop(struct timer8_priv *p) +{ + unsigned long flags; + + raw_spin_lock_irqsave(&p->lock, flags); + + ctrl_outw(0x0000, p->mapbase + _8TCR); + + raw_spin_unlock_irqrestore(&p->lock, flags); +} + +static inline struct timer8_priv *ced_to_priv(struct clock_event_device *ced) +{ + return container_of(ced, struct timer8_priv, ced); +} + +static void timer8_clock_event_start(struct timer8_priv *p, int periodic) +{ + struct clock_event_device *ced = &p->ced; + + timer8_start(p); + + ced->shift = 32; + ced->mult = div_sc(p->rate, NSEC_PER_SEC, ced->shift); + ced->max_delta_ns = clockevent_delta2ns(0xffff, ced); + ced->min_delta_ns = clockevent_delta2ns(0x0001, ced); + + timer8_set_next(p, periodic?(p->rate + HZ/2) / HZ:0x10000); +} + +static void timer8_clock_event_mode(enum clock_event_mode mode, + struct clock_event_device *ced) +{ + struct timer8_priv *p = ced_to_priv(ced); + + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + dev_info(&p->pdev->dev, "used for periodic clock events\n"); + timer8_stop(p); + timer8_clock_event_start(p, PERIODIC); + break; + case CLOCK_EVT_MODE_ONESHOT: + dev_info(&p->pdev->dev, "used for oneshot clock events\n"); + timer8_stop(p); + timer8_clock_event_start(p, ONESHOT); + break; + case CLOCK_EVT_MODE_SHUTDOWN: + case CLOCK_EVT_MODE_UNUSED: + timer8_stop(p); + break; + default: + break; + } +} + +static int timer8_clock_event_next(unsigned long delta, + struct clock_event_device *ced) +{ + struct timer8_priv *p = ced_to_priv(ced); + + BUG_ON(ced->mode != CLOCK_EVT_MODE_ONESHOT); + timer8_set_next(p, delta - 1); + + return 0; +} + +static int timer8_setup(struct timer8_priv *p, + struct platform_device *pdev) +{ + struct resource *res; + int irq; + int ret; + + memset(p, 0, sizeof(*p)); + p->pdev = pdev; + + res = platform_get_resource(p->pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&p->pdev->dev, "failed to get I/O memory\n"); + return -ENXIO; + } + + irq = platform_get_irq(p->pdev, 0); + if (irq < 0) { + dev_err(&p->pdev->dev, "failed to get irq\n"); + return -ENXIO; + } + + p->mapbase = res->start; + + p->irqaction.name = dev_name(&p->pdev->dev); + p->irqaction.handler = timer8_interrupt; + p->irqaction.dev_id = p; + p->irqaction.flags = IRQF_TIMER; + + p->pclk = clk_get(&p->pdev->dev, "fck"); + if (IS_ERR(p->pclk)) { + dev_err(&p->pdev->dev, "can't get clk\n"); + return PTR_ERR(p->pclk); + } + + p->ced.name = pdev->name; + p->ced.features = CLOCK_EVT_FEAT_PERIODIC | + CLOCK_EVT_FEAT_ONESHOT; + p->ced.rating = 200; + p->ced.cpumask = cpumask_of(0); + p->ced.set_next_event = timer8_clock_event_next; + p->ced.set_mode = timer8_clock_event_mode; + + ret = setup_irq(irq, &p->irqaction); + if (ret < 0) { + dev_err(&p->pdev->dev, + "failed to request irq %d\n", irq); + return ret; + } + clockevents_register_device(&p->ced); + platform_set_drvdata(pdev, p); + + return 0; +} + +static int timer8_probe(struct platform_device *pdev) +{ + struct timer8_priv *p = platform_get_drvdata(pdev); + + if (p) { + dev_info(&pdev->dev, "kept as earlytimer\n"); + return 0; + } + + p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL); + if (!p) + return -ENOMEM; + + return timer8_setup(p, pdev); +} + +static int timer8_remove(struct platform_device *pdev) +{ + return -EBUSY; +} + +static const struct of_device_id timer8_of_table[] __maybe_unused = { + { .compatible = "renesas,8bit-timer" }, + { } +}; + +MODULE_DEVICE_TABLE(of, sh_cmt_of_table); +static struct platform_driver timer8_driver = { + .probe = timer8_probe, + .remove = timer8_remove, + .driver = { + .name = "h8300-8timer", + .of_match_table = of_match_ptr(timer8_of_table), + } +}; + +static int __init timer8_init(void) +{ + return platform_driver_register(&timer8_driver); +} + +static void __exit timer8_exit(void) +{ + platform_driver_unregister(&timer8_driver); +} + +subsys_initcall(timer8_init); +module_exit(timer8_exit); +MODULE_AUTHOR("Yoshinori Sato"); +MODULE_DESCRIPTION("H8/300 8bit Timer Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clocksource/h8300_tpu.c b/drivers/clocksource/h8300_tpu.c new file mode 100644 index 000000000000..64195fdd78bf --- /dev/null +++ b/drivers/clocksource/h8300_tpu.c @@ -0,0 +1,207 @@ +/* + * H8/300 TPU Driver + * + * Copyright 2015 Yoshinori Sato + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define TCR 0 +#define TMDR 1 +#define TIOR 2 +#define TER 4 +#define TSR 5 +#define TCNT 6 +#define TGRA 8 +#define TGRB 10 +#define TGRC 12 +#define TGRD 14 + +struct tpu_priv { + struct platform_device *pdev; + struct clocksource cs; + struct clk *clk; + unsigned long mapbase1; + unsigned long mapbase2; + raw_spinlock_t lock; + unsigned int cs_enabled; +}; + +static inline unsigned long read_tcnt32(struct tpu_priv *p) +{ + unsigned long tcnt; + + tcnt = ctrl_inw(p->mapbase1 + TCNT) << 16; + tcnt |= ctrl_inw(p->mapbase2 + TCNT); + return tcnt; +} + +static int tpu_get_counter(struct tpu_priv *p, unsigned long long *val) +{ + unsigned long v1, v2, v3; + int o1, o2; + + o1 = ctrl_inb(p->mapbase1 + TSR) & 0x10; + + /* Make sure the timer value is stable. Stolen from acpi_pm.c */ + do { + o2 = o1; + v1 = read_tcnt32(p); + v2 = read_tcnt32(p); + v3 = read_tcnt32(p); + o1 = ctrl_inb(p->mapbase1 + TSR) & 0x10; + } while (unlikely((o1 != o2) || (v1 > v2 && v1 < v3) + || (v2 > v3 && v2 < v1) || (v3 > v1 && v3 < v2))); + + *val = v2; + return o1; +} + +static inline struct tpu_priv *cs_to_priv(struct clocksource *cs) +{ + return container_of(cs, struct tpu_priv, cs); +} + +static cycle_t tpu_clocksource_read(struct clocksource *cs) +{ + struct tpu_priv *p = cs_to_priv(cs); + unsigned long flags; + unsigned long long value; + + raw_spin_lock_irqsave(&p->lock, flags); + if (tpu_get_counter(p, &value)) + value += 0x100000000; + raw_spin_unlock_irqrestore(&p->lock, flags); + + return value; +} + +static int tpu_clocksource_enable(struct clocksource *cs) +{ + struct tpu_priv *p = cs_to_priv(cs); + + WARN_ON(p->cs_enabled); + + ctrl_outw(0, p->mapbase1 + TCNT); + ctrl_outw(0, p->mapbase2 + TCNT); + ctrl_outb(0x0f, p->mapbase1 + TCR); + ctrl_outb(0x03, p->mapbase2 + TCR); + + p->cs_enabled = true; + return 0; +} + +static void tpu_clocksource_disable(struct clocksource *cs) +{ + struct tpu_priv *p = cs_to_priv(cs); + + WARN_ON(!p->cs_enabled); + + ctrl_outb(0, p->mapbase1 + TCR); + ctrl_outb(0, p->mapbase2 + TCR); + p->cs_enabled = false; +} + +#define CH_L 0 +#define CH_H 1 + +static int __init tpu_setup(struct tpu_priv *p, struct platform_device *pdev) +{ + struct resource *res[2]; + + memset(p, 0, sizeof(*p)); + p->pdev = pdev; + + res[CH_L] = platform_get_resource(p->pdev, IORESOURCE_MEM, CH_L); + res[CH_H] = platform_get_resource(p->pdev, IORESOURCE_MEM, CH_H); + if (!res[CH_L] || !res[CH_H]) { + dev_err(&p->pdev->dev, "failed to get I/O memory\n"); + return -ENXIO; + } + + p->clk = clk_get(&p->pdev->dev, "fck"); + if (IS_ERR(p->clk)) { + dev_err(&p->pdev->dev, "can't get clk\n"); + return PTR_ERR(p->clk); + } + + p->mapbase1 = res[CH_L]->start; + p->mapbase2 = res[CH_H]->start; + + p->cs.name = pdev->name; + p->cs.rating = 200; + p->cs.read = tpu_clocksource_read; + p->cs.enable = tpu_clocksource_enable; + p->cs.disable = tpu_clocksource_disable; + p->cs.mask = CLOCKSOURCE_MASK(sizeof(unsigned long) * 8); + p->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS; + clocksource_register_hz(&p->cs, clk_get_rate(p->clk) / 64); + platform_set_drvdata(pdev, p); + + return 0; +} + +static int tpu_probe(struct platform_device *pdev) +{ + struct tpu_priv *p = platform_get_drvdata(pdev); + + if (p) { + dev_info(&pdev->dev, "kept as earlytimer\n"); + return 0; + } + + p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL); + if (!p) + return -ENOMEM; + + return tpu_setup(p, pdev); +} + +static int tpu_remove(struct platform_device *pdev) +{ + return -EBUSY; +} + +static const struct of_device_id tpu_of_table[] = { + { .compatible = "renesas,tpu" }, + { } +}; + +static struct platform_driver tpu_driver = { + .probe = tpu_probe, + .remove = tpu_remove, + .driver = { + .name = "h8s-tpu", + .of_match_table = of_match_ptr(tpu_of_table), + } +}; + +static int __init tpu_init(void) +{ + return platform_driver_register(&tpu_driver); +} + +static void __exit tpu_exit(void) +{ + platform_driver_unregister(&tpu_driver); +} + +subsys_initcall(tpu_init); +module_exit(tpu_exit); +MODULE_AUTHOR("Yoshinori Sato"); +MODULE_DESCRIPTION("H8S Timer Pulse Unit Driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 8a7644821ae00b76e0c039f9128ee584dda146a8 Mon Sep 17 00:00:00 2001 From: Yoshinori Sato Date: Sun, 10 May 2015 02:30:47 +0900 Subject: h8300: IRQ chip driver Signed-off-by: Yoshinori Sato --- .../interrupt-controller/renesas,h8300h-intc.txt | 22 +++++ .../interrupt-controller/renesas,h8s-intc.txt | 22 +++++ drivers/irqchip/Kconfig | 8 ++ drivers/irqchip/Makefile | 2 + drivers/irqchip/irq-renesas-h8300h.c | 95 +++++++++++++++++++ drivers/irqchip/irq-renesas-h8s.c | 101 +++++++++++++++++++++ 6 files changed, 250 insertions(+) create mode 100644 Documentation/devicetree/bindings/interrupt-controller/renesas,h8300h-intc.txt create mode 100644 Documentation/devicetree/bindings/interrupt-controller/renesas,h8s-intc.txt create mode 100644 drivers/irqchip/irq-renesas-h8300h.c create mode 100644 drivers/irqchip/irq-renesas-h8s.c (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/interrupt-controller/renesas,h8300h-intc.txt b/Documentation/devicetree/bindings/interrupt-controller/renesas,h8300h-intc.txt new file mode 100644 index 000000000000..56e8d82aff34 --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/renesas,h8300h-intc.txt @@ -0,0 +1,22 @@ +* H8/300H Interrupt controller + +Required properties: + +- compatible: has to be "renesas,h8300h-intc", "renesas,h8300-intc" as fallback. +- #interrupt-cells: has to be <2>: an interrupt index and flags, as defined in + interrupts.txt in this directory +- regs: Base address of interrupt controller registers. + +Optional properties: + +- any properties, listed in interrupts.txt, and any standard resource allocation + properties + +Example: + + h8intc: interrupt-controller@fee012 { + compatible = "renesas,h8300h-intc", "renesas,h8300-intc"; + #interrupt-cells = <2>; + interrupt-controller; + reg = <0xfee012 7>; + }; diff --git a/Documentation/devicetree/bindings/interrupt-controller/renesas,h8s-intc.txt b/Documentation/devicetree/bindings/interrupt-controller/renesas,h8s-intc.txt new file mode 100644 index 000000000000..faded2b1559b --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/renesas,h8s-intc.txt @@ -0,0 +1,22 @@ +* H8S Interrupt controller + +Required properties: + +- compatible: has to be "renesas,h8s-intc", "renesas,h8300-intc" as fallback. +- #interrupt-cells: has to be <2>: an interrupt index and flags, as defined in + interrupts.txt in this directory +- regs: Base address of interrupt controller registers. + +Optional properties: + +- any properties, listed in interrupts.txt, and any standard resource allocation + properties + +Example: + + h8intc: interrupt-controller@fffe00 { + compatible = "renesas,h8s-intc", "renesas,h8300-intc"; + #interrupt-cells = <2>; + interrupt-controller; + reg = <0xfffe00 24>; + }; diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 6de62a96e79c..998681587d1e 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -158,3 +158,11 @@ config KEYSTONE_IRQ config MIPS_GIC bool select MIPS_CM + +config RENESAS_H8300H_INTC + bool + select IRQ_DOMAIN + +config RENESAS_H8S_INTC + bool + select IRQ_DOMAIN \ No newline at end of file diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index dda4927e47a6..f8efb7087760 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -47,3 +47,5 @@ obj-$(CONFIG_KEYSTONE_IRQ) += irq-keystone.o obj-$(CONFIG_MIPS_GIC) += irq-mips-gic.o obj-$(CONFIG_ARCH_MEDIATEK) += irq-mtk-sysirq.o obj-$(CONFIG_ARCH_DIGICOLOR) += irq-digicolor.o +obj-$(CONFIG_RENESAS_H8300H_INTC) += irq-renesas-h8300h.o +obj-$(CONFIG_RENESAS_H8S_INTC) += irq-renesas-h8s.o diff --git a/drivers/irqchip/irq-renesas-h8300h.c b/drivers/irqchip/irq-renesas-h8300h.c new file mode 100644 index 000000000000..1870e6bd3dd9 --- /dev/null +++ b/drivers/irqchip/irq-renesas-h8300h.c @@ -0,0 +1,95 @@ +/* + * H8/300H interrupt controller driver + * + * Copyright 2015 Yoshinori Sato + */ + +#include +#include +#include +#include +#include +#include + +#include "irqchip.h" + +static const char ipr_bit[] = { + 7, 6, 5, 5, + 4, 4, 4, 4, 3, 3, 3, 3, + 2, 2, 2, 2, 1, 1, 1, 1, + 0, 0, 0, 0, 15, 15, 15, 15, + 14, 14, 14, 14, 13, 13, 13, 13, + -1, -1, -1, -1, 11, 11, 11, 11, + 10, 10, 10, 10, 9, 9, 9, 9, +}; + +static void *intc_baseaddr; + +#define IPR ((unsigned long)intc_baseaddr + 6) + +static void h8300h_disable_irq(struct irq_data *data) +{ + int bit; + int irq = data->irq - 12; + + bit = ipr_bit[irq]; + if (bit >= 0) { + if (bit < 8) + ctrl_bclr(bit & 7, IPR); + else + ctrl_bclr(bit & 7, (IPR+1)); + } +} + +static void h8300h_enable_irq(struct irq_data *data) +{ + int bit; + int irq = data->irq - 12; + + bit = ipr_bit[irq]; + if (bit >= 0) { + if (bit < 8) + ctrl_bset(bit & 7, IPR); + else + ctrl_bset(bit & 7, (IPR+1)); + } +} + +struct irq_chip h8300h_irq_chip = { + .name = "H8/300H-INTC", + .irq_enable = h8300h_enable_irq, + .irq_disable = h8300h_disable_irq, +}; + +static int irq_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw_irq_num) +{ + irq_set_chip_and_handler(virq, &h8300h_irq_chip, handle_simple_irq); + + return 0; +} + +static struct irq_domain_ops irq_ops = { + .map = irq_map, + .xlate = irq_domain_xlate_onecell, +}; + +static int __init h8300h_intc_of_init(struct device_node *intc, + struct device_node *parent) +{ + struct irq_domain *domain; + + intc_baseaddr = of_iomap(intc, 0); + BUG_ON(!intc_baseaddr); + + /* All interrupt priority low */ + ctrl_outb(0x00, IPR + 0); + ctrl_outb(0x00, IPR + 1); + + domain = irq_domain_add_linear(intc, NR_IRQS, &irq_ops, NULL); + BUG_ON(!domain); + irq_set_default_host(domain); + return 0; +} + +IRQCHIP_DECLARE(h8300h_intc, "renesas,h8300h-intc", h8300h_intc_of_init); diff --git a/drivers/irqchip/irq-renesas-h8s.c b/drivers/irqchip/irq-renesas-h8s.c new file mode 100644 index 000000000000..64425f4de7d9 --- /dev/null +++ b/drivers/irqchip/irq-renesas-h8s.c @@ -0,0 +1,101 @@ +/* + * H8S interrupt contoller driver + * + * Copyright 2015 Yoshinori Sato + */ + +#include +#include +#include +#include +#include "irqchip.h" + +static void *intc_baseaddr; +#define IPRA ((unsigned long)intc_baseaddr) + +static const unsigned char ipr_table[] = { + 0x03, 0x02, 0x01, 0x00, 0x13, 0x12, 0x11, 0x10, /* 16 - 23 */ + 0x23, 0x22, 0x21, 0x20, 0x33, 0x32, 0x31, 0x30, /* 24 - 31 */ + 0x43, 0x42, 0x41, 0x40, 0x53, 0x53, 0x52, 0x52, /* 32 - 39 */ + 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, /* 40 - 47 */ + 0x50, 0x50, 0x50, 0x50, 0x63, 0x63, 0x63, 0x63, /* 48 - 55 */ + 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, /* 56 - 63 */ + 0x61, 0x61, 0x61, 0x61, 0x60, 0x60, 0x60, 0x60, /* 64 - 71 */ + 0x73, 0x73, 0x73, 0x73, 0x72, 0x72, 0x72, 0x72, /* 72 - 79 */ + 0x71, 0x71, 0x71, 0x71, 0x70, 0x83, 0x82, 0x81, /* 80 - 87 */ + 0x80, 0x80, 0x80, 0x80, 0x93, 0x93, 0x93, 0x93, /* 88 - 95 */ + 0x92, 0x92, 0x92, 0x92, 0x91, 0x91, 0x91, 0x91, /* 96 - 103 */ + 0x90, 0x90, 0x90, 0x90, 0xa3, 0xa3, 0xa3, 0xa3, /* 104 - 111 */ + 0xa2, 0xa2, 0xa2, 0xa2, 0xa1, 0xa1, 0xa1, 0xa1, /* 112 - 119 */ + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, /* 120 - 127 */ +}; + +static void h8s_disable_irq(struct irq_data *data) +{ + int pos; + unsigned int addr; + unsigned short pri; + int irq = data->irq; + + addr = IPRA + ((ipr_table[irq - 16] & 0xf0) >> 3); + pos = (ipr_table[irq - 16] & 0x0f) * 4; + pri = ~(0x000f << pos); + pri &= ctrl_inw(addr); + ctrl_outw(pri, addr); +} + +static void h8s_enable_irq(struct irq_data *data) +{ + int pos; + unsigned int addr; + unsigned short pri; + int irq = data->irq; + + addr = IPRA + ((ipr_table[irq - 16] & 0xf0) >> 3); + pos = (ipr_table[irq - 16] & 0x0f) * 4; + pri = ~(0x000f << pos); + pri &= ctrl_inw(addr); + pri |= 1 << pos; + ctrl_outw(pri, addr); +} + +struct irq_chip h8s_irq_chip = { + .name = "H8S-INTC", + .irq_enable = h8s_enable_irq, + .irq_disable = h8s_disable_irq, +}; + +static __init int irq_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw_irq_num) +{ + irq_set_chip_and_handler(virq, &h8s_irq_chip, handle_simple_irq); + + return 0; +} + +static struct irq_domain_ops irq_ops = { + .map = irq_map, + .xlate = irq_domain_xlate_onecell, +}; + +static int __init h8s_intc_of_init(struct device_node *intc, + struct device_node *parent) +{ + struct irq_domain *domain; + int n; + + intc_baseaddr = of_iomap(intc, 0); + BUG_ON(!intc_baseaddr); + + /* All interrupt priority is 0 (disable) */ + /* IPRA to IPRK */ + for (n = 0; n <= 'k' - 'a'; n++) + ctrl_outw(0x0000, IPRA + (n * 2)); + + domain = irq_domain_add_linear(intc, NR_IRQS, &irq_ops, NULL); + BUG_ON(!domain); + irq_set_default_host(domain); + return 0; +} + +IRQCHIP_DECLARE(h8s_intc, "renesas,h8s-intc", h8s_intc_of_init); -- cgit v1.2.3