diff options
author | Linus Torvalds <torvalds@g5.osdl.org> | 2006-09-27 08:49:07 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-09-27 08:49:07 -0700 |
commit | b98adfccdf5f8dd34ae56a2d5adbe2c030bd4674 (patch) | |
tree | 1807a029520f550dd4f90c95ad0063bceb00d645 /drivers | |
parent | ba21fe71725f94792330ebc3034ef2b35a36276f (diff) | |
parent | 33573c0e3243aaa38b6ad96942de85a1b713c2ff (diff) |
Merge master.kernel.org:/pub/scm/linux/kernel/git/lethal/sh-2.6
* master.kernel.org:/pub/scm/linux/kernel/git/lethal/sh-2.6: (108 commits)
sh: Fix occasional flush_cache_4096() stack corruption.
sh: Calculate shm alignment at runtime.
sh: dma-mapping compile fixes.
sh: Initial vsyscall page support.
sh: Clean up PAGE_SIZE definition for assembly use.
sh: Selective flush_cache_mm() flushing.
sh: More intelligent entry_mask/way_size calculation.
sh: Support for L2 cache on newer SH-4A CPUs.
sh: Update kexec support for API changes.
sh: Optimized readsl()/writesl() support.
sh: Report movli.l/movco.l capabilities.
sh: CPU flags in AT_HWCAP in ELF auxvt.
sh: Add support for 4K stacks.
sh: Enable /proc/kcore support.
sh: stack debugging support.
sh: select CONFIG_EMBEDDED.
sh: machvec rework.
sh: Solution Engine SH7343 board support.
sh: SH7710VoIPGW board support.
sh: Enable verbose BUG() support.
...
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/char/Kconfig | 2 | ||||
-rw-r--r-- | drivers/char/watchdog/Kconfig | 8 | ||||
-rw-r--r-- | drivers/char/watchdog/shwdt.c | 110 | ||||
-rw-r--r-- | drivers/input/touchscreen/hp680_ts_input.c | 14 | ||||
-rw-r--r-- | drivers/net/stnic.c | 2 | ||||
-rw-r--r-- | drivers/rtc/Kconfig | 10 | ||||
-rw-r--r-- | drivers/rtc/Makefile | 1 | ||||
-rw-r--r-- | drivers/rtc/rtc-sh.c | 467 | ||||
-rw-r--r-- | drivers/serial/Kconfig | 9 | ||||
-rw-r--r-- | drivers/serial/sh-sci.c | 1146 | ||||
-rw-r--r-- | drivers/serial/sh-sci.h | 90 | ||||
-rw-r--r-- | drivers/video/backlight/hp680_bl.c | 4 | ||||
-rw-r--r-- | drivers/video/console/Kconfig | 2 | ||||
-rw-r--r-- | drivers/video/hitfb.c | 229 | ||||
-rw-r--r-- | drivers/video/pvr2fb.c | 22 |
15 files changed, 1234 insertions, 882 deletions
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 6a3d3af45c59..1b21c3a911d9 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -747,7 +747,7 @@ config NVRAM config RTC tristate "Enhanced Real Time Clock Support" - depends on !PPC && !PARISC && !IA64 && !M68K && (!SPARC || PCI) && !FRV && !ARM + depends on !PPC && !PARISC && !IA64 && !M68K && (!SPARC || PCI) && !FRV && !ARM && !SUPERH ---help--- If you say Y here and create a character special file /dev/rtc with major number 10 and minor number 135 using mknod ("man mknod"), you diff --git a/drivers/char/watchdog/Kconfig b/drivers/char/watchdog/Kconfig index fff89c2d88fd..f114d7b5bb2a 100644 --- a/drivers/char/watchdog/Kconfig +++ b/drivers/char/watchdog/Kconfig @@ -510,6 +510,14 @@ config SH_WDT To compile this driver as a module, choose M here: the module will be called shwdt. +config SH_WDT_MMAP + bool "Allow mmap of SH WDT" + default n + depends on SH_WDT + help + If you say Y here, user applications will be able to mmap the + WDT/CPG registers. +# # SPARC64 Architecture config WATCHDOG_CP1XXX diff --git a/drivers/char/watchdog/shwdt.c b/drivers/char/watchdog/shwdt.c index 1355038f1044..e5b8c64f1d65 100644 --- a/drivers/char/watchdog/shwdt.c +++ b/drivers/char/watchdog/shwdt.c @@ -27,7 +27,7 @@ #include <linux/notifier.h> #include <linux/ioport.h> #include <linux/fs.h> - +#include <linux/mm.h> #include <asm/io.h> #include <asm/uaccess.h> #include <asm/watchdog.h> @@ -125,7 +125,6 @@ static void sh_wdt_start(void) /** * sh_wdt_stop - Stop the Watchdog - * * Stops the watchdog. */ static void sh_wdt_stop(void) @@ -141,22 +140,20 @@ static void sh_wdt_stop(void) /** * sh_wdt_keepalive - Keep the Userspace Watchdog Alive - * * The Userspace watchdog got a KeepAlive: schedule the next heartbeat. */ -static void sh_wdt_keepalive(void) +static inline void sh_wdt_keepalive(void) { next_heartbeat = jiffies + (heartbeat * HZ); } /** * sh_wdt_set_heartbeat - Set the Userspace Watchdog heartbeat - * * Set the Userspace Watchdog heartbeat */ static int sh_wdt_set_heartbeat(int t) { - if ((t < 1) || (t > 3600)) /* arbitrary upper limit */ + if (unlikely((t < 1) || (t > 3600))) /* arbitrary upper limit */ return -EINVAL; heartbeat = t; @@ -165,7 +162,6 @@ static int sh_wdt_set_heartbeat(int t) /** * sh_wdt_ping - Ping the Watchdog - * * @data: Unused * * Clears overflow bit, resets timer counter. @@ -182,14 +178,13 @@ static void sh_wdt_ping(unsigned long data) sh_wdt_write_cnt(0); mod_timer(&timer, next_ping_period(clock_division_ratio)); - } else { - printk(KERN_WARNING PFX "Heartbeat lost! Will not ping the watchdog\n"); - } + } else + printk(KERN_WARNING PFX "Heartbeat lost! Will not ping " + "the watchdog\n"); } /** * sh_wdt_open - Open the Device - * * @inode: inode of device * @file: file handle of device * @@ -209,7 +204,6 @@ static int sh_wdt_open(struct inode *inode, struct file *file) /** * sh_wdt_close - Close the Device - * * @inode: inode of device * @file: file handle of device * @@ -220,7 +214,8 @@ static int sh_wdt_close(struct inode *inode, struct file *file) if (shwdt_expect_close == 42) { sh_wdt_stop(); } else { - printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n"); + printk(KERN_CRIT PFX "Unexpected close, not " + "stopping watchdog!\n"); sh_wdt_keepalive(); } @@ -232,7 +227,6 @@ static int sh_wdt_close(struct inode *inode, struct file *file) /** * sh_wdt_write - Write to Device - * * @file: file handle of device * @buf: buffer to write * @count: length of buffer @@ -264,8 +258,56 @@ static ssize_t sh_wdt_write(struct file *file, const char *buf, } /** - * sh_wdt_ioctl - Query Device + * sh_wdt_mmap - map WDT/CPG registers into userspace + * @file: file structure for the device + * @vma: VMA to map the registers into + * + * A simple mmap() implementation for the corner cases where the counter + * needs to be mapped in userspace directly. Due to the relatively small + * size of the area, neighbouring registers not necessarily tied to the + * CPG will also be accessible through the register page, so this remains + * configurable for users that really know what they're doing. * + * Additionaly, the register page maps in the CPG register base relative + * to the nearest page-aligned boundary, which requires that userspace do + * the appropriate CPU subtype math for calculating the page offset for + * the counter value. + */ +static int sh_wdt_mmap(struct file *file, struct vm_area_struct *vma) +{ + int ret = -ENOSYS; + +#ifdef CONFIG_SH_WDT_MMAP + unsigned long addr; + + /* Only support the simple cases where we map in a register page. */ + if (((vma->vm_end - vma->vm_start) != PAGE_SIZE) || vma->vm_pgoff) + return -EINVAL; + + /* + * Pick WTCNT as the start, it's usually the first register after the + * FRQCR, and neither one are generally page-aligned out of the box. + */ + addr = WTCNT & ~(PAGE_SIZE - 1); + + vma->vm_flags |= VM_IO; + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + if (io_remap_pfn_range(vma, vma->vm_start, addr >> PAGE_SHIFT, + PAGE_SIZE, vma->vm_page_prot)) { + printk(KERN_ERR PFX "%s: io_remap_pfn_range failed\n", + __FUNCTION__); + return -EAGAIN; + } + + ret = 0; +#endif + + return ret; +} + +/** + * sh_wdt_ioctl - Query Device * @inode: inode of device * @file: file handle of device * @cmd: watchdog command @@ -326,7 +368,6 @@ static int sh_wdt_ioctl(struct inode *inode, struct file *file, /** * sh_wdt_notify_sys - Notifier Handler - * * @this: notifier block * @code: notifier event * @unused: unused @@ -337,9 +378,8 @@ static int sh_wdt_ioctl(struct inode *inode, struct file *file, static int sh_wdt_notify_sys(struct notifier_block *this, unsigned long code, void *unused) { - if (code == SYS_DOWN || code == SYS_HALT) { + if (code == SYS_DOWN || code == SYS_HALT) sh_wdt_stop(); - } return NOTIFY_DONE; } @@ -351,10 +391,12 @@ static const struct file_operations sh_wdt_fops = { .ioctl = sh_wdt_ioctl, .open = sh_wdt_open, .release = sh_wdt_close, + .mmap = sh_wdt_mmap, }; static struct watchdog_info sh_wdt_info = { - .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, + .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | + WDIOF_MAGICCLOSE, .firmware_version = 1, .identity = "SH WDT", }; @@ -371,7 +413,6 @@ static struct miscdevice sh_wdt_miscdev = { /** * sh_wdt_init - Initialize module - * * Registers the device and notifier handler. Actual device * initialization is handled by sh_wdt_open(). */ @@ -381,15 +422,15 @@ static int __init sh_wdt_init(void) if ((clock_division_ratio < 0x5) || (clock_division_ratio > 0x7)) { clock_division_ratio = WTCSR_CKS_4096; - printk(KERN_INFO PFX "clock_division_ratio value must be 0x5<=x<=0x7, using %d\n", - clock_division_ratio); + printk(KERN_INFO PFX "clock_division_ratio value must " + "be 0x5<=x<=0x7, using %d\n", clock_division_ratio); } - if (sh_wdt_set_heartbeat(heartbeat)) - { + rc = sh_wdt_set_heartbeat(heartbeat); + if (unlikely(rc)) { heartbeat = WATCHDOG_HEARTBEAT; - printk(KERN_INFO PFX "heartbeat value must be 1<=x<=3600, using %d\n", - heartbeat); + printk(KERN_INFO PFX "heartbeat value must " + "be 1<=x<=3600, using %d\n", heartbeat); } init_timer(&timer); @@ -397,15 +438,16 @@ static int __init sh_wdt_init(void) timer.data = 0; rc = register_reboot_notifier(&sh_wdt_notifier); - if (rc) { - printk(KERN_ERR PFX "Can't register reboot notifier (err=%d)\n", rc); + if (unlikely(rc)) { + printk(KERN_ERR PFX "Can't register reboot notifier (err=%d)\n", + rc); return rc; } rc = misc_register(&sh_wdt_miscdev); - if (rc) { - printk(KERN_ERR PFX "Can't register miscdev on minor=%d (err=%d)\n", - sh_wdt_miscdev.minor, rc); + if (unlikely(rc)) { + printk(KERN_ERR PFX "Can't register miscdev on " + "minor=%d (err=%d)\n", sh_wdt_miscdev.minor, rc); unregister_reboot_notifier(&sh_wdt_notifier); return rc; } @@ -418,7 +460,6 @@ static int __init sh_wdt_init(void) /** * sh_wdt_exit - Deinitialize module - * * Unregisters the device and notifier handler. Actual device * deinitialization is handled by sh_wdt_close(). */ @@ -434,14 +475,13 @@ MODULE_LICENSE("GPL"); MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); module_param(clock_division_ratio, int, 0); -MODULE_PARM_DESC(clock_division_ratio, "Clock division ratio. Valid ranges are from 0x5 (1.31ms) to 0x7 (5.25ms). Defaults to 0x7."); +MODULE_PARM_DESC(clock_division_ratio, "Clock division ratio. Valid ranges are from 0x5 (1.31ms) to 0x7 (5.25ms). (default=" __MODULE_STRING(clock_division_ratio) ")"); module_param(heartbeat, int, 0); MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (1<=heartbeat<=3600, default=" __MODULE_STRING(WATCHDOG_HEARTBEAT) ")"); module_param(nowayout, int, 0); -MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)"); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); module_init(sh_wdt_init); module_exit(sh_wdt_exit); - diff --git a/drivers/input/touchscreen/hp680_ts_input.c b/drivers/input/touchscreen/hp680_ts_input.c index fa97e0f79e7e..ee6c2f40cdf6 100644 --- a/drivers/input/touchscreen/hp680_ts_input.c +++ b/drivers/input/touchscreen/hp680_ts_input.c @@ -15,7 +15,6 @@ #define HP680_TS_ABS_Y_MIN 80 #define HP680_TS_ABS_Y_MAX 910 -#define SCPCR 0xa4000116 #define PHDR 0xa400012e #define SCPDR 0xa4000136 @@ -77,19 +76,6 @@ static irqreturn_t hp680_ts_interrupt(int irq, void *dev, struct pt_regs *regs) static int __init hp680_ts_init(void) { - u8 scpdr; - u16 scpcr; - - scpdr = ctrl_inb(SCPDR); - scpdr |= SCPDR_TS_SCAN_X | SCPDR_TS_SCAN_Y; - scpdr &= ~SCPDR_TS_SCAN_ENABLE; - ctrl_outb(scpdr, SCPDR); - - scpcr = ctrl_inw(SCPCR); - scpcr &= ~SCPCR_TS_MASK; - scpcr |= SCPCR_TS_ENABLE; - ctrl_outw(scpcr, SCPCR); - hp680_ts_dev = input_allocate_device(); if (!hp680_ts_dev) return -ENOMEM; diff --git a/drivers/net/stnic.c b/drivers/net/stnic.c index 3fd7a4fee665..e6f90427160c 100644 --- a/drivers/net/stnic.c +++ b/drivers/net/stnic.c @@ -19,7 +19,7 @@ #include <asm/system.h> #include <asm/io.h> -#include <asm/se/se.h> +#include <asm/se.h> #include <asm/machvec.h> #ifdef CONFIG_SH_STANDARD_BIOS #include <asm/sh_bios.h> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 7ff1d88094b6..33a7b720539b 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -238,6 +238,16 @@ config RTC_DRV_SA1100 To compile this driver as a module, choose M here: the module will be called rtc-sa1100. +config RTC_DRV_SH + tristate "SuperH On-Chip RTC" + depends on RTC_CLASS && SUPERH + help + Say Y here to enable support for the on-chip RTC found in + most SuperH processors. + + To compile this driver as a module, choose M here: the + module will be called rtc-sh. + config RTC_DRV_VR41XX tristate "NEC VR41XX" depends on RTC_CLASS && CPU_VR41XX diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index bbcfb09d81d9..e72d467ab214 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -31,3 +31,4 @@ obj-$(CONFIG_RTC_DRV_PL031) += rtc-pl031.o obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o obj-$(CONFIG_RTC_DRV_V3020) += rtc-v3020.o obj-$(CONFIG_RTC_DRV_AT91) += rtc-at91.o +obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o diff --git a/drivers/rtc/rtc-sh.c b/drivers/rtc/rtc-sh.c new file mode 100644 index 000000000000..d2ce0c8bb8f3 --- /dev/null +++ b/drivers/rtc/rtc-sh.c @@ -0,0 +1,467 @@ +/* + * SuperH On-Chip RTC Support + * + * Copyright (C) 2006 Paul Mundt + * + * Based on the old arch/sh/kernel/cpu/rtc.c by: + * + * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org> + * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka + * + * 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/module.h> +#include <linux/kernel.h> +#include <linux/bcd.h> +#include <linux/rtc.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/seq_file.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <asm/io.h> + +#ifdef CONFIG_CPU_SH3 +#define rtc_reg_size sizeof(u16) +#define RTC_BIT_INVERTED 0 /* No bug on SH7708, SH7709A */ +#elif defined(CONFIG_CPU_SH4) +#define rtc_reg_size sizeof(u32) +#define RTC_BIT_INVERTED 0x40 /* bug on SH7750, SH7750S */ +#endif + +#define RTC_REG(r) ((r) * rtc_reg_size) + +#define R64CNT RTC_REG(0) +#define RSECCNT RTC_REG(1) +#define RMINCNT RTC_REG(2) +#define RHRCNT RTC_REG(3) +#define RWKCNT RTC_REG(4) +#define RDAYCNT RTC_REG(5) +#define RMONCNT RTC_REG(6) +#define RYRCNT RTC_REG(7) +#define RSECAR RTC_REG(8) +#define RMINAR RTC_REG(9) +#define RHRAR RTC_REG(10) +#define RWKAR RTC_REG(11) +#define RDAYAR RTC_REG(12) +#define RMONAR RTC_REG(13) +#define RCR1 RTC_REG(14) +#define RCR2 RTC_REG(15) + +/* RCR1 Bits */ +#define RCR1_CF 0x80 /* Carry Flag */ +#define RCR1_CIE 0x10 /* Carry Interrupt Enable */ +#define RCR1_AIE 0x08 /* Alarm Interrupt Enable */ +#define RCR1_AF 0x01 /* Alarm Flag */ + +/* RCR2 Bits */ +#define RCR2_PEF 0x80 /* PEriodic interrupt Flag */ +#define RCR2_PESMASK 0x70 /* Periodic interrupt Set */ +#define RCR2_RTCEN 0x08 /* ENable RTC */ +#define RCR2_ADJ 0x04 /* ADJustment (30-second) */ +#define RCR2_RESET 0x02 /* Reset bit */ +#define RCR2_START 0x01 /* Start bit */ + +struct sh_rtc { + void __iomem *regbase; + unsigned long regsize; + struct resource *res; + unsigned int alarm_irq, periodic_irq, carry_irq; + struct rtc_device *rtc_dev; + spinlock_t lock; +}; + +static irqreturn_t sh_rtc_interrupt(int irq, void *id, struct pt_regs *regs) +{ + struct platform_device *pdev = id; + struct sh_rtc *rtc = platform_get_drvdata(pdev); + unsigned int tmp, events = 0; + + spin_lock(&rtc->lock); + + tmp = readb(rtc->regbase + RCR1); + + if (tmp & RCR1_AF) + events |= RTC_AF | RTC_IRQF; + + tmp &= ~(RCR1_CF | RCR1_AF); + + writeb(tmp, rtc->regbase + RCR1); + + rtc_update_irq(&rtc->rtc_dev->class_dev, 1, events); + + spin_unlock(&rtc->lock); + + return IRQ_HANDLED; +} + +static irqreturn_t sh_rtc_periodic(int irq, void *id, struct pt_regs *regs) +{ + struct sh_rtc *rtc = dev_get_drvdata(id); + + spin_lock(&rtc->lock); + + rtc_update_irq(&rtc->rtc_dev->class_dev, 1, RTC_PF | RTC_IRQF); + + spin_unlock(&rtc->lock); + + return IRQ_HANDLED; +} + +static inline void sh_rtc_setpie(struct device *dev, unsigned int enable) +{ + struct sh_rtc *rtc = dev_get_drvdata(dev); + unsigned int tmp; + + spin_lock_irq(&rtc->lock); + + tmp = readb(rtc->regbase + RCR2); + + if (enable) { + tmp &= ~RCR2_PESMASK; + tmp |= RCR2_PEF | (2 << 4); + } else + tmp &= ~(RCR2_PESMASK | RCR2_PEF); + + writeb(tmp, rtc->regbase + RCR2); + + spin_unlock_irq(&rtc->lock); +} + +static inline void sh_rtc_setaie(struct device *dev, unsigned int enable) +{ + struct sh_rtc *rtc = dev_get_drvdata(dev); + unsigned int tmp; + + spin_lock_irq(&rtc->lock); + + tmp = readb(rtc->regbase + RCR1); + + if (enable) + tmp |= RCR1_AIE; + else + tmp &= ~RCR1_AIE; + + writeb(tmp, rtc->regbase + RCR1); + + spin_unlock_irq(&rtc->lock); +} + +static int sh_rtc_open(struct device *dev) +{ + struct sh_rtc *rtc = dev_get_drvdata(dev); + unsigned int tmp; + int ret; + + tmp = readb(rtc->regbase + RCR1); + tmp &= ~RCR1_CF; + tmp |= RCR1_CIE; + writeb(tmp, rtc->regbase + RCR1); + + ret = request_irq(rtc->periodic_irq, sh_rtc_periodic, SA_INTERRUPT, + "sh-rtc period", dev); + if (unlikely(ret)) { + dev_err(dev, "request period IRQ failed with %d, IRQ %d\n", + ret, rtc->periodic_irq); + return ret; + } + + ret = request_irq(rtc->carry_irq, sh_rtc_interrupt, SA_INTERRUPT, + "sh-rtc carry", dev); + if (unlikely(ret)) { + dev_err(dev, "request carry IRQ failed with %d, IRQ %d\n", + ret, rtc->carry_irq); + free_irq(rtc->periodic_irq, dev); + goto err_bad_carry; + } + + ret = request_irq(rtc->alarm_irq, sh_rtc_interrupt, SA_INTERRUPT, + "sh-rtc alarm", dev); + if (unlikely(ret)) { + dev_err(dev, "request alarm IRQ failed with %d, IRQ %d\n", + ret, rtc->alarm_irq); + goto err_bad_alarm; + } + + return 0; + +err_bad_alarm: + free_irq(rtc->carry_irq, dev); +err_bad_carry: + free_irq(rtc->periodic_irq, dev); + + return ret; +} + +static void sh_rtc_release(struct device *dev) +{ + struct sh_rtc *rtc = dev_get_drvdata(dev); + + sh_rtc_setpie(dev, 0); + + free_irq(rtc->periodic_irq, dev); + free_irq(rtc->carry_irq, dev); + free_irq(rtc->alarm_irq, dev); +} + +static int sh_rtc_proc(struct device *dev, struct seq_file *seq) +{ + struct sh_rtc *rtc = dev_get_drvdata(dev); + unsigned int tmp; + + tmp = readb(rtc->regbase + RCR1); + seq_printf(seq, "alarm_IRQ\t: %s\n", + (tmp & RCR1_AIE) ? "yes" : "no"); + seq_printf(seq, "carry_IRQ\t: %s\n", + (tmp & RCR1_CIE) ? "yes" : "no"); + + tmp = readb(rtc->regbase + RCR2); + seq_printf(seq, "periodic_IRQ\t: %s\n", + (tmp & RCR2_PEF) ? "yes" : "no"); + + return 0; +} + +static int sh_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) +{ + unsigned int ret = -ENOIOCTLCMD; + + switch (cmd) { + case RTC_PIE_OFF: + case RTC_PIE_ON: + sh_rtc_setpie(dev, cmd == RTC_PIE_ON); + ret = 0; + break; + case RTC_AIE_OFF: + case RTC_AIE_ON: + sh_rtc_setaie(dev, cmd == RTC_AIE_ON); + ret = 0; + break; + } + + return ret; +} + +static int sh_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct platform_device *pdev = to_platform_device(dev); + struct sh_rtc *rtc = platform_get_drvdata(pdev); + unsigned int sec128, sec2, yr, yr100, cf_bit; + + do { + unsigned int tmp; + + spin_lock_irq(&rtc->lock); + + tmp = readb(rtc->regbase + RCR1); + tmp &= ~RCR1_CF; /* Clear CF-bit */ + tmp |= RCR1_CIE; + writeb(tmp, rtc->regbase + RCR1); + + sec128 = readb(rtc->regbase + R64CNT); + + tm->tm_sec = BCD2BIN(readb(rtc->regbase + RSECCNT)); + tm->tm_min = BCD2BIN(readb(rtc->regbase + RMINCNT)); + tm->tm_hour = BCD2BIN(readb(rtc->regbase + RHRCNT)); + tm->tm_wday = BCD2BIN(readb(rtc->regbase + RWKCNT)); + tm->tm_mday = BCD2BIN(readb(rtc->regbase + RDAYCNT)); + tm->tm_mon = BCD2BIN(readb(rtc->regbase + RMONCNT)); + +#if defined(CONFIG_CPU_SH4) + yr = readw(rtc->regbase + RYRCNT); + yr100 = BCD2BIN(yr >> 8); + yr &= 0xff; +#else + yr = readb(rtc->regbase + RYRCNT); + yr100 = BCD2BIN((yr == 0x99) ? 0x19 : 0x20); +#endif + + tm->tm_year = (yr100 * 100 + BCD2BIN(yr)) - 1900; + + sec2 = readb(rtc->regbase + R64CNT); + cf_bit = readb(rtc->regbase + RCR1) & RCR1_CF; + + spin_unlock_irq(&rtc->lock); + } while (cf_bit != 0 || ((sec128 ^ sec2) & RTC_BIT_INVERTED) != 0); + +#if RTC_BIT_INVERTED != 0 + if ((sec128 & RTC_BIT_INVERTED)) + tm->tm_sec--; +#endif + + dev_dbg(&dev, "%s: tm is secs=%d, mins=%d, hours=%d, " + "mday=%d, mon=%d, year=%d, wday=%d\n", + __FUNCTION__, + tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + + if (rtc_valid_tm(tm) < 0) + dev_err(dev, "invalid date\n"); + + return 0; +} + +static int sh_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct platform_device *pdev = to_platform_device(dev); + struct sh_rtc *rtc = platform_get_drvdata(pdev); + unsigned int tmp; + int year; + + spin_lock_irq(&rtc->lock); + + /* Reset pre-scaler & stop RTC */ + tmp = readb(rtc->regbase + RCR2); + tmp |= RCR2_RESET; + writeb(tmp, rtc->regbase + RCR2); + + writeb(BIN2BCD(tm->tm_sec), rtc->regbase + RSECCNT); + writeb(BIN2BCD(tm->tm_min), rtc->regbase + RMINCNT); + writeb(BIN2BCD(tm->tm_hour), rtc->regbase + RHRCNT); + writeb(BIN2BCD(tm->tm_wday), rtc->regbase + RWKCNT); + writeb(BIN2BCD(tm->tm_mday), rtc->regbase + RDAYCNT); + writeb(BIN2BCD(tm->tm_mon), rtc->regbase + RMONCNT); + +#ifdef CONFIG_CPU_SH3 + year = tm->tm_year % 100; + writeb(BIN2BCD(year), rtc->regbase + RYRCNT); +#else + year = (BIN2BCD((tm->tm_year + 1900) / 100) << 8) | + BIN2BCD(tm->tm_year % 100); + writew(year, rtc->regbase + RYRCNT); +#endif + + /* Start RTC */ + tmp = readb(rtc->regbase + RCR2); + tmp &= ~RCR2_RESET; + tmp |= RCR2_RTCEN | RCR2_START; + writeb(tmp, rtc->regbase + RCR2); + + spin_unlock_irq(&rtc->lock); + + return 0; +} + +static struct rtc_class_ops sh_rtc_ops = { + .open = sh_rtc_open, + .release = sh_rtc_release, + .ioctl = sh_rtc_ioctl, + .read_time = sh_rtc_read_time, + .set_time = sh_rtc_set_time, + .proc = sh_rtc_proc, +}; + +static int __devinit sh_rtc_probe(struct platform_device *pdev) +{ + struct sh_rtc *rtc; + struct resource *res; + int ret = -ENOENT; + + rtc = kzalloc(sizeof(struct sh_rtc), GFP_KERNEL); + if (unlikely(!rtc)) + return -ENOMEM; + + spin_lock_init(&rtc->lock); + + rtc->periodic_irq = platform_get_irq(pdev, 0); + if (unlikely(rtc->periodic_irq < 0)) { + dev_err(&pdev->dev, "No IRQ for period\n"); + goto err_badres; + } + + rtc->carry_irq = platform_get_irq(pdev, 1); + if (unlikely(rtc->carry_irq < 0)) { + dev_err(&pdev->dev, "No IRQ for carry\n"); + goto err_badres; + } + + rtc->alarm_irq = platform_get_irq(pdev, 2); + if (unlikely(rtc->alarm_irq < 0)) { + dev_err(&pdev->dev, "No IRQ for alarm\n"); + goto err_badres; + } + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (unlikely(res == NULL)) { + dev_err(&pdev->dev, "No IO resource\n"); + goto err_badres; + } + + rtc->regsize = res->end - res->start + 1; + + rtc->res = request_mem_region(res->start, rtc->regsize, pdev->name); + if (unlikely(!rtc->res)) { + ret = -EBUSY; + goto err_badres; + } + + rtc->regbase = (void __iomem *)rtc->res->start; + if (unlikely(!rtc->regbase)) { + ret = -EINVAL; + goto err_badmap; + } + + rtc->rtc_dev = rtc_device_register("sh", &pdev->dev, + &sh_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc)) { + ret = PTR_ERR(rtc->rtc_dev); + goto err_badmap; + } + + platform_set_drvdata(pdev, rtc); + + return 0; + +err_badmap: + release_resource(rtc->res); +err_badres: + kfree(rtc); + + return ret; +} + +static int __devexit sh_rtc_remove(struct platform_device *pdev) +{ + struct sh_rtc *rtc = platform_get_drvdata(pdev); + + if (likely(rtc->rtc_dev)) + rtc_device_unregister(rtc->rtc_dev); + + sh_rtc_setpie(&pdev->dev, 0); + sh_rtc_setaie(&pdev->dev, 0); + + release_resource(rtc->res); + + platform_set_drvdata(pdev, NULL); + + kfree(rtc); + + return 0; +} +static struct platform_driver sh_rtc_platform_driver = { + .driver = { + .name = "sh-rtc", + .owner = THIS_MODULE, + }, + .probe = sh_rtc_probe, + .remove = __devexit_p(sh_rtc_remove), +}; + +static int __init sh_rtc_init(void) +{ + return platform_driver_register(&sh_rtc_platform_driver); +} + +static void __exit sh_rtc_exit(void) +{ + platform_driver_unregister(&sh_rtc_platform_driver); +} + +module_init(sh_rtc_init); +module_exit(sh_rtc_exit); + +MODULE_DESCRIPTION("SuperH on-chip RTC driver"); +MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 5b48ac22c9c5..261eaa442953 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -642,12 +642,17 @@ config V850E_UART_CONSOLE select SERIAL_CORE_CONSOLE config SERIAL_SH_SCI - tristate "SH SCI(F) serial port support" + tristate "SuperH SCI(F) serial port support" depends on SUPERH || H8300 select SERIAL_CORE +config SERIAL_SH_SCI_NR_UARTS + int "Maximum number of SCI(F) serial ports" + depends on SERIAL_SH_SCI + default "2" + config SERIAL_SH_SCI_CONSOLE - bool "Support for console on SH SCI(F)" + bool "Support for console on SuperH SCI(F)" depends on SERIAL_SH_SCI=y select SERIAL_CORE_CONSOLE diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c index cbede06cac27..f336ba6778dd 100644 --- a/drivers/serial/sh-sci.c +++ b/drivers/serial/sh-sci.c @@ -3,7 +3,7 @@ * * SuperH on-chip serial module support. (SCI with no FIFO / with FIFO) * - * Copyright (C) 2002, 2003, 2004 Paul Mundt + * Copyright (C) 2002 - 2006 Paul Mundt * * based off of the old drivers/char/sh-sci.c by: * @@ -20,10 +20,9 @@ #undef DEBUG +#include <linux/config.h> #include <linux/module.h> #include <linux/errno.h> -#include <linux/signal.h> -#include <linux/sched.h> #include <linux/timer.h> #include <linux/interrupt.h> #include <linux/tty.h> @@ -32,71 +31,77 @@ #include <linux/major.h> #include <linux/string.h> #include <linux/sysrq.h> -#include <linux/fcntl.h> -#include <linux/ptrace.h> #include <linux/ioport.h> #include <linux/mm.h> -#include <linux/slab.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/console.h> -#include <linux/bitops.h> -#include <linux/generic_serial.h> +#include <linux/platform_device.h> #ifdef CONFIG_CPU_FREQ #include <linux/notifier.h> #include <linux/cpufreq.h> #endif -#include <asm/system.h> -#include <asm/io.h> -#include <asm/irq.h> -#include <asm/uaccess.h> - #if defined(CONFIG_SUPERH) && !defined(CONFIG_SUPERH64) #include <asm/clock.h> -#endif - -#ifdef CONFIG_SH_STANDARD_BIOS #include <asm/sh_bios.h> +#include <asm/kgdb.h> #endif +#include <asm/sci.h> + #if defined(CONFIG_SERIAL_SH_SCI_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) #define SUPPORT_SYSRQ #endif #include "sh-sci.h" -#ifdef CONFIG_SH_KGDB -#include <asm/kgdb.h> +struct sci_port { + struct uart_port port; + + /* Port type */ + unsigned int type; + + /* Port IRQs: ERI, RXI, TXI, BRI (optional) */ + unsigned int irqs[SCIx_NR_IRQS]; + + /* Port pin configuration */ + void (*init_pins)(struct uart_port *port, + unsigned int cflag); -static int kgdb_get_char(struct sci_port *port); -static void kgdb_put_char(struct sci_port *port, char c); -static void kgdb_handle_error(struct sci_port *port); + /* Port enable callback */ + void (*enable)(struct uart_port *port); + + /* Port disable callback */ + void (*disable)(struct uart_port *port); + + /* Break timer */ + struct timer_list break_timer; + int break_flag; +}; + +#ifdef CONFIG_SH_KGDB static struct sci_port *kgdb_sci_port; -#endif /* CONFIG_SH_KGDB */ +#endif #ifdef CONFIG_SERIAL_SH_SCI_CONSOLE -static struct sci_port *serial_console_port = 0; -#endif /* CONFIG_SERIAL_SH_SCI_CONSOLE */ +static struct sci_port *serial_console_port; +#endif /* Function prototypes */ static void sci_stop_tx(struct uart_port *port); -static void sci_start_tx(struct uart_port *port); -static void sci_start_rx(struct uart_port *port, unsigned int tty_start); -static void sci_stop_rx(struct uart_port *port); -static int sci_request_irq(struct sci_port *port); -static void sci_free_irq(struct sci_port *port); - -static struct sci_port sci_ports[]; -static struct uart_driver sci_uart_driver; -#define SCI_NPORTS sci_uart_driver.nr +#define SCI_NPORTS CONFIG_SERIAL_SH_SCI_NR_UARTS -#if defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_SH_KGDB) +static struct sci_port sci_ports[SCI_NPORTS]; +static struct uart_driver sci_uart_driver; -static void handle_error(struct uart_port *port) -{ /* Clear error flags */ +#if defined(CONFIG_SERIAL_SH_SCI_CONSOLE) && \ + defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_SH_KGDB) +static inline void handle_error(struct uart_port *port) +{ + /* Clear error flags */ sci_out(port, SCxSR, SCxSR_ERROR_CLEAR(port)); } @@ -106,8 +111,8 @@ static int get_char(struct uart_port *port) unsigned short status; int c; - local_irq_save(flags) |