diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/input/keyboard/Kconfig | 9 | ||||
-rw-r--r-- | drivers/input/keyboard/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/keyboard/sh_keysc.c | 280 | ||||
-rw-r--r-- | drivers/rtc/rtc-sh.c | 296 | ||||
-rw-r--r-- | drivers/serial/sh-sci.c | 7 | ||||
-rw-r--r-- | drivers/serial/sh-sci.h | 60 |
6 files changed, 528 insertions, 125 deletions
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 8ea709be3306..efd70a974591 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -314,4 +314,13 @@ config KEYBOARD_BFIN To compile this driver as a module, choose M here: the module will be called bf54x-keys. +config KEYBOARD_SH_KEYSC + tristate "SuperH KEYSC keypad support" + depends on SUPERH + help + Say Y here if you want to use a keypad attached to the KEYSC block + on SuperH processors such as sh7722 and sh7343. + + To compile this driver as a module, choose M here: the + module will be called sh_keysc. endif diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index e741f4031012..0edc8f285d1c 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -26,3 +26,4 @@ obj-$(CONFIG_KEYBOARD_HP6XX) += jornada680_kbd.o obj-$(CONFIG_KEYBOARD_HP7XX) += jornada720_kbd.o obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o +obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o diff --git a/drivers/input/keyboard/sh_keysc.c b/drivers/input/keyboard/sh_keysc.c new file mode 100644 index 000000000000..8486abc457ed --- /dev/null +++ b/drivers/input/keyboard/sh_keysc.c @@ -0,0 +1,280 @@ +/* + * SuperH KEYSC Keypad Driver + * + * Copyright (C) 2008 Magnus Damm + * + * Based on gpio_keys.c, Copyright 2005 Phil Blundell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/input.h> +#include <linux/io.h> +#include <asm/sh_keysc.h> + +#define KYCR1_OFFS 0x00 +#define KYCR2_OFFS 0x04 +#define KYINDR_OFFS 0x08 +#define KYOUTDR_OFFS 0x0c + +#define KYCR2_IRQ_LEVEL 0x10 +#define KYCR2_IRQ_DISABLED 0x00 + +static const struct { + unsigned char kymd, keyout, keyin; +} sh_keysc_mode[] = { + [SH_KEYSC_MODE_1] = { 0, 6, 5 }, + [SH_KEYSC_MODE_2] = { 1, 5, 6 }, + [SH_KEYSC_MODE_3] = { 2, 4, 7 }, +}; + +struct sh_keysc_priv { + void __iomem *iomem_base; + unsigned long last_keys; + struct input_dev *input; + struct sh_keysc_info pdata; +}; + +static irqreturn_t sh_keysc_isr(int irq, void *dev_id) +{ + struct platform_device *pdev = dev_id; + struct sh_keysc_priv *priv = platform_get_drvdata(pdev); + struct sh_keysc_info *pdata = &priv->pdata; + unsigned long keys, keys1, keys0, mask; + unsigned char keyin_set, tmp; + int i, k; + + dev_dbg(&pdev->dev, "isr!\n"); + + keys1 = ~0; + keys0 = 0; + + do { + keys = 0; + keyin_set = 0; + + iowrite16(KYCR2_IRQ_DISABLED, priv->iomem_base + KYCR2_OFFS); + + for (i = 0; i < sh_keysc_mode[pdata->mode].keyout; i++) { + iowrite16(0xfff ^ (3 << (i * 2)), + priv->iomem_base + KYOUTDR_OFFS); + udelay(pdata->delay); + tmp = ioread16(priv->iomem_base + KYINDR_OFFS); + keys |= tmp << (sh_keysc_mode[pdata->mode].keyin * i); + tmp ^= (1 << sh_keysc_mode[pdata->mode].keyin) - 1; + keyin_set |= tmp; + } + + iowrite16(0, priv->iomem_base + KYOUTDR_OFFS); + iowrite16(KYCR2_IRQ_LEVEL | (keyin_set << 8), + priv->iomem_base + KYCR2_OFFS); + + keys ^= ~0; + keys &= (1 << (sh_keysc_mode[pdata->mode].keyin * + sh_keysc_mode[pdata->mode].keyout)) - 1; + keys1 &= keys; + keys0 |= keys; + + dev_dbg(&pdev->dev, "keys 0x%08lx\n", keys); + + } while (ioread16(priv->iomem_base + KYCR2_OFFS) & 0x01); + + dev_dbg(&pdev->dev, "last_keys 0x%08lx keys0 0x%08lx keys1 0x%08lx\n", + priv->last_keys, keys0, keys1); + + for (i = 0; i < SH_KEYSC_MAXKEYS; i++) { + k = pdata->keycodes[i]; + if (!k) + continue; + + mask = 1 << i; + + if (!((priv->last_keys ^ keys0) & mask)) + continue; + + if ((keys1 | keys0) & mask) { + input_event(priv->input, EV_KEY, k, 1); + priv->last_keys |= mask; + } + + if (!(keys1 & mask)) { + input_event(priv->input, EV_KEY, k, 0); + priv->last_keys &= ~mask; + } + + } + input_sync(priv->input); + + return IRQ_HANDLED; +} + +#define res_size(res) ((res)->end - (res)->start + 1) + +static int __devinit sh_keysc_probe(struct platform_device *pdev) +{ + struct sh_keysc_priv *priv; + struct sh_keysc_info *pdata; + struct resource *res; + struct input_dev *input; + int i, k; + int irq, error; + + if (!pdev->dev.platform_data) { + dev_err(&pdev->dev, "no platform data defined\n"); + error = -EINVAL; + goto err0; + } + + error = -ENXIO; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "failed to get I/O memory\n"); + goto err0; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "failed to get irq\n"); + goto err0; + } + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (priv == NULL) { + dev_err(&pdev->dev, "failed to allocate driver data\n"); + error = -ENOMEM; + goto err0; + } + + platform_set_drvdata(pdev, priv); + memcpy(&priv->pdata, pdev->dev.platform_data, sizeof(priv->pdata)); + pdata = &priv->pdata; + + res = request_mem_region(res->start, res_size(res), pdev->name); + if (res == NULL) { + dev_err(&pdev->dev, "failed to request I/O memory\n"); + error = -EBUSY; + goto err1; + } + + priv->iomem_base = ioremap_nocache(res->start, res_size(res)); + if (priv->iomem_base == NULL) { + dev_err(&pdev->dev, "failed to remap I/O memory\n"); + error = -ENXIO; + goto err2; + } + + priv->input = input_allocate_device(); + if (!priv->input) { + dev_err(&pdev->dev, "failed to allocate input device\n"); + error = -ENOMEM; + goto err3; + } + + input = priv->input; + input->evbit[0] = BIT_MASK(EV_KEY); + + input->name = pdev->name; + input->phys = "sh-keysc-keys/input0"; + input->dev.parent = &pdev->dev; + + input->id.bustype = BUS_HOST; + input->id.vendor = 0x0001; + input->id.product = 0x0001; + input->id.version = 0x0100; + + error = request_irq(irq, sh_keysc_isr, 0, pdev->name, pdev); + if (error) { + dev_err(&pdev->dev, "failed to request IRQ\n"); + goto err4; + } + + for (i = 0; i < SH_KEYSC_MAXKEYS; i++) { + k = pdata->keycodes[i]; + if (k) + input_set_capability(input, EV_KEY, k); + } + + error = input_register_device(input); + if (error) { + dev_err(&pdev->dev, "failed to register input device\n"); + goto err5; + } + + iowrite16((sh_keysc_mode[pdata->mode].kymd << 8) | + pdata->scan_timing, priv->iomem_base + KYCR1_OFFS); + iowrite16(0, priv->iomem_base + KYOUTDR_OFFS); + iowrite16(KYCR2_IRQ_LEVEL, priv->iomem_base + KYCR2_OFFS); + return 0; + err5: + free_irq(irq, pdev); + err4: + input_free_device(input); + err3: + iounmap(priv->iomem_base); + err2: + release_mem_region(res->start, res_size(res)); + err1: + platform_set_drvdata(pdev, NULL); + kfree(priv); + err0: + return error; +} + +static int __devexit sh_keysc_remove(struct platform_device *pdev) +{ + struct sh_keysc_priv *priv = platform_get_drvdata(pdev); + struct resource *res; + + iowrite16(KYCR2_IRQ_DISABLED, priv->iomem_base + KYCR2_OFFS); + + input_unregister_device(priv->input); + free_irq(platform_get_irq(pdev, 0), pdev); + iounmap(priv->iomem_base); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, res_size(res)); + + platform_set_drvdata(pdev, NULL); + kfree(priv); + return 0; +} + + +#define sh_keysc_suspend NULL +#define sh_keysc_resume NULL + +struct platform_driver sh_keysc_device_driver = { + .probe = sh_keysc_probe, + .remove = __devexit_p(sh_keysc_remove), + .suspend = sh_keysc_suspend, + .resume = sh_keysc_resume, + .driver = { + .name = "sh_keysc", + } +}; + +static int __init sh_keysc_init(void) +{ + return platform_driver_register(&sh_keysc_device_driver); +} + +static void __exit sh_keysc_exit(void) +{ + platform_driver_unregister(&sh_keysc_device_driver); +} + +module_init(sh_keysc_init); +module_exit(sh_keysc_exit); + +MODULE_AUTHOR("Magnus Damm"); +MODULE_DESCRIPTION("SuperH KEYSC Keypad Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-sh.c b/drivers/rtc/rtc-sh.c index 9e9caa5d7f5f..c594b34c6767 100644 --- a/drivers/rtc/rtc-sh.c +++ b/drivers/rtc/rtc-sh.c @@ -1,8 +1,9 @@ /* * SuperH On-Chip RTC Support * - * Copyright (C) 2006, 2007 Paul Mundt + * Copyright (C) 2006, 2007, 2008 Paul Mundt * Copyright (C) 2006 Jamie Lenehan + * Copyright (C) 2008 Angelo Castello * * Based on the old arch/sh/kernel/cpu/rtc.c by: * @@ -26,7 +27,7 @@ #include <asm/rtc.h> #define DRV_NAME "sh-rtc" -#define DRV_VERSION "0.1.6" +#define DRV_VERSION "0.2.0" #define RTC_REG(r) ((r) * rtc_reg_size) @@ -63,6 +64,13 @@ /* ALARM Bits - or with BCD encoded value */ #define AR_ENB 0x80 /* Enable for alarm cmp */ +/* Period Bits */ +#define PF_HP 0x100 /* Enable Half Period to support 8,32,128Hz */ +#define PF_COUNT 0x200 /* Half periodic counter */ +#define PF_OXS 0x400 /* Periodic One x Second */ +#define PF_KOU 0x800 /* Kernel or User periodic request 1=kernel */ +#define PF_MASK 0xf00 + /* RCR1 Bits */ #define RCR1_CF 0x80 /* Carry Flag */ #define RCR1_CIE 0x10 /* Carry Interrupt Enable */ @@ -84,33 +92,24 @@ struct sh_rtc { unsigned int alarm_irq, periodic_irq, carry_irq; struct rtc_device *rtc_dev; spinlock_t lock; - int rearm_aie; unsigned long capabilities; /* See asm-sh/rtc.h for cap bits */ + unsigned short periodic_freq; }; static irqreturn_t sh_rtc_interrupt(int irq, void *dev_id) { - struct platform_device *pdev = to_platform_device(dev_id); - struct sh_rtc *rtc = platform_get_drvdata(pdev); - unsigned int tmp, events = 0; + struct sh_rtc *rtc = dev_id; + unsigned int tmp; spin_lock(&rtc->lock); tmp = readb(rtc->regbase + RCR1); tmp &= ~RCR1_CF; - - if (rtc->rearm_aie) { - if (tmp & RCR1_AF) - tmp &= ~RCR1_AF; /* try to clear AF again */ - else { - tmp |= RCR1_AIE; /* AF has cleared, rearm IRQ */ - rtc->rearm_aie = 0; - } - } - writeb(tmp, rtc->regbase + RCR1); - rtc_update_irq(rtc->rtc_dev, 1, events); + /* Users have requested One x Second IRQ */ + if (rtc->periodic_freq & PF_OXS) + rtc_update_irq(rtc->rtc_dev, 1, RTC_UF | RTC_IRQF); spin_unlock(&rtc->lock); @@ -119,47 +118,48 @@ static irqreturn_t sh_rtc_interrupt(int irq, void *dev_id) static irqreturn_t sh_rtc_alarm(int irq, void *dev_id) { - struct platform_device *pdev = to_platform_device(dev_id); - struct sh_rtc *rtc = platform_get_drvdata(pdev); - unsigned int tmp, events = 0; + struct sh_rtc *rtc = dev_id; + unsigned int tmp; spin_lock(&rtc->lock); tmp = readb(rtc->regbase + RCR1); - - /* - * If AF is set then the alarm has triggered. If we clear AF while - * the alarm time still matches the RTC time then AF will - * immediately be set again, and if AIE is enabled then the alarm - * interrupt will immediately be retrigger. So we clear AIE here - * and use rtc->rearm_aie so that the carry interrupt will keep - * trying to clear AF and once it stays cleared it'll re-enable - * AIE. - */ - if (tmp & RCR1_AF) { - events |= RTC_AF | RTC_IRQF; - - tmp &= ~(RCR1_AF|RCR1_AIE); - + tmp &= ~(RCR1_AF | RCR1_AIE); writeb(tmp, rtc->regbase + RCR1); - rtc->rearm_aie = 1; - - rtc_update_irq(rtc->rtc_dev, 1, events); - } + rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF); spin_unlock(&rtc->lock); + return IRQ_HANDLED; } static irqreturn_t sh_rtc_periodic(int irq, void *dev_id) { - struct platform_device *pdev = to_platform_device(dev_id); - struct sh_rtc *rtc = platform_get_drvdata(pdev); + struct sh_rtc *rtc = dev_id; + struct rtc_device *rtc_dev = rtc->rtc_dev; + unsigned int tmp; spin_lock(&rtc->lock); - rtc_update_irq(rtc->rtc_dev, 1, RTC_PF | RTC_IRQF); + tmp = readb(rtc->regbase + RCR2); + tmp &= ~RCR2_PEF; + writeb(tmp, rtc->regbase + RCR2); + + /* Half period enabled than one skipped and the next notified */ + if ((rtc->periodic_freq & PF_HP) && (rtc->periodic_freq & PF_COUNT)) + rtc->periodic_freq &= ~PF_COUNT; + else { + if (rtc->periodic_freq & PF_HP) + rtc->periodic_freq |= PF_COUNT; + if (rtc->periodic_freq & PF_KOU) { + spin_lock(&rtc_dev->irq_task_lock); + if (rtc_dev->irq_task) + rtc_dev->irq_task->func(rtc_dev->irq_task->private_data); + spin_unlock(&rtc_dev->irq_task_lock); + } else + rtc_update_irq(rtc->rtc_dev, 1, RTC_PF | RTC_IRQF); + } spin_unlock(&rtc->lock); @@ -176,8 +176,8 @@ static inline void sh_rtc_setpie(struct device *dev, unsigned int enable) tmp = readb(rtc->regbase + RCR2); if (enable) { - tmp &= ~RCR2_PESMASK; - tmp |= RCR2_PEF | (2 << 4); + tmp &= ~RCR2_PEF; /* Clear PES bit */ + tmp |= (rtc->periodic_freq & ~PF_HP); /* Set PES2-0 */ } else tmp &= ~(RCR2_PESMASK | RCR2_PEF); @@ -186,82 +186,81 @@ static inline void sh_rtc_setpie(struct device *dev, unsigned int enable) spin_unlock_irq(&rtc->lock); } -static inline void sh_rtc_setaie(struct device *dev, unsigned int enable) +static inline int sh_rtc_setfreq(struct device *dev, unsigned int freq) { struct sh_rtc *rtc = dev_get_drvdata(dev); - unsigned int tmp; + int tmp, ret = 0; spin_lock_irq(&rtc->lock); + tmp = rtc->periodic_freq & PF_MASK; - tmp = readb(rtc->regbase + RCR1); - - if (!enable) { - tmp &= ~RCR1_AIE; - rtc->rearm_aie = 0; - } else if (rtc->rearm_aie == 0) - tmp |= RCR1_AIE; + switch (freq) { + case 0: + rtc->periodic_freq = 0x00; + break; + case 1: + rtc->periodic_freq = 0x60; + break; + case 2: + rtc->periodic_freq = 0x50; + break; + case 4: + rtc->periodic_freq = 0x40; + break; + case 8: + rtc->periodic_freq = 0x30 | PF_HP; + break; + case 16: + rtc->periodic_freq = 0x30; + break; + case 32: + rtc->periodic_freq = 0x20 | PF_HP; + break; + case 64: + rtc->periodic_freq = 0x20; + break; + case 128: + rtc->periodic_freq = 0x10 | PF_HP; + break; + case 256: + rtc->periodic_freq = 0x10; + break; + default: + ret = -ENOTSUPP; + } - writeb(tmp, rtc->regbase + RCR1); + if (ret == 0) { + rtc->periodic_freq |= tmp; + rtc->rtc_dev->irq_freq = freq; + } spin_unlock_irq(&rtc->lock); + return ret; } -static int sh_rtc_open(struct device *dev) +static inline void sh_rtc_setaie(struct device *dev, unsigned int enable) { 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, IRQF_DISABLED, - "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, IRQF_DISABLED, - "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; - } + spin_lock_irq(&rtc->lock); - ret = request_irq(rtc->alarm_irq, sh_rtc_alarm, IRQF_DISABLED, - "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; - } + tmp = readb(rtc->regbase + RCR1); - return 0; + if (!enable) + tmp &= ~RCR1_AIE; + else + tmp |= RCR1_AIE; -err_bad_alarm: - free_irq(rtc->carry_irq, dev); -err_bad_carry: - free_irq(rtc->periodic_irq, dev); + writeb(tmp, rtc->regbase + RCR1); - return ret; + spin_unlock_irq(&rtc->lock); } static void sh_rtc_release(struct device *dev) { - struct sh_rtc *rtc = dev_get_drvdata(dev); - sh_rtc_setpie(dev, 0); sh_rtc_setaie(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) @@ -270,31 +269,44 @@ static int sh_rtc_proc(struct device *dev, struct seq_file *seq) unsigned int tmp; tmp = readb(rtc->regbase + RCR1); - seq_printf(seq, "carry_IRQ\t: %s\n", - (tmp & RCR1_CIE) ? "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"); + (tmp & RCR2_PESMASK) ? "yes" : "no"); return 0; } static int sh_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) { - unsigned int ret = -ENOIOCTLCMD; + struct sh_rtc *rtc = dev_get_drvdata(dev); + unsigned int ret = 0; 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; + case RTC_UIE_OFF: + rtc->periodic_freq &= ~PF_OXS; + break; + case RTC_UIE_ON: + rtc->periodic_freq |= PF_OXS; + break; + case RTC_IRQP_READ: + ret = put_user(rtc->rtc_dev->irq_freq, + (unsigned long __user *)arg); + break; + case RTC_IRQP_SET: + ret = sh_rtc_setfreq(dev, arg); + break; + default: + ret = -ENOIOCTLCMD; } return ret; @@ -421,7 +433,7 @@ static int sh_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) { struct platform_device *pdev = to_platform_device(dev); struct sh_rtc *rtc = platform_get_drvdata(pdev); - struct rtc_time* tm = &wkalrm->time; + struct rtc_time *tm = &wkalrm->time; spin_lock_irq(&rtc->lock); @@ -452,7 +464,7 @@ static inline void sh_rtc_write_alarm_value(struct sh_rtc *rtc, writeb(BIN2BCD(value) | AR_ENB, rtc->regbase + reg_off); } -static int sh_rtc_check_alarm(struct rtc_time* tm) +static int sh_rtc_check_alarm(struct rtc_time *tm) { /* * The original rtc says anything > 0xc0 is "don't care" or "match @@ -503,11 +515,9 @@ static int sh_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) /* disable alarm interrupt and clear the alarm flag */ rcr1 = readb(rtc->regbase + RCR1); - rcr1 &= ~(RCR1_AF|RCR1_AIE); + rcr1 &= ~(RCR1_AF | RCR1_AIE); writeb(rcr1, rtc->regbase + RCR1); - rtc->rearm_aie = 0; - /* set alarm time */ sh_rtc_write_alarm_value(rtc, tm->tm_sec, RSECAR); sh_rtc_write_alarm_value(rtc, tm->tm_min, RMINAR); @@ -529,14 +539,34 @@ static int sh_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) return 0; } +static int sh_rtc_irq_set_state(struct device *dev, int enabled) +{ + struct platform_device *pdev = to_platform_device(dev); + struct sh_rtc *rtc = platform_get_drvdata(pdev); + + if (enabled) { + rtc->periodic_freq |= PF_KOU; + return sh_rtc_ioctl(dev, RTC_PIE_ON, 0); + } else { + rtc->periodic_freq &= ~PF_KOU; + return sh_rtc_ioctl(dev, RTC_PIE_OFF, 0); + } +} + +static int sh_rtc_irq_set_freq(struct device *dev, int freq) +{ + return sh_rtc_ioctl(dev, RTC_IRQP_SET, freq); +} + 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, .read_alarm = sh_rtc_read_alarm, .set_alarm = sh_rtc_set_alarm, + .irq_set_state = sh_rtc_irq_set_state, + .irq_set_freq = sh_rtc_irq_set_freq, .proc = sh_rtc_proc, }; @@ -544,6 +574,7 @@ static int __devinit sh_rtc_probe(struct platform_device *pdev) { struct sh_rtc *rtc; struct resource *res; + unsigned int tmp; int ret = -ENOENT; rtc = kzalloc(sizeof(struct sh_rtc), GFP_KERNEL); @@ -552,6 +583,7 @@ static int __devinit sh_rtc_probe(struct platform_device *pdev) spin_lock_init(&rtc->lock); + /* get periodic/carry/alarm irqs */ rtc->periodic_irq = platform_get_irq(pdev, 0); if (unlikely(rtc->periodic_irq < 0)) { dev_err(&pdev->dev, "No IRQ for period\n"); @@ -608,8 +640,48 @@ static int __devinit sh_rtc_probe(struct platform_device *pdev) rtc->capabilities |= pinfo->capabilities; } + rtc->rtc_dev->max_user_freq = 256; + rtc->rtc_dev->irq_freq = 1; + rtc->periodic_freq = 0x60; + platform_set_drvdata(pdev, rtc); + /* register periodic/carry/alarm irqs */ + ret = request_irq(rtc->periodic_irq, sh_rtc_periodic, IRQF_DISABLED, + "sh-rtc period", rtc); + if (unlikely(ret)) { + dev_err(&pdev->dev, + "request period IRQ failed with %d, IRQ %d\n", ret, + rtc->periodic_irq); + goto err_badmap; + } + + ret = request_irq(rtc->carry_irq, sh_rtc_interrupt, IRQF_DISABLED, + "sh-rtc carry", rtc); + if (unlikely(ret)) { + dev_err(&pdev->dev, + "request carry IRQ failed with %d, IRQ %d\n", ret, + rtc->carry_irq); + free_irq(rtc->periodic_irq, rtc); + goto err_badmap; + } + + ret = request_irq(rtc->alarm_irq, sh_rtc_alarm, IRQF_DISABLED, + "sh-rtc alarm", rtc); + if (unlikely(ret)) { + dev_err(&pdev->dev, + "request alarm IRQ failed with %d, IRQ %d\n", ret, + rtc->alarm_irq); + free_irq(rtc->carry_irq, rtc); + free_irq(rtc->periodic_irq, rtc); + goto err_badmap; + } + + tmp = readb(rtc->regbase + RCR1); + tmp &= ~RCR1_CF; + tmp |= RCR1_CIE; + writeb(tmp, rtc->regbase + RCR1); + return 0; err_badmap: @@ -630,6 +702,10 @@ static int __devexit sh_rtc_remove(struct platform_device *pdev) sh_rtc_setpie(&pdev->dev, 0); sh_rtc_setaie(&pdev->dev, 0); + free_irq(rtc->carry_irq, rtc); + free_irq(rtc->periodic_irq, rtc); + free_irq(rtc->alarm_irq, rtc); + release_resource(rtc->res); platform_set_drvdata(pdev, NULL); @@ -662,6 +738,8 @@ module_exit(sh_rtc_exit); MODULE_DESCRIPTION("SuperH on-chip RTC driver"); MODULE_VERSION(DRV_VERSION); -MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>, Jamie Lenehan <lenehan@twibble.org>"); +MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>, " + "Jamie Lenehan <lenehan@twibble.org>, " + "Angelo Castello <angelo.castello@st.com>"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c index eff593080d4f..c2ea5d4df44a 100644 --- a/drivers/serial/sh-sci.c +++ b/drivers/serial/sh-sci.c @@ -333,7 +333,6 @@ static void sci_init_pins_scif(struct uart_port *port, unsigned int cflag) } sci_out(port, SCFCR, fcr_val); } - #elif defined(CONFIG_CPU_SH3) /* For SH7705, SH7706, SH7707, SH7709, SH7709A, SH7729 */ static void sci_init_pins_scif(struct uart_port *port, unsigned int cflag) @@ -384,6 +383,12 @@ static void sci_init_pins_scif(struct uart_port *port, unsigned int cflag) sci_out(port, SCFCR, fcr_val); } +#elif defined(CONFIG_CPU_SUBTYPE_SH7723) +static void sci_init_pins_scif(struct uart_port *port, unsigned int cflag) +{ + /* Nothing to do here.. */ + sci_out(port, SCFCR, 0); +} #else /* For SH7750 */ static void sci_init_pins_scif(struct uart_port *port, unsigned int cflag) diff --git a/drivers/serial/sh-sci.h b/drivers/serial/sh-sci.h index 01a9dd715f5d..fa8700a968fc 100644 --- a/drivers/serial/sh-sci.h +++ b/drivers/serial/sh-sci.h @@ -1,20 +1,5 @@ -/* $Id: sh-sci.h,v 1.4 2004/02/19 16:43:56 lethal Exp $ - * - * linux/drivers/serial/sh-sci.h - * - * SuperH on-chip serial module support. (SCI with no FIFO / with FIFO) - * Copyright (C) 1999, 2000 Niibe Yutaka - * Copyright (C) 2000 Greg Banks - * Copyright (C) 2002, 2003 Paul Mundt - * Modified to support multiple serial ports. Stuart Menefy (May 2000). - * Modified to support SH7300(SH-Mobile) SCIF. Takashi Kusuda (Jun 2003). - * Modified to support H8/300 Series Yoshinori Sato (Feb 2004). - * Removed SH7300 support (Jul 2007). - * Modified to support SH7720 SCIF. Markus Brunner, Mark Jonas (Aug 2007). - */ #include <linux/serial_core.h> #include <asm/io.h> - #include <asm/gpio.h> #if defined(CONFIG_H83007) || defined(CONFIG_H83068) @@ -102,6 +87,15 @@ # define SCSPTR0 SCPDR0 # define SCIF_ORER 0x0001 /* overrun error bit */ # define SCSCR_INIT(port) 0x0038 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ +#elif defined(CONFIG_CPU_SUBTYPE_SH7723) +# define SCSPTR0 0xa4050160 +# define SCSPTR1 0xa405013e +# define SCSPTR2 0xa4050160 +# define SCSPTR3 0xa405013e +# define SCSPTR4 0xa4050128 +# define SCSPTR5 0xa4050128 +# define SCIF_ORER 0x0001 /* overrun error bit */ +# define SCSCR_INIT(port) 0x0038 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ # define SCIF_ONLY #elif defined(CONFIG_CPU_SUBTYPE_SH4_202) # define SCSPTR2 0xffe80020 /* 16 bit SCIF */ @@ -395,6 +389,11 @@ h8_sci_offset, h8_sci_size) \ CPU_SCI_FNS(name, h8_sci_offset, h8_sci_size) #define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) +#elif defined(CONFIG_CPU_SUBTYPE_SH7723) + #define SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size, sh4_scif_offset, sh4_scif_size) \ + CPU_SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size, sh4_scif_offset, sh4_scif_size) + #define SCIF_FNS(name, sh4_scif_offset, sh4_scif_size) \ + CPU_SCIF_FNS(name, sh4_scif_offset, sh4_scif_size) #else #define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh4_sci_offset, sh4_sci_size, \ sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size, \ @@ -419,6 +418,18 @@ SCIF_FNS(SCFDR, 0x1c, 16) SCIF_FNS(SCxTDR, 0x20, 8) SCIF_FNS(SCxRDR, 0x24, 8) SCIF_FNS(SCLSR, 0x24, 16) +#elif defined(CONFIG_CPU_SUBTYPE_SH7723) +SCIx_FNS(SCSMR, 0x00, 16, 0x00, 16) +SCIx_FNS(SCBRR, 0x04, 8, 0x04, 8) +SCIx_FNS(SCSCR, 0x08, 16, 0x08, 16) +SCIx_FNS(SCxTDR, 0x20, 8, 0x0c, 8) +SCIx_FNS(SCxSR, 0x14, 16, 0x10, 16) +SCIx_FNS(SCxRDR, 0x24, 8, 0x14, 8) +SCIF_FNS(SCTDSR, 0x0c, 8) +SCIF_FNS(SCFER, 0x10, 16) +SCIF_FNS(SCFCR, 0x18, 16) +SCIF_FNS(SCFDR, 0x1c, 16) +SCIF_FNS(SCLSR, 0x24, 16) #else /* reg SCI/SH3 SCI/SH4 SCIF/SH3 SCIF/SH4 SCI/H8*/ /* name off sz off sz off sz off sz off sz*/ @@ -589,6 +600,23 @@ static inline int sci_rxd_in(struct uart_port *port) return ctrl_inb(SCPDR0) & 0x0001 ? 1 : 0; /* SCIF0 */ return 1; } +#elif defined(CONFIG_CPU_SUBTYPE_SH7723) +static inline int sci_rxd_in(struct uart_port *port) +{ + if (port->mapbase == 0xffe00000) + return ctrl_inb(SCSPTR0) & 0x0008 ? 1 : 0; /* SCIF0 */ + if (port->mapbase == 0xffe10000) + return ctrl_inb(SCSPTR1) & 0x0020 ? 1 : 0; /* SCIF1 */ + if (port->mapbase == 0xffe20000) + return ctrl_inb(SCSPTR2) & 0x0001 ? 1 : 0; /* SCIF2 */ + if (port->mapbase == 0xa4e30000) + return ctrl_inb(SCSPTR3) & 0x0001 ? 1 : 0; /* SCIF3 */ + if (port->mapbase == 0xa4e40000) + return ctrl_inb(SCSPTR4) & 0x0001 ? 1 : 0; /* SCIF4 */ + if (port->mapbase == 0xa4e50000) + return ctrl_inb(SCSPTR5) & 0x0008 ? 1 : 0; /* SCIF5 */ + return 1; +} #elif defined(CONFIG_CPU_SUBTYPE_SH5_101) || defined(CONFIG_CPU_SUBTYPE_SH5_103) static inline int sci_rxd_in(struct uart_port *port) { @@ -727,6 +755,8 @@ static inline int sci_rxd_in(struct uart_port *port) defined(CONFIG_CPU_SUBTYPE_SH7720) || \ defined(CONFIG_CPU_SUBTYPE_SH7721) #define SCBRR_VALUE(bps, clk) (((clk*2)+16*bps)/(32*bps)-1) +#elif defined(CONFIG_CPU_SUBTYPE_SH7723) +#define SCBRR_VALUE(bps, clk) (((clk*2)+16*bps)/(16*bps)-1) #elif defined(__H8300H__) || defined(__H8300S__) #define SCBRR_VALUE(bps) (((CONFIG_CPU_CLOCK*1000/32)/bps)-1) #elif defined(CONFIG_SUPERH64) |