summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2015-06-25 13:07:24 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2015-06-25 13:07:24 -0700
commit55a7d4b85ca1f723d26b8956e8faeff730d0d240 (patch)
tree4f8e9460bbd2096971215b67321c14a14c3c1d3f /drivers
parentaefbef10e3ae6e2c6e3c54f906f10b34c73a2c66 (diff)
parent07834743f42b4f27a21010cf5bab483b3ae3d13d (diff)
Merge tag 'for-4.2' of git://git.sourceforge.jp/gitroot/uclinux-h8/linux
Pull Renesas H8/300 architecture re-introduction from Yoshinori Sato. We dropped arch/h8300 two years ago as stale and old, this is a new and more modern rewritten arch support for the same architecture. * tag 'for-4.2' of git://git.sourceforge.jp/gitroot/uclinux-h8/linux: (27 commits) h8300: fix typo. h8300: Always build dtb h8300: Remove ARCH_WANT_IPC_PARSE_VERSION sh-sci: Get register size from platform device clk: h8300: fix error handling in h8s2678_pll_clk_setup() h8300: Symbol name fix h8300: devicetree source h8300: configs h8300: IRQ chip driver h8300: clocksource h8300: clock driver h8300: Build scripts h8300: library functions h8300: Memory management h8300: miscellaneous functions h8300: process helpers h8300: compressed image support h8300: Low level entry h8300: kernel startup h8300: Interrupt and exceptions ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/clk/Makefile1
-rw-r--r--drivers/clk/h8300/Makefile2
-rw-r--r--drivers/clk/h8300/clk-div.c53
-rw-r--r--drivers/clk/h8300/clk-h8s2678.c146
-rw-r--r--drivers/clocksource/Kconfig7
-rw-r--r--drivers/clocksource/Makefile3
-rw-r--r--drivers/clocksource/h8300_timer16.c254
-rw-r--r--drivers/clocksource/h8300_timer8.c313
-rw-r--r--drivers/clocksource/h8300_tpu.c207
-rw-r--r--drivers/irqchip/Kconfig8
-rw-r--r--drivers/irqchip/Makefile2
-rw-r--r--drivers/irqchip/irq-renesas-h8300h.c95
-rw-r--r--drivers/irqchip/irq-renesas-h8s.c101
-rw-r--r--drivers/tty/serial/Kconfig2
-rw-r--r--drivers/tty/serial/sh-sci.c40
15 files changed, 1212 insertions, 22 deletions
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 <ysato@users.sourceforge.jp>
+ */
+
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+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..4701b093e497
--- /dev/null
+++ b/drivers/clk/h8300/clk-h8s2678.c
@@ -0,0 +1,146 @@
+/*
+ * H8S2678 clock driver
+ *
+ * Copyright 2015 Yoshinori Sato <ysato@users.sourceforge.jp>
+ */
+
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/of_address.h>
+
+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 free_clock;
+ }
+
+ pll_clock->pllcr = of_iomap(node, 1);
+ if (pll_clock->pllcr == NULL) {
+ pr_err("%s: failed to map multiply register", clk_name);
+ goto unmap_sckcr;
+ }
+
+ 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)) {
+ pr_err("%s: failed to register %s div clock (%ld)\n",
+ __func__, clk_name, PTR_ERR(clk));
+ goto unmap_pllcr;
+ }
+
+ of_clk_add_provider(node, of_clk_src_simple_get, clk);
+ return;
+
+unmap_pllcr:
+ iounmap(pll_clock->pllcr);
+unmap_sckcr:
+ iounmap(pll_clock->sckcr);
+free_clock:
+ kfree(pll_clock);
+}
+
+CLK_OF_DECLARE(h8s2678_div_clk, "renesas,h8s2678-pll-clock",
+ h8s2678_pll_clk_setup);
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 32164ba3d36a..d1bd53f2f360 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -275,4 +275,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 1831a588b988..2b344232262c 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -55,3 +55,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 <ysato@users.sourcefoge.jp>
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/clocksource.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/of.h>
+
+#include <asm/segment.h>
+#include <asm/irq.h>
+
+#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..0214cb3a7f5e
--- /dev/null
+++ b/drivers/clocksource/h8300_timer8.c
@@ -0,0 +1,313 @@
+/*
+ * linux/arch/h8300/kernel/cpu/timer/timer8.c
+ *
+ * Yoshinori Sato <ysato@users.sourcefoge.jp>
+ *
+ * 8bit Timer driver
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/clockchips.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/of.h>
+
+#include <asm/irq.h>
+
+#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, timer8_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 <ysato@users.sourcefoge.jp>
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/clocksource.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/of.h>
+
+#include <asm/irq.h>
+
+#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");
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 99b9a9792975..8a7d7807b596 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -159,3 +159,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 <ysato@users.sourceforge.jp>
+ */
+
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <asm/io.h>
+
+#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 <ysato@users.sourceforge.jp>
+ */
+
+#include <linux/irq.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <asm/io.h>
+#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);
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index f8120c1bde14..dea1eff6a92c 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -728,7 +728,7 @@ config SERIAL_IP22_ZILOG_CONSOLE
config SERIAL_SH_SCI
tristate "SuperH SCI(F) serial port support"
- depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST
+ depends on SUPERH || ARCH_SHMOBILE || H8300 || COMPILE_TEST
select SERIAL_CORE
config SERIAL_SH_SCI_NR_UARTS
diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
index e7d6566fafaf..95772cf4e7b0 100644
--- a/drivers/tty/serial/sh-sci.c
+++ b/drivers/tty/serial/sh-sci.c
@@ -84,7 +84,7 @@ struct sci_port {
int overrun_bit;
unsigned int error_mask;
unsigned int sampling_rate;
-
+ resource_size_t reg_size;
/* Break timer */
struct timer_list break_timer;
@@ -2073,23 +2073,9 @@ static const char *sci_type(struct uart_port *port)
return NULL;
}
-static inline unsigned long sci_port_size(struct uart_port *port)
-{
- /*
- * Pick an arbitrary size that encapsulates all of the base
- * registers by default. This can be optimized later, or derived
- * from platform resource data at such a time that ports begin to
- * behave more erratically.
- */
- if (port->type == PORT_HSCIF)
- return 96;
- else
- return 64;
-}
-
static int sci_remap_port(struct uart_port *port)
{
- unsigned long size = sci_port_size(port);
+ struct sci_port *sport = to_sci_port(port);
/*
* Nothing to do if there's already an established membase.
@@ -2098,7 +2084,7 @@ static int sci_remap_port(struct uart_port *port)
return 0;
if (port->flags & UPF_IOREMAP) {
- port->membase = ioremap_nocache(port->mapbase, size);
+ port->membase = ioremap_nocache(port->mapbase, sport->reg_size);
if (unlikely(!port->membase)) {
dev_err(port->dev, "can't remap port#%d\n", port->line);
return -ENXIO;
@@ -2117,23 +2103,28 @@ static int sci_remap_port(struct uart_port *port)
static void sci_release_port(struct uart_port *port)
{
+ struct sci_port *sport = to_sci_port(port);
+
if (port->flags & UPF_IOREMAP) {
iounmap(port->membase);
port->membase = NULL;
}
- release_mem_region(port->mapbase, sci_port_size(port));
+ release_mem_region(port->mapbase, sport->reg_size);
}
static int sci_request_port(struct uart_port *port)
{
- unsigned long size = sci_port_size(port);
struct resource *res;
+ struct sci_port *sport = to_sci_port(port);
int ret;
- res = request_mem_region(port->mapbase, size, dev_name(port->dev));
- if (unlikely(res == NULL))
+ res = request_mem_region(port->mapbase, sport->reg_size,
+ dev_name(port->dev));
+ if (unlikely(res == NULL)) {
+ dev_err(port->dev, "request_mem_region failed.");
return -EBUSY;
+ }
ret = sci_remap_port(port);
if (unlikely(ret != 0)) {
@@ -2207,6 +2198,7 @@ static int sci_init_single(struct platform_device *dev,
return -ENOMEM;
port->mapbase = res->start;
+ sci_port->reg_size = resource_size(res);
for (i = 0; i < ARRAY_SIZE(sci_port->irqs); ++i)
sci_port->irqs[i] = platform_get_irq(dev, i);
@@ -2536,6 +2528,12 @@ static const struct of_device_id of_sci_match[] = {
.regtype = SCIx_HSCIF_REGTYPE,
},
}, {
+ .compatible = "renesas,sci",
+ .data = &(const struct sci_port_info) {
+ .type = PORT_SCI,
+ .regtype = SCIx_SCI_REGTYPE,
+ },
+ }, {
/* Terminator */
},
};