diff options
Diffstat (limited to 'drivers')
55 files changed, 3092 insertions, 373 deletions
diff --git a/drivers/char/applicom.c b/drivers/char/applicom.c index e7ba774beda..25373df1dcf 100644 --- a/drivers/char/applicom.c +++ b/drivers/char/applicom.c @@ -566,6 +566,7 @@ static ssize_t ac_read (struct file *filp, char __user *buf, size_t count, loff_ struct mailbox mailbox; /* Got a packet for us */ + memset(&st_loc, 0, sizeof(st_loc)); ret = do_ac_read(i, buf, &st_loc, &mailbox); spin_unlock_irqrestore(&apbs[i].mutex, flags); set_current_state(TASK_RUNNING); diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c index 3afd62e856e..e9cba13ee80 100644 --- a/drivers/char/hvc_console.c +++ b/drivers/char/hvc_console.c @@ -713,7 +713,6 @@ static int khvcd(void *unused) struct hvc_struct *hp; set_freezable(); - __set_current_state(TASK_RUNNING); do { poll_mask = 0; hvc_kicked = 0; diff --git a/drivers/char/ip2/Makefile b/drivers/char/ip2/Makefile index bc397d92b49..7b78e0dfc5b 100644 --- a/drivers/char/ip2/Makefile +++ b/drivers/char/ip2/Makefile @@ -4,5 +4,5 @@ obj-$(CONFIG_COMPUTONE) += ip2.o -ip2-objs := ip2main.o +ip2-y := ip2main.o diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile index eb8a1a8c188..16a93648d54 100644 --- a/drivers/char/ipmi/Makefile +++ b/drivers/char/ipmi/Makefile @@ -2,7 +2,7 @@ # Makefile for the ipmi drivers. # -ipmi_si-objs := ipmi_si_intf.o ipmi_kcs_sm.o ipmi_smic_sm.o ipmi_bt_sm.o +ipmi_si-y := ipmi_si_intf.o ipmi_kcs_sm.o ipmi_smic_sm.o ipmi_bt_sm.o obj-$(CONFIG_IPMI_HANDLER) += ipmi_msghandler.o obj-$(CONFIG_IPMI_DEVICE_INTERFACE) += ipmi_devintf.o diff --git a/drivers/char/ipmi/ipmi_devintf.c b/drivers/char/ipmi/ipmi_devintf.c index 1fc8876af1f..2aa3977aae5 100644 --- a/drivers/char/ipmi/ipmi_devintf.c +++ b/drivers/char/ipmi/ipmi_devintf.c @@ -916,7 +916,7 @@ static struct ipmi_smi_watcher smi_watcher = .smi_gone = ipmi_smi_gone, }; -static __init int init_ipmi_devintf(void) +static int __init init_ipmi_devintf(void) { int rv; @@ -954,7 +954,7 @@ static __init int init_ipmi_devintf(void) } module_init(init_ipmi_devintf); -static __exit void cleanup_ipmi(void) +static void __exit cleanup_ipmi(void) { struct ipmi_reg_list *entry, *entry2; mutex_lock(®_list_mutex); diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 4f3f8c9ec26..2fe72f8edf4 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -4442,13 +4442,13 @@ static int ipmi_init_msghandler(void) return 0; } -static __init int ipmi_init_msghandler_mod(void) +static int __init ipmi_init_msghandler_mod(void) { ipmi_init_msghandler(); return 0; } -static __exit void cleanup_ipmi(void) +static void __exit cleanup_ipmi(void) { int count; diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index b293d57d30a..035da9e64a1 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -1846,7 +1846,7 @@ static int hotmod_handler(const char *val, struct kernel_param *kp) return rv; } -static __devinit void hardcode_find_bmc(void) +static void __devinit hardcode_find_bmc(void) { int i; struct smi_info *info; @@ -2029,7 +2029,7 @@ struct SPMITable { s8 spmi_id[1]; /* A '\0' terminated array starts here. */ }; -static __devinit int try_init_spmi(struct SPMITable *spmi) +static int __devinit try_init_spmi(struct SPMITable *spmi) { struct smi_info *info; @@ -2112,7 +2112,7 @@ static __devinit int try_init_spmi(struct SPMITable *spmi) return 0; } -static __devinit void spmi_find_bmc(void) +static void __devinit spmi_find_bmc(void) { acpi_status status; struct SPMITable *spmi; @@ -2325,7 +2325,7 @@ static int __devinit decode_dmi(const struct dmi_header *dm, return 0; } -static __devinit void try_init_dmi(struct dmi_ipmi_data *ipmi_data) +static void __devinit try_init_dmi(struct dmi_ipmi_data *ipmi_data) { struct smi_info *info; @@ -3012,7 +3012,7 @@ static __devinitdata struct ipmi_default_vals { .port = 0 } }; -static __devinit void default_find_bmc(void) +static void __devinit default_find_bmc(void) { struct smi_info *info; int i; @@ -3312,7 +3312,7 @@ static int try_smi_init(struct smi_info *new_smi) return rv; } -static __devinit int init_ipmi_si(void) +static int __devinit init_ipmi_si(void) { int i; char *str; @@ -3525,7 +3525,7 @@ static void cleanup_one_si(struct smi_info *to_clean) kfree(to_clean); } -static __exit void cleanup_ipmi_si(void) +static void __exit cleanup_ipmi_si(void) { struct smi_info *e, *tmp_e; diff --git a/drivers/char/mmtimer.c b/drivers/char/mmtimer.c index c070b53984e..e6d75627c6c 100644 --- a/drivers/char/mmtimer.c +++ b/drivers/char/mmtimer.c @@ -176,9 +176,9 @@ static void mmtimer_setup_int_2(int cpu, u64 expires) * in order to insure that the setup succeeds in a deterministic time frame. * It will check if the interrupt setup succeeded. */ -static int mmtimer_setup(int cpu, int comparator, unsigned long expires) +static int mmtimer_setup(int cpu, int comparator, unsigned long expires, + u64 *set_completion_time) { - switch (comparator) { case 0: mmtimer_setup_int_0(cpu, expires); @@ -191,7 +191,8 @@ static int mmtimer_setup(int cpu, int comparator, unsigned long expires) break; } /* We might've missed our expiration time */ - if (rtc_time() <= expires) + *set_completion_time = rtc_time(); + if (*set_completion_time <= expires) return 1; /* @@ -227,6 +228,8 @@ static int mmtimer_disable_int(long nasid, int comparator) #define TIMER_OFF 0xbadcabLL /* Timer is not setup */ #define TIMER_SET 0 /* Comparator is set for this timer */ +#define MMTIMER_INTERVAL_RETRY_INCREMENT_DEFAULT 40 + /* There is one of these for each timer */ struct mmtimer { struct rb_node list; @@ -242,6 +245,11 @@ struct mmtimer_node { }; static struct mmtimer_node *timers; +static unsigned mmtimer_interval_retry_increment = + MMTIMER_INTERVAL_RETRY_INCREMENT_DEFAULT; +module_param(mmtimer_interval_retry_increment, uint, 0644); +MODULE_PARM_DESC(mmtimer_interval_retry_increment, + "RTC ticks to add to expiration on interval retry (default 40)"); /* * Add a new mmtimer struct to the node's mmtimer list. @@ -289,7 +297,8 @@ static void mmtimer_set_next_timer(int nodeid) struct mmtimer_node *n = &timers[nodeid]; struct mmtimer *x; struct k_itimer *t; - int o; + u64 expires, exp, set_completion_time; + int i; restart: if (n->next == NULL) @@ -300,7 +309,8 @@ restart: if (!t->it.mmtimer.incr) { /* Not an interval timer */ if (!mmtimer_setup(x->cpu, COMPARATOR, - t->it.mmtimer.expires)) { + t->it.mmtimer.expires, + &set_completion_time)) { /* Late setup, fire now */ tasklet_schedule(&n->tasklet); } @@ -308,14 +318,23 @@ restart: } /* Interval timer */ - o = 0; - while (!mmtimer_setup(x->cpu, COMPARATOR, t->it.mmtimer.expires)) { - unsigned long e, e1; - struct rb_node *next; - t->it.mmtimer.expires += t->it.mmtimer.incr << o; - t->it_overrun += 1 << o; - o++; - if (o > 20) { + i = 0; + expires = exp = t->it.mmtimer.expires; + while (!mmtimer_setup(x->cpu, COMPARATOR, expires, + &set_completion_time)) { + int to; + + i++; + expires = set_completion_time + + mmtimer_interval_retry_increment + (1 << i); + /* Calculate overruns as we go. */ + to = ((u64)(expires - exp) / t->it.mmtimer.incr); + if (to) { + t->it_overrun += to; + t->it.mmtimer.expires += t->it.mmtimer.incr * to; + exp = t->it.mmtimer.expires; + } + if (i > 20) { printk(KERN_ALERT "mmtimer: cannot reschedule timer\n"); t->it.mmtimer.clock = TIMER_OFF; n->next = rb_next(&x->list); @@ -323,21 +342,6 @@ restart: kfree(x); goto restart; } - - e = t->it.mmtimer.expires; - next = rb_next(&x->list); - - if (next == NULL) - continue; - - e1 = rb_entry(next, struct mmtimer, list)-> - timer->it.mmtimer.expires; - if (e > e1) { - n->next = next; - rb_erase(&x->list, &n->timer_head); - mmtimer_add_list(x); - goto restart; - } } } diff --git a/drivers/char/mwave/Makefile b/drivers/char/mwave/Makefile index 754c9e2058e..26b4fce217b 100644 --- a/drivers/char/mwave/Makefile +++ b/drivers/char/mwave/Makefile @@ -6,10 +6,10 @@ obj-$(CONFIG_MWAVE) += mwave.o -mwave-objs := mwavedd.o smapi.o tp3780i.o 3780i.o +mwave-y := mwavedd.o smapi.o tp3780i.o 3780i.o # To have the mwave driver disable other uarts if necessary # EXTRA_CFLAGS += -DMWAVE_FUTZ_WITH_OTHER_DEVICES # To compile in lots (~20 KiB) of run-time enablable printk()s for debugging: -EXTRA_CFLAGS += -DMW_TRACE +ccflags-y := -DMW_TRACE diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c index 463df27494b..dd9d75351cd 100644 --- a/drivers/char/mxser.c +++ b/drivers/char/mxser.c @@ -303,6 +303,7 @@ static void mxser_enable_must_enchance_mode(unsigned long baseio) outb(oldlcr, baseio + UART_LCR); } +#ifdef CONFIG_PCI static void mxser_disable_must_enchance_mode(unsigned long baseio) { u8 oldlcr; @@ -317,6 +318,7 @@ static void mxser_disable_must_enchance_mode(unsigned long baseio) outb(efr, baseio + MOXA_MUST_EFR_REGISTER); outb(oldlcr, baseio + UART_LCR); } +#endif static void mxser_set_must_xon1_value(unsigned long baseio, u8 value) { @@ -388,6 +390,7 @@ static void mxser_set_must_enum_value(unsigned long baseio, u8 value) outb(oldlcr, baseio + UART_LCR); } +#ifdef CONFIG_PCI static void mxser_get_must_hardware_id(unsigned long baseio, u8 *pId) { u8 oldlcr; @@ -404,6 +407,7 @@ static void mxser_get_must_hardware_id(unsigned long baseio, u8 *pId) *pId = inb(baseio + MOXA_MUST_HWID_REGISTER); outb(oldlcr, baseio + UART_LCR); } +#endif static void SET_MOXA_MUST_NO_SOFTWARE_FLOW_CONTROL(unsigned long baseio) { diff --git a/drivers/char/pcmcia/ipwireless/Makefile b/drivers/char/pcmcia/ipwireless/Makefile index b71eb593643..db80873d7f2 100644 --- a/drivers/char/pcmcia/ipwireless/Makefile +++ b/drivers/char/pcmcia/ipwireless/Makefile @@ -6,5 +6,5 @@ obj-$(CONFIG_IPWIRELESS) += ipwireless.o -ipwireless-objs := hardware.o main.o network.o tty.o +ipwireless-y := hardware.o main.o network.o tty.o diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index 723152d978a..f176dbaeb15 100644 --- a/drivers/char/ppdev.c +++ b/drivers/char/ppdev.c @@ -613,6 +613,7 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case PPGETTIME: to_jiffies = pp->pdev->timeout; + memset(&par_timeout, 0, sizeof(par_timeout)); par_timeout.tv_sec = to_jiffies / HZ; par_timeout.tv_usec = (to_jiffies % (long)HZ) * (1000000/HZ); if (copy_to_user (argp, &par_timeout, sizeof(struct timeval))) diff --git a/drivers/char/ramoops.c b/drivers/char/ramoops.c index 74f00b5ffa3..73dcb0ee41f 100644 --- a/drivers/char/ramoops.c +++ b/drivers/char/ramoops.c @@ -25,6 +25,8 @@ #include <linux/time.h> #include <linux/io.h> #include <linux/ioport.h> +#include <linux/platform_device.h> +#include <linux/ramoops.h> #define RAMOOPS_KERNMSG_HDR "====" #define RAMOOPS_HEADER_SIZE (5 + sizeof(struct timeval)) @@ -91,11 +93,17 @@ static void ramoops_do_dump(struct kmsg_dumper *dumper, cxt->count = (cxt->count + 1) % cxt->max_count; } -static int __init ramoops_init(void) +static int __init ramoops_probe(struct platform_device *pdev) { + struct ramoops_platform_data *pdata = pdev->dev.platform_data; struct ramoops_context *cxt = &oops_cxt; int err = -EINVAL; + if (pdata) { + mem_size = pdata->mem_size; + mem_address = pdata->mem_address; + } + if (!mem_size) { printk(KERN_ERR "ramoops: invalid size specification"); goto fail3; @@ -142,7 +150,7 @@ fail3: return err; } -static void __exit ramoops_exit(void) +static int __exit ramoops_remove(struct platform_device *pdev) { struct ramoops_context *cxt = &oops_cxt; @@ -151,8 +159,26 @@ static void __exit ramoops_exit(void) iounmap(cxt->virt_addr); release_mem_region(cxt->phys_addr, cxt->size); + return 0; } +static struct platform_driver ramoops_driver = { + .remove = __exit_p(ramoops_remove), + .driver = { + .name = "ramoops", + .owner = THIS_MODULE, + }, +}; + +static int __init ramoops_init(void) +{ + return platform_driver_probe(&ramoops_driver, ramoops_probe); +} + +static void __exit ramoops_exit(void) +{ + platform_driver_unregister(&ramoops_driver); +} module_init(ramoops_init); module_exit(ramoops_exit); diff --git a/drivers/char/rio/Makefile b/drivers/char/rio/Makefile index 2d1c5a7cba7..1661875883f 100644 --- a/drivers/char/rio/Makefile +++ b/drivers/char/rio/Makefile @@ -8,5 +8,5 @@ obj-$(CONFIG_RIO) += rio.o -rio-objs := rio_linux.o rioinit.o rioboot.o riocmd.o rioctrl.o riointr.o \ +rio-y := rio_linux.o rioinit.o rioboot.o riocmd.o rioctrl.o riointr.o \ rioparam.o rioroute.o riotable.o riotty.o diff --git a/drivers/char/rocket.c b/drivers/char/rocket.c index 7c79d243acc..86308830ac4 100644 --- a/drivers/char/rocket.c +++ b/drivers/char/rocket.c @@ -2345,7 +2345,7 @@ static int __init rp_init(void) ret = tty_register_driver(rocket_driver); if (ret < 0) { printk(KERN_ERR "Couldn't install tty RocketPort driver\n"); - goto err_tty; + goto err_controller; } #ifdef ROCKET_DEBUG_OPEN @@ -2380,6 +2380,9 @@ static int __init rp_init(void) return 0; err_ttyu: tty_unregister_driver(rocket_driver); +err_controller: + if (controller) + release_region(controller, 4); err_tty: put_tty_driver(rocket_driver); err: diff --git a/drivers/char/synclink_gt.c b/drivers/char/synclink_gt.c index 1746d91205f..d01fffeac95 100644 --- a/drivers/char/synclink_gt.c +++ b/drivers/char/synclink_gt.c @@ -301,6 +301,8 @@ struct slgt_info { unsigned int rx_pio; unsigned int if_mode; unsigned int base_clock; + unsigned int xsync; + unsigned int xctrl; /* device status */ @@ -405,6 +407,8 @@ static MGSL_PARAMS default_params = { #define TDCSR 0x94 /* tx DMA control/status */ #define RDDAR 0x98 /* rx DMA descriptor address */ #define TDDAR 0x9c /* tx DMA descriptor address */ +#define XSR 0x40 /* extended sync pattern */ +#define XCR 0x44 /* extended control */ #define RXIDLE BIT14 #define RXBREAK BIT14 @@ -517,6 +521,10 @@ static int set_interface(struct slgt_info *info, int if_mode); static int set_gpio(struct slgt_info *info, struct gpio_desc __user *gpio); static int get_gpio(struct slgt_info *info, struct gpio_desc __user *gpio); static int wait_gpio(struct slgt_info *info, struct gpio_desc __user *gpio); +static int get_xsync(struct slgt_info *info, int __user *if_mode); +static int set_xsync(struct slgt_info *info, int if_mode); +static int get_xctrl(struct slgt_info *info, int __user *if_mode); +static int set_xctrl(struct slgt_info *info, int if_mode); /* * driver functions @@ -1056,6 +1064,14 @@ static int ioctl(struct tty_struct *tty, struct file *file, return get_gpio(info, argp); case MGSL_IOCWAITGPIO: return wait_gpio(info, argp); + case MGSL_IOCGXSYNC: + return get_xsync(info, argp); + case MGSL_IOCSXSYNC: + return set_xsync(info, (int)arg); + case MGSL_IOCGXCTRL: + return get_xctrl(info, argp); + case MGSL_IOCSXCTRL: + return set_xctrl(info, (int)arg); } mutex_lock(&info->port.mutex); switch (cmd) { @@ -1132,6 +1148,7 @@ static long get_params32(struct slgt_info *info, struct MGSL_PARAMS32 __user *us struct MGSL_PARAMS32 tmp_params; DBGINFO(("%s get_params32\n", info->device_name)); + memset(&tmp_params, 0, sizeof(tmp_params)); tmp_params.mode = (compat_ulong_t)info->params.mode; tmp_params.loopback = info->params.loopback; tmp_params.flags = info->params.flags; @@ -1212,12 +1229,16 @@ static long slgt_compat_ioctl(struct tty_struct *tty, struct file *file, case MGSL_IOCSGPIO: case MGSL_IOCGGPIO: case MGSL_IOCWAITGPIO: + case MGSL_IOCGXSYNC: + case MGSL_IOCGXCTRL: case MGSL_IOCSTXIDLE: case MGSL_IOCTXENABLE: case MGSL_IOCRXENABLE: case MGSL_IOCTXABORT: case TIOCMIWAIT: case MGSL_IOCSIF: + case MGSL_IOCSXSYNC: + case MGSL_IOCSXCTRL: rc = ioctl(tty, file, cmd, arg); break; } @@ -1617,6 +1638,8 @@ static int hdlcdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) if (cmd != SIOCWANDEV) return hdlc_ioctl(dev, ifr, cmd); + memset(&new_line, 0, sizeof(new_line)); + switch(ifr->ifr_settings.type) { case IF_GET_IFACE: /* return current sync_serial_settings */ @@ -1958,6 +1981,7 @@ static void bh_handler(struct work_struct *work) case MGSL_MODE_RAW: case MGSL_MODE_MONOSYNC: case MGSL_MODE_BISYNC: + case MGSL_MODE_XSYNC: while(rx_get_buf(info)); break; } @@ -2357,26 +2381,27 @@ static irqreturn_t slgt_interrupt(int dummy, void *dev_id) DBGISR(("slgt_interrupt irq=%d entry\n", info->irq_level)); - spin_lock(&info->lock); - while((gsr = rd_reg32(info, GSR) & 0xffffff00)) { DBGISR(("%s gsr=%08x\n", info->device_name, gsr)); info->irq_occurred = true; for(i=0; i < info->port_count ; i++) { if (info->port_array[i] == NULL) continue; + spin_lock(&info->port_array[i]->lock); if (gsr & (BIT8 << i)) isr_serial(info->port_array[i]); if (gsr & (BIT16 << (i*2))) isr_rdma(info->port_array[i]); if (gsr & (BIT17 << (i*2))) isr_tdma(info->port_array[i]); + spin_unlock(&info->port_array[i]->lock); } } if (info->gpio_present) { unsigned int state; unsigned int changed; + spin_lock(&info->lock); while ((changed = rd_reg32(info, IOSR)) != 0) { DBGISR(("%s iosr=%08x\n", info->device_name, changed)); /* read latched state of GPIO signals */ @@ -2388,22 +2413,24 @@ static irqreturn_t slgt_interrupt(int dummy, void *dev_id) isr_gpio(info->port_array[i], changed, state); } } + spin_unlock(&info->lock); } for(i=0; i < info->port_count ; i++) { struct slgt_info *port = info->port_array[i]; - - if (port && (port->port.count || port->netcount) && + if (port == NULL) + continue; + spin_lock(&port->lock); + if ((port->port.count || port->netcount) && port->pending_bh && !port->bh_running && !port->bh_requested) { DBGISR(("%s bh queued\n", port->device_name)); schedule_work(&port->task); port->bh_requested = true; } + spin_unlock(&port->lock); } - spin_unlock(&info->lock); - DBGISR(("slgt_interrupt irq=%d exit\n", info->irq_level)); return IRQ_HANDLED; } @@ -2883,6 +2910,69 @@ static int set_interface(struct slgt_info *info, int if_mode) return 0; } +static int get_xsync(struct slgt_info *info, int __user *xsync) +{ + DBGINFO(("%s get_xsync=%x\n", info->device_name, info->xsync)); + if (put_user(info->xsync, xsync)) + return -EFAULT; + return 0; +} + +/* + * set extended sync pattern (1 to 4 bytes) for extended sync mode + * + * sync pattern is contained in least significant bytes of value + * most significant byte of sync pattern is oldest (1st sent/detected) + */ +static int set_xsync(struct slgt_info *info, int xsync) +{ + unsigned long flags; + + DBGINFO(("%s set_xsync=%x)\n", info->device_name, xsync)); + spin_lock_irqsave(&info->lock, flags); + info->xsync = xsync; + wr_reg32(info, XSR, xsync); + spin_unlock_irqrestore(&info->lock, flags); + return 0; +} + +static int get_xctrl(struct slgt_info *info, int __user *xctrl) +{ + DBGINFO(("%s get_xctrl=%x\n", info->device_name, info->xctrl)); + if (put_user(info->xctrl, xctrl)) + return -EFAULT; + return 0; +} + +/* + * set extended control options + * + * xctrl[31:19] reserved, must be zero + * xctrl[18:17] extended sync pattern length in bytes + * 00 = 1 byte in xsr[7:0] + * 01 = 2 bytes in xsr[15:0] + * 10 = 3 bytes in xsr[23:0] + * 11 = 4 bytes in xsr[31:0] + * xctrl[16] 1 = enable terminal count, 0=disabled + * xctrl[15:0] receive terminal count for fixed length packets + * value is count minus one (0 = 1 byte packet) + * when terminal count is reached, receiver + * automatically returns to hunt mode and receive + * FIFO contents are flushed to DMA buffers with + * end of frame (EOF) status + */ +static int set_xctrl(struct slgt_info *info, int xctrl) +{ + unsigned long flags; + + DBGINFO(("%s set_xctrl=%x)\n", info->device_name, xctrl)); + spin_lock_irqsave(&info->lock, flags); + info->xctrl = xctrl; + wr_reg32(info, XCR, xctrl); + spin_unlock_irqrestore(&info->lock, flags); + return 0; +} + /* * set general purpose IO pin state and direction * @@ -2906,7 +2996,7 @@ static int set_gpio(struct slgt_info *info, struct gpio_desc __user *user_gpio) info->device_name, gpio.state, gpio.smask, gpio.dir, gpio.dmask)); - spin_lock_irqsave(&info->lock,flags); + spin_lock_irqsave(&info->port_array[0]->lock, flags); if (gpio.dmask) { data = rd_reg32(info, IODR); data |= gpio.dmask & gpio.dir; @@ -2919,7 +3009,7 @@ static int set_gpio(struct slgt_info *info, struct gpio_desc __user *user_gpio) data &= ~(gpio.smask & ~gpio.state); wr_reg32(info, IOVR, data); } - spin_unlock_irqrestore(&info->lock,flags); + spin_unlock_irqrestore(&info->port_array[0]->lock, flags); return 0; } @@ -3020,7 +3110,7 @@ static int wait_gpio(struct slgt_info *info, struct gpio_desc __user *user_gpio) return -EINVAL; init_cond_wait(&wait, gpio.smask); - spin_lock_irqsave(&info->lock, flags); + spin_lock_irqsave(&info->port_array[0]->lock, flags); /* enable interrupts for watched pins */ wr_reg32(info, IOER, rd_reg32(info, IOER) | gpio.smask); /* get current pin states */ @@ -3032,20 +3122,20 @@ static int wait_gpio(struct slgt_info *info, struct gpio_desc __user *user_gpio) } else { /* wait for target state */ add_cond_wait(&info->gpio_wait_q, &wait); - spin_unlock_irqrestore(&info->lock, flags); + spin_unlock_irqrestore(&info->port_array[0]->lock, flags); schedule(); if (signal_pending(current)) rc = -ERESTARTSYS; else gpio.state = wait.data; - spin_lock_irqsave(&info->lock, flags); + spin_lock_irqsave(&info->port_array[0]->lock, flags); remove_cond_wait(&info->gpio_wait_q, &wait); } /* disable all GPIO interrupts if no waiting processes */ if (info->gpio_wait_q == NULL) wr_reg32(info, IOER, 0); - spin_unlock_irqrestore(&info->lock,flags); + spin_unlock_irqrestore(&info->port_array[0]->lock, flags); if ((rc == 0) && copy_to_user(user_gpio, &gpio, sizeof(gpio))) rc = -EFAULT; @@ -3578,7 +3668,6 @@ static void device_init(int adapter_num, struct pci_dev *pdev) /* copy resource information from first port to others */ for (i = 1; i < port_count; ++i) { - port_array[i]->lock = port_array[0]->lock; port_array[i]->irq_level = port_array[0]->irq_level; port_array[i]->reg_addr = port_array[0]->reg_addr; alloc_dma_bufs(port_array[i]); @@ -3763,7 +3852,9 @@ module_exit(slgt_exit); #define CALC_REGADDR() \ unsigned long reg_addr = ((unsigned long)info->reg_addr) + addr; \ if (addr >= 0x80) \ - reg_addr += (info->port_num) * 32; + reg_addr += (info->port_num) * 32; \ + else if (addr >= 0x40) \ + reg_addr += (info->port_num) * 16; static __u8 rd_reg8(struct slgt_info *info, unsigned int addr) { @@ -4182,7 +4273,13 @@ static void sync_mode(struct slgt_info *info) /* TCR (tx control) * - * 15..13 mode, 000=HDLC 001=raw 010=async 011=monosync 100=bisync + * 15..13 mode + * 000=HDLC/SDLC + * 001=raw bit synchronous + * 010=asynchronous/isochronous + * 011=monosync byte synchronous + * 100=bisync byte synchronous + * 101=xsync byte synchronous * 12..10 encoding * 09 CRC enable * 08 CRC32 @@ -4197,6 +4294,9 @@ static void sync_mode(struct slgt_info *info) val = BIT2; switch(info->params.mode) { + case MGSL_MODE_XSYNC: + val |= BIT15 + BIT13; + break; case MGSL_MODE_MONOSYNC: val |= BIT14 + BIT13; break; case MGSL_MODE_BISYNC: val |= BIT15; break; case MGSL_MODE_RAW: val |= BIT13; break; @@ -4251,7 +4351,13 @@ static void sync_mode(struct slgt_info *info) /* RCR (rx control) * - * 15..13 mode, 000=HDLC 001=raw 010=async 011=monosync 100=bisync + * 15..13 mode + * 000=HDLC/SDLC + * 001=raw bit synchronous + * 010=asynchronous/isochronous + * 011=monosync byte synchronous + * 100=bisync byte synchronous + * 101=xsync byte synchronous * 12..10 encoding * 09 CRC enable * 08 CRC32 @@ -4263,6 +4369,9 @@ static void sync_mode(struct slgt_info *info) val = 0; switch(info->params.mode) { + case MGSL_MODE_XSYNC: + val |= BIT15 + BIT13; + break; case MGSL_MODE_MONOSYNC: val |= BIT14 + BIT13; break; case MGSL_MODE_BISYNC: val |= BIT15; break; case MGSL_MODE_RAW: val |= BIT13; break; @@ -4679,6 +4788,7 @@ static bool rx_get_buf(struct slgt_info *info) switch(info->params.mode) { case MGSL_MODE_MONOSYNC: case MGSL_MODE_BISYNC: + case MGSL_MODE_XSYNC: /* ignore residue in byte synchronous modes */ if (desc_residue(info->rbufs[i])) count--; diff --git a/drivers/char/vt_ioctl.c b/drivers/char/vt_ioctl.c index 38df8c19e74..6b68a0fb461 100644 --- a/drivers/char/vt_ioctl.c +++ b/drivers/char/vt_ioctl.c @@ -503,6 +503,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, struct kbd_struct * kbd; unsigned int console; unsigned char ucval; + unsigned int uival; void __user *up = (void __user *)arg; int i, perm; int ret = 0; @@ -657,7 +658,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, break; case KDGETMODE: - ucval = vc->vc_mode; + uival = vc->vc_mode; goto setint; case KDMAPDISP: @@ -695,7 +696,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, break; case KDGKBMODE: - ucval = ((kbd->kbdmode == VC_RAW) ? K_RAW : + uival = ((kbd->kbdmode == VC_RAW) ? K_RAW : (kbd->kbdmode == VC_MEDIUMRAW) ? K_MEDIUMRAW : (kbd->kbdmode == VC_UNICODE) ? K_UNICODE : K_XLATE); @@ -717,9 +718,9 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, break; case KDGKBMETA: - ucval = (vc_kbd_mode(kbd, VC_META) ? K_ESCPREFIX : K_METABIT); + uival = (vc_kbd_mode(kbd, VC_META) ? K_ESCPREFIX : K_METABIT); setint: - ret = put_user(ucval, (int __user *)arg); + ret = put_user(uival, (int __user *)arg); break; case KDGETKEYCODE: @@ -949,7 +950,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, for (i = 0; i < MAX_NR_CONSOLES; ++i) if (! VT_IS_IN_USE(i)) break; - ucval = i < MAX_NR_CONSOLES ? (i+1) : -1; + uival = i < MAX_NR_CONSOLES ? (i+1) : -1; goto setint; /* diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c index b3d22d65999..e28e4166817 100644 --- a/drivers/firmware/dmi_scan.c +++ b/drivers/firmware/dmi_scan.c @@ -2,6 +2,7 @@ #include <linux/string.h> #include <linux/init.h> #include <linux/module.h> +#include <linux/ctype.h> #include <linux/dmi.h> #include <linux/efi.h> #include <linux/bootmem.h> @@ -361,6 +362,33 @@ static void __init dmi_decode(const struct dmi_header *dm, void *dummy) } } +static void __init print_filtered(const char *info) +{ + const char *p; + + if (!info) + return; + + for (p = info; *p; p++) + if (isprint(*p)) + printk(KERN_CONT "%c", *p); + else + printk(KERN_CONT "\\x%02x", *p & 0xff); +} + +static void __init dmi_dump_ids(void) +{ + printk(KERN_DEBUG "DMI: "); + print_filtered(dmi_get_system_info(DMI_BOARD_NAME)); + printk(KERN_CONT "/"); + print_filtered(dmi_get_system_info(DMI_PRODUCT_NAME)); + printk(KERN_CONT ", BIOS "); + print_filtered(dmi_get_system_info(DMI_BIOS_VERSION)); + printk(KERN_CONT " "); + print_filtered(dmi_get_system_info(DMI_BIOS_DATE)); + printk(KERN_CONT "\n"); +} + static int __init dmi_present(const char __iomem *p) { u8 buf[15]; @@ -381,8 +409,10 @@ static int __init dmi_present(const char __iomem *p) buf[14] >> 4, buf[14] & 0xF); else printk(KERN_INFO "DMI present.\n"); - if (dmi_walk_early(dmi_decode) == 0) + if (dmi_walk_early(dmi_decode) == 0) { + dmi_dump_ids(); return 0; + } } return 1; } diff --git a/drivers/gpio/74x164.c b/drivers/gpio/74x164.c new file mode 100644 index 00000000000..d91ff4c282e --- /dev/null +++ b/drivers/gpio/74x164.c @@ -0,0 +1,182 @@ +/* + * 74Hx164 - Generic serial-in/parallel-out 8-bits shift register GPIO driver + * + * Copyright (C) 2010 Gabor Juhos <juhosg@openwrt.org> + * Copyright (C) 2010 Miguel Gaio <miguel.gaio@efixo.com> + * + * 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/init.h> +#include <linux/mutex.h> +#include <linux/spi/spi.h> +#include <linux/spi/74x164.h> +#include <linux/gpio.h> +#include <linux/slab.h> + +#define GEN_74X164_GPIO_COUNT 8 + + +struct gen_74x164_chip { + struct spi_device *spi; + struct gpio_chip gpio_chip; + struct mutex lock; + u8 port_config; +}; + +static void gen_74x164_set_value(struct gpio_chip *, unsigned, int); + +static struct gen_74x164_chip *gpio_to_chip(struct gpio_chip *gc) +{ + return container_of(gc, struct gen_74x164_chip, gpio_chip); +} + +static int __gen_74x164_write_config(struct gen_74x164_chip *chip) +{ + return spi_write(chip->spi, + &chip->port_config, sizeof(chip->port_config)); +} + +static int gen_74x164_direction_output(struct gpio_chip *gc, + unsigned offset, int val) +{ + gen_74x164_set_value(gc, offset, val); + return 0; +} + +static int gen_74x164_get_value(struct gpio_chip *gc, unsigned offset) +{ + struct gen_74x164_chip *chip = gpio_to_chip(gc); + int ret; + + mutex_lock(&chip->lock); + ret = (chip->port_config >> offset) & 0x1; + mutex_unlock(&chip->lock); + + return ret; +} + +static void gen_74x164_set_value(struct gpio_chip *gc, + unsigned offset, int val) +{ + struct gen_74x164_chip *chip = gpio_to_chip(gc); + + mutex_lock(&chip->lock); + if (val) + chip->port_config |= (1 << offset); + else + chip->port_config &= ~(1 << offset); + + __gen_74x164_write_config(chip); + mutex_unlock(&chip->lock); +} + +static int __devinit gen_74x164_probe(struct spi_device *spi) +{ + struct gen_74x164_chip *chip; + struct gen_74x164_chip_platform_data *pdata; + int ret; + + pdata = spi->dev.platform_data; + if (!pdata || !pdata->base) { + dev_dbg(&spi->dev, "incorrect or missing platform data\n"); + return -EINVAL; + } + + /* + * bits_per_word cannot be configured in platform data + */ + spi->bits_per_word = 8; + + ret = spi_setup(spi); + if (ret < 0) + return ret; + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + mutex_init(&chip->lock); + + dev_set_drvdata(&spi->dev, chip); + + chip->spi = spi; + + chip->gpio_chip.label = GEN_74X164_DRIVER_NAME, + chip->gpio_chip.direction_output = gen_74x164_direction_output; + chip->gpio_chip.get = gen_74x164_get_value; + chip->gpio_chip.set = gen_74x164_set_value; + chip->gpio_chip.base = pdata->base; + chip->gpio_chip.ngpio = GEN_74X164_GPIO_COUNT; + chip->gpio_chip.can_sleep = 1; + chip->gpio_chip.dev = &spi->dev; + chip->gpio_chip.owner = THIS_MODULE; + + ret = __gen_74x164_write_config(chip); + if (ret) { + dev_err(&spi->dev, "Failed writing: %d\n", ret); + goto exit_destroy; + } + + ret = gpiochip_add(&chip->gpio_chip); + if (ret) + goto exit_destroy; + + return ret; + +exit_destroy: + dev_set_drvdata(&spi->dev, NULL); + mutex_destroy(&chip->lock); + kfree(chip); + return ret; +} + +static int gen_74x164_remove(struct spi_device *spi) +{ + struct gen_74x164_chip *chip; + int ret; + + chip = dev_get_drvdata(&spi->dev); + if (chip == NULL) + return -ENODEV; + + dev_set_drvdata(&spi->dev, NULL); + + ret = gpiochip_remove(&chip->gpio_chip); + if (!ret) { + mutex_destroy(&chip->lock); + kfree(chip); + } else + dev_err(&spi->dev, "Failed to remove the GPIO controller: %d\n", + ret); + + return ret; +} + +static struct spi_driver gen_74x164_driver = { + .driver = { + .name = GEN_74X164_DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = gen_74x164_probe, + .remove = __devexit_p(gen_74x164_remove), +}; + +static int __init gen_74x164_init(void) +{ + return spi_register_driver(&gen_74x164_driver); +} +subsys_initcall(gen_74x164_init); + +static void __exit gen_74x164_exit(void) +{ + spi_unregister_driver(&gen_74x164_driver); +} +module_exit(gen_74x164_exit); + +MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>"); +MODULE_AUTHOR("Miguel Gaio <miguel.gaio@efixo.com>"); +MODULE_DESCRIPTION("GPIO expander driver for 74X164 8-bits shift register"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 510aa205454..dd9b4ba8d32 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -70,6 +70,11 @@ config GPIO_MAX730X comment "Memory mapped GPIO expanders:" +config GPIO_BASIC_MMIO + tristate "Basic memory-mapped GPIO controllers support" + help + Say yes here to support basic memory-mapped GPIO controllers. + config GPIO_IT8761E tristate "IT8761E GPIO support" depends on GPIOLIB @@ -267,6 +272,13 @@ config GPIO_ADP5588 To compile this driver as a module, choose M here: the module will be called adp5588-gpio. +config GPIO_ADP5588_IRQ + bool "Interrupt controller support for ADP5588" + depends on GPIO_ADP5588=y + help + Say yes here to enable the adp5588 to be used as an interrupt + controller. It requires the driver to be built in the kernel. + comment "PCI GPIO expanders:" config GPIO_CS5535 @@ -301,6 +313,14 @@ config GPIO_LANGWELL help Say Y here to support Intel Langwell/Penwell GPIO. +config GPIO_PCH + tristate "PCH GPIO of Intel Topcliff" + depends on PCI + help + This driver is for PCH(Platform controller Hub) GPIO of Intel Topcliff + which is an IOH(Input/Output Hub) for x86 embedded processor. + This driver can access PCH GPIO device. + config GPIO_TIMBERDALE bool "Support for timberdale GPIO IP" depends on MFD_TIMBERDALE && GPIOLIB && HAS_IOMEM @@ -339,6 +359,14 @@ config GPIO_MC33880 SPI driver for Freescale MC33880 high-side/low-side switch. This provides GPIO interface supporting inputs and outputs. +config GPIO_74X164 + tristate "74x164 serial-in/parallel-out 8-bits shift register" + depends on SPI_MASTER + help + Platform driver for 74x164 compatible serial-in/parallel-out + 8-outputs shift registers. This driver can be used to provide access + to more gpio outputs. + comment "AC97 GPIO expanders:" config GPIO_UCB1400 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index fc6019d9372..da2ecde5abd 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_GPIOLIB) += gpiolib.o obj-$(CONFIG_GPIO_ADP5520) += adp5520-gpio.o obj-$(CONFIG_GPIO_ADP5588) += adp5588-gpio.o +obj-$(CONFIG_GPIO_BASIC_MMIO) += basic_mmio_gpio.o obj-$(CONFIG_GPIO_LANGWELL) += langwell_gpio.o obj-$(CONFIG_GPIO_MAX730X) += max730x.o obj-$(CONFIG_GPIO_MAX7300) += max7300.o @@ -17,8 +18,10 @@ obj-$(CONFIG_GPIO_MAX7301) += max7301.o obj-$(CONFIG_GPIO_MAX732X) += max732x.o obj-$(CONFIG_GPIO_MC33880) += mc33880.o obj-$(CONFIG_GPIO_MCP23S08) += mcp23s08.o +obj-$(CONFIG_GPIO_74X164) += 74x164.o obj-$(CONFIG_GPIO_PCA953X) += pca953x.o obj-$(CONFIG_GPIO_PCF857X) += pcf857x.o +obj-$(CONFIG_GPIO_PCH) += pch_gpio.o obj-$(CONFIG_GPIO_PL061) += pl061.o obj-$(CONFIG_GPIO_STMPE) += stmpe-gpio.o obj-$(CONFIG_GPIO_TC35892) += tc35892-gpio.o diff --git a/drivers/gpio/adp5588-gpio.c b/drivers/gpio/adp5588-gpio.c index 2e8e9e24f88..0871f78af59 100644 --- a/drivers/gpio/adp5588-gpio.c +++ b/drivers/gpio/adp5588-gpio.c @@ -1,8 +1,8 @@ /* * GPIO Chip driver for Analog Devices - * ADP5588 I/O Expander and QWERTY Keypad Controller + * ADP5588/ADP5587 I/O Expander and QWERTY Keypad Controller * - * Copyright 2009 Analog Devices Inc. + * Copyright 2009-2010 Analog Devices Inc. * * Licensed under the GPL-2 or later. */ @@ -13,21 +13,34 @@ #include <linux/init.h> #include <linux/i2c.h> #include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/irq.h> #include <linux/i2c/adp5588.h> -#define DRV_NAME "adp5588-gpio" -#define MAXGPIO 18 -#define ADP_BANK(offs) ((offs) >> 3) -#define ADP_BIT(offs) (1u << ((offs) & 0x7)) +#define DRV_NAME "adp5588-gpio" + +/* + * Early pre 4.0 Silicon required to delay readout by at least 25ms, + * since the Event Counter Register updated 25ms after the interrupt + * asserted. + */ +#define WA_DELAYED_READOUT_REVID(rev) ((rev) < 4) struct adp5588_gpio { struct i2c_client *client; struct gpio_chip gpio_chip; struct mutex lock; /* protect cached dir, dat_out */ + /* protect serialized access to the interrupt controller bus */ + struct mutex irq_lock; unsigned gpio_start; + unsigned irq_base; uint8_t dat_out[3]; uint8_t dir[3]; + uint8_t int_lvl[3]; + uint8_t int_en[3]; + uint8_t irq_mask[3]; + uint8_t irq_stat[3]; }; static int adp5588_gpio_read(struct i2c_client *client, u8 reg) @@ -55,8 +68,8 @@ static int adp5588_gpio_get_value(struct gpio_chip *chip, unsigned off) struct adp5588_gpio *dev = container_of(chip, struct adp5588_gpio, gpio_chip); - return !!(adp5588_gpio_read(dev->client, GPIO_DAT_STAT1 + ADP_BANK(off)) - & ADP_BIT(off)); + return !!(adp5588_gpio_read(dev->client, + GPIO_DAT_STAT1 + ADP5588_BANK(off)) & ADP5588_BIT(off)); } static void adp5588_gpio_set_value(struct gpio_chip *chip, @@ -66,8 +79,8 @@ static void adp5588_gpio_set_value(struct gpio_chip *chip, struct adp5588_gpio *dev = container_of(chip, struct adp5588_gpio, gpio_chip); - bank = ADP_BANK(off); - bit = ADP_BIT(off); + bank = ADP5588_BANK(off); + bit = ADP5588_BIT(off); mutex_lock(&dev->lock); if (val) @@ -87,10 +100,10 @@ static int adp5588_gpio_direction_input(struct gpio_chip *chip, unsigned off) struct adp5588_gpio *dev = container_of(chip, struct adp5588_gpio, gpio_chip); - bank = ADP_BANK(off); + bank = ADP5588_BANK(off); mutex_lock(&dev->lock); - dev->dir[bank] &= ~ADP_BIT(off); + dev->dir[bank] &= ~ADP5588_BIT(off); ret = adp5588_gpio_write(dev->client, GPIO_DIR1 + bank, dev->dir[bank]); mutex_unlock(&dev->lock); @@ -105,8 +118,8 @@ static int adp5588_gpio_direction_output(struct gpio_chip *chip, struct adp5588_gpio *dev = container_of(chip, struct adp5588_gpio, gpio_chip); - bank = ADP_BANK(off); - bit = ADP_BIT(off); + bank = ADP5588_BANK(off); + bit = ADP5588_BIT(off); mutex_lock(&dev->lock); dev->dir[bank] |= bit; @@ -125,6 +138,213 @@ static int adp5588_gpio_direction_output(struct gpio_chip *chip, return ret; } +#ifdef CONFIG_GPIO_ADP5588_IRQ +static int adp5588_gpio_to_irq(struct gpio_chip *chip, unsigned off) +{ + struct adp5588_gpio *dev = + container_of(chip, struct adp5588_gpio, gpio_chip); + return dev->irq_base + off; +} + +static void adp5588_irq_bus_lock(unsigned int irq) +{ + struct adp5588_gpio *dev = get_irq_chip_data(irq); + mutex_lock(&dev->irq_lock); +} + + /* + * genirq core code can issue chip->mask/unmask from atomic context. + * This doesn't work for slow busses where an access needs to sleep. + * bus_sync_unlock() is therefore called outside the atomic context, + * syncs the current irq mask state with the slow external controller + * and unlocks the bus. + */ + +static void adp5588_irq_bus_sync_unlock(unsigned int irq) +{ + struct adp5588_gpio *dev = get_irq_chip_data(irq); + int i; + + for (i = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) + if (dev->int_en[i] ^ dev->irq_mask[i]) { + dev->int_en[i] = dev->irq_mask[i]; + adp5588_gpio_write(dev->client, GPIO_INT_EN1 + i, + dev->int_en[i]); + } + + mutex_unlock(&dev->irq_lock); +} + +static void adp5588_irq_mask(unsigned int irq) +{ + struct adp5588_gpio *dev = get_irq_chip_data(irq); + unsigned gpio = irq - dev->irq_base; + + dev->irq_mask[ADP5588_BANK(gpio)] &= ~ADP5588_BIT(gpio); +} + +static void adp5588_irq_unmask(unsigned int irq) +{ + struct adp5588_gpio *dev = get_irq_chip_data(irq); + unsigned gpio = irq - dev->irq_base; + + dev->irq_mask[ADP5588_BANK(gpio)] |= ADP5588_BIT(gpio); +} + +static int adp5588_irq_set_type(unsigned int irq, unsigned int type) +{ + struct adp5588_gpio *dev = get_irq_chip_data(irq); + uint16_t gpio = irq - dev->irq_base; + unsigned bank, bit; + + if ((type & IRQ_TYPE_EDGE_BOTH)) { + dev_err(&dev->client->dev, "irq %d: unsupported type %d\n", + irq, type); + return -EINVAL; + } + + bank = ADP5588_BANK(gpio); + bit = ADP5588_BIT(gpio); + + if (type & IRQ_TYPE_LEVEL_HIGH) + dev->int_lvl[bank] |= bit; + else if (type & IRQ_TYPE_LEVEL_LOW) + dev->int_lvl[bank] &= ~bit; + else + return -EINVAL; + + adp5588_gpio_direction_input(&dev->gpio_chip, gpio); + adp5588_gpio_write(dev->client, GPIO_INT_LVL1 + bank, + dev->int_lvl[bank]); + + return 0; +} + +static struct irq_chip adp5588_irq_chip = { + .name = "adp5588", + .mask = adp5588_irq_mask, + .unmask = adp5588_irq_unmask, + .bus_lock = adp5588_irq_bus_lock, + .bus_sync_unlock = adp5588_irq_bus_sync_unlock, + .set_type = adp5588_irq_set_type, +}; + +static int adp5588_gpio_read_intstat(struct i2c_client *client, u8 *buf) +{ + int ret = i2c_smbus_read_i2c_block_data(client, GPIO_INT_STAT1, 3, buf); + + if (ret < 0) + dev_err(&client->dev, "Read INT_STAT Error\n"); + + return ret; +} + +static irqreturn_t adp5588_irq_handler(int irq, void *devid) +{ + struct adp5588_gpio *dev = devid; + unsigned status, bank, bit, pending; + int ret; + status = adp5588_gpio_read(dev->client, INT_STAT); + + if (status & ADP5588_GPI_INT) { + ret = adp5588_gpio_read_intstat(dev->client, dev->irq_stat); + if (ret < 0) + memset(dev->irq_stat, 0, ARRAY_SIZE(dev->irq_stat)); + + for (bank = 0; bank <= ADP5588_BANK(ADP5588_MAXGPIO); + bank++, bit = 0) { + pending = dev->irq_stat[bank] & dev->irq_mask[bank]; + + while (pending) { + if (pending & (1 << bit)) { + handle_nested_irq(dev->irq_base + + (bank << 3) + bit); + pending &= ~(1 << bit); + + } + bit++; + } + } + } + + adp5588_gpio_write(dev->client, INT_STAT, status); /* Status is W1C */ + + return IRQ_HANDLED; +} + +static int adp5588_irq_setup(struct adp5588_gpio *dev) +{ + struct i2c_client *client = dev->client; + struct adp5588_gpio_platform_data *pdata = client->dev.platform_data; + unsigned gpio; + int ret; + + adp5588_gpio_write(client, CFG, ADP5588_AUTO_INC); + adp5588_gpio_write(client, INT_STAT, -1); /* status is W1C */ + adp5588_gpio_read_intstat(client, dev->irq_stat); /* read to clear */ + + dev->irq_base = pdata->irq_base; + mutex_init(&dev->irq_lock); + + for (gpio = 0; gpio < dev->gpio_chip.ngpio; gpio++) { + int irq = gpio + dev->irq_base; + set_irq_chip_data(irq, dev); + set_irq_chip_and_handler(irq, &adp5588_irq_chip, + handle_level_irq); + set_irq_nested_thread(irq, 1); +#ifdef CONFIG_ARM + /* + * ARM needs us to explicitly flag the IRQ as VALID, + * once we do so, it will also set the noprobe. + */ + set_irq_flags(irq, IRQF_VALID); +#else + set_irq_noprobe(irq); +#endif + } + + ret = request_threaded_irq(client->irq, + NULL, + adp5588_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + dev_name(&client->dev), dev); + if (ret) { + dev_err(&client->dev, "failed to request irq %d\n", + client->irq); + goto out; + } + + dev->gpio_chip.to_irq = adp5588_gpio_to_irq; + adp5588_gpio_write(client, CFG, + ADP5588_AUTO_INC | ADP5588_INT_CFG | ADP5588_GPI_INT); + + return 0; + +out: + dev->irq_base = 0; + return ret; +} + +static void adp5588_irq_teardown(struct adp5588_gpio *dev) +{ + if (dev->irq_base) + free_irq(dev->client->irq, dev); +} + +#else +static int adp5588_irq_setup(struct adp5588_gpio *dev) +{ + struct i2c_client *client = dev->client; + dev_warn(&client->dev, "interrupt support not compiled in\n"); + + return 0; +} + +static void adp5588_irq_teardown(struct adp5588_gpio *dev) +{ +} +#endif /* CONFIG_GPIO_ADP5588_IRQ */ + static int __devinit adp5588_gpio_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -160,37 +380,46 @@ static int __devinit adp5588_gpio_probe(struct i2c_client *client, gc->can_sleep = 1; gc->base = pdata->gpio_start; - gc->ngpio = MAXGPIO; + gc->ngpio = ADP5588_MAXGPIO; gc->label = client->name; gc->owner = THIS_MODULE; mutex_init(&dev->lock); - ret = adp5588_gpio_read(dev->client, DEV_ID); if (ret < 0) goto err; revid = ret & ADP5588_DEVICE_ID_MASK; - for (i = 0, ret = 0; i <= ADP_BANK(MAXGPIO); i++) { + for (i = 0, ret = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) { dev->dat_out[i] = adp5588_gpio_read(client, GPIO_DAT_OUT1 + i); dev->dir[i] = adp5588_gpio_read(client, GPIO_DIR1 + i); ret |= adp5588_gpio_write(client, KP_GPIO1 + i, 0); ret |= adp5588_gpio_write(client, GPIO_PULL1 + i, (pdata->pullup_dis_mask >> (8 * i)) & 0xFF); - + ret |= adp5588_gpio_write(client, GPIO_INT_EN1 + i, 0); if (ret) goto err; } + if (pdata->irq_base) { + if (WA_DELAYED_READOUT_REVID(revid)) { + dev_warn(&client->dev, "GPIO int not supported\n"); + } else { + ret = adp5588_irq_setup(dev); + if (ret) + goto err; + } + } + ret = gpiochip_add(&dev->gpio_chip); if (ret) - goto err; + goto err_irq; - dev_info(&client->dev, "gpios %d..%d on a %s Rev. %d\n", + dev_info(&client->dev, "gpios %d..%d (IRQ Base %d) on a %s Rev. %d\n", gc->base, gc->base + gc->ngpio - 1, - client->name, revid); + pdata->irq_base, client->name, revid); if (pdata->setup) { ret = pdata->setup(client, gc->base, gc->ngpio, pdata->context); @@ -199,8 +428,11 @@ static int __devinit adp5588_gpio_probe(struct i2c_client *client, } i2c_set_clientdata(client, dev); + return 0; +err_irq: + adp5588_irq_teardown(dev); err: kfree(dev); return ret; @@ -222,6 +454,9 @@ static int __devexit adp5588_gpio_remove(struct i2c_client *client) } } + if (dev->irq_base) + free_irq(dev->client->irq, dev); + ret = gpiochip_remove(&dev->gpio_chip); if (ret) { dev_err(&client->dev, "gpiochip_remove failed %d\n", ret); diff --git a/drivers/gpio/basic_mmio_gpio.c b/drivers/gpio/basic_mmio_gpio.c new file mode 100644 index 00000000000..3addea65894 --- /dev/null +++ b/drivers/gpio/basic_mmio_gpio.c @@ -0,0 +1,297 @@ +/* + * Driver for basic memory-mapped GPIO controllers. + * + * Copyright 2008 MontaVista Software, Inc. + * Copyright 2008,2010 Anton Vorontsov <cbouatmailru@gmail.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * ....``.```~~~~````.`.`.`.`.```````'',,,.........`````......`....... + * ...`` ```````.. + * ..The simplest form of a GPIO controller that the driver supports is`` + * `.just a single "data" register, where GPIO state can be read and/or ` + * `,..written. ,,..``~~~~ .....``.`.`.~~.```.`.........``````.``````` + * ````````` + ___ +_/~~|___/~| . ```~~~~~~ ___/___\___ ,~.`.`.`.`````.~~...,,,,... +__________|~$@~~~ %~ /o*o*o*o*o*o\ .. Implementing such a GPIO . +o ` ~~~~\___/~~~~ ` controller in FPGA is ,.` + `....trivial..'~`.```.``` + * ``````` + * .```````~~~~`..`.``.``. + * . The driver supports `... ,..```.`~~~```````````````....````.``,, + * . big-endian notation, just`. .. A bit more sophisticated controllers , + * . register the device with -be`. .with a pair of set/clear-bit registers , + * `.. suffix. ```~~`````....`.` . affecting the data register and the .` + * ``.`.``...``` ```.. output pins are also supported.` + * ^^ `````.`````````.,``~``~``~~`````` + * . ^^ + * ,..`.`.`...````````````......`.`.`.`.`.`..`.`.`.. + * .. The expectation is that in at least some cases . ,-~~~-, + * .this will be used with roll-your-own ASIC/FPGA .` \ / + * .logic in Verilog or VHDL. ~~~`````````..`````~~` \ / + * ..````````......``````````` \o_ + * | + * ^^ / \ + * + * ...`````~~`.....``.`..........``````.`.``.```........``. + * ` 8, 16, 32 and 64 bits registers are supported, and``. + * . the number of GPIOs is determined by the width of ~ + * .. the registers. ,............```.`.`..`.`.~~~.`.`.`~ + * `.......````.``` + */ + +#include <linux/init.h> +#include <linux/bug.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/compiler.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/log2.h> +#include <linux/ioport.h> +#include <linux/io.h> +#include <linux/gpio.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/mod_devicetable.h> +#include <linux/basic_mmio_gpio.h> + +struct bgpio_chip { + struct gpio_chip gc; + void __iomem *reg_dat; + void __iomem *reg_set; + void __iomem *reg_clr; + + /* Number of bits (GPIOs): <register width> * 8. */ + int bits; + + /* + * Some GPIO controllers work with the big-endian bits notation, + * e.g. in a 8-bits register, GPIO7 is the least significant bit. + */ + int big_endian_bits; + + /* + * Used to lock bgpio_chip->data. Also, this is needed to keep + * shadowed and real data registers writes together. + */ + spinlock_t lock; + + /* Shadowed data register to clear/set bits safely. */ + unsigned long data; +}; + +static struct bgpio_chip *to_bgpio_chip(struct gpio_chip *gc) +{ + return container_of(gc, struct bgpio_chip, gc); +} + +static unsigned long bgpio_in(struct bgpio_chip *bgc) +{ + switch (bgc->bits) { + case 8: + return __raw_readb(bgc->reg_dat); + case 16: + return __raw_readw(bgc->reg_dat); + case 32: + return __raw_readl(bgc->reg_dat); +#if BITS_PER_LONG >= 64 + case 64: + return __raw_readq(bgc->reg_dat); +#endif + } + return -EINVAL; +} + +static void bgpio_out(struct bgpio_chip *bgc, void __iomem *reg, + unsigned long data) +{ + switch (bgc->bits) { + case 8: + __raw_writeb(data, reg); + return; + case 16: + __raw_writew(data, reg); + return; + case 32: + __raw_writel(data, reg); + return; +#if BITS_PER_LONG >= 64 + case 64: + __raw_writeq(data, reg); + return; +#endif + } +} + +static unsigned long bgpio_pin2mask(struct bgpio_chip *bgc, unsigned int pin) +{ + if (bgc->big_endian_bits) + return 1 << (bgc->bits - 1 - pin); + else + return 1 << pin; +} + +static int bgpio_get(struct gpio_chip *gc, unsigned int gpio) +{ + struct bgpio_chip *bgc = to_bgpio_chip(gc); + + return bgpio_in(bgc) & bgpio_pin2mask(bgc, gpio); +} + +static void bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) +{ + struct bgpio_chip *bgc = to_bgpio_chip(gc); + unsigned long mask = bgpio_pin2mask(bgc, gpio); + unsigned long flags; + + if (bgc->reg_set) { + if (val) + bgpio_out(bgc, bgc->reg_set, mask); + else + bgpio_out(bgc, bgc->reg_clr, mask); + return; + } + + spin_lock_irqsave(&bgc->lock, flags); + + if (val) + bgc->data |= mask; + else + bgc->data &= ~mask; + + bgpio_out(bgc, bgc->reg_dat, bgc->data); + + spin_unlock_irqrestore(&bgc->lock, flags); +} + +static int bgpio_dir_in(struct gpio_chip *gc, unsigned int gpio) +{ + return 0; +} + +static int bgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) +{ + bgpio_set(gc, gpio, val); + return 0; +} + +static int __devinit bgpio_probe(struct platform_device *pdev) +{ + const struct platform_device_id *platid = platform_get_device_id(pdev); + struct device *dev = &pdev->dev; + struct bgpio_pdata *pdata = dev_get_platdata(dev); + struct bgpio_chip *bgc; + struct resource *res_dat; + struct resource *res_set; + struct resource *res_clr; + resource_size_t dat_sz; + int bits; + int ret; + + res_dat = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dat"); + if (!res_dat) + return -EINVAL; + + dat_sz = resource_size(res_dat); + if (!is_power_of_2(dat_sz)) + return -EINVAL; + + bits = dat_sz * 8; + if (bits > BITS_PER_LONG) + return -EINVAL; + + bgc = devm_kzalloc(dev, sizeof(*bgc), GFP_KERNEL); + if (!bgc) + return -ENOMEM; + + bgc->reg_dat = devm_ioremap(dev, res_dat->start, dat_sz); + if (!bgc->reg_dat) + return -ENOMEM; + + res_set = platform_get_resource_byname(pdev, IORESOURCE_MEM, "set"); + res_clr = platform_get_resource_byname(pdev, IORESOURCE_MEM, "clr"); + if (res_set && res_clr) { + if (resource_size(res_set) != resource_size(res_clr) || + resource_size(res_set) != dat_sz) + return -EINVAL; + + bgc->reg_set = devm_ioremap(dev, res_set->start, dat_sz); + bgc->reg_clr = devm_ioremap(dev, res_clr->start, dat_sz); + if (!bgc->reg_set || !bgc->reg_clr) + return -ENOMEM; + } else if (res_set || res_clr) { + return -EINVAL; + } + + spin_lock_init(&bgc->lock); + + bgc->bits = bits; + bgc->big_endian_bits = !strcmp(platid->name, "basic-mmio-gpio-be"); + bgc->data = bgpio_in(bgc); + + bgc->gc.ngpio = bits; + bgc->gc.direction_input = bgpio_dir_in; + bgc->gc.direction_output = bgpio_dir_out; + bgc->gc.get = bgpio_get; + bgc->gc.set = bgpio_set; + bgc->gc.dev = dev; + bgc->gc.label = dev_name(dev); + + if (pdata) + bgc->gc.base = pdata->base; + else + bgc->gc.base = -1; + + dev_set_drvdata(dev, bgc); + + ret = gpiochip_add(&bgc->gc); + if (ret) + dev_err(dev, "gpiochip_add() failed: %d\n", ret); + + return ret; +} + +static int __devexit bgpio_remove(struct platform_device *pdev) +{ + struct bgpio_chip *bgc = dev_get_drvdata(&pdev->dev); + + return gpiochip_remove(&bgc->gc); +} + +static const struct platform_device_id bgpio_id_table[] = { + { "basic-mmio-gpio", }, + { "basic-mmio-gpio-be", }, + {}, +}; +MODULE_DEVICE_TABLE(platform, bgpio_id_table); + +static struct platform_driver bgpio_driver = { + .driver = { + .name = "basic-mmio-gpio", + }, + .id_table = bgpio_id_table, + .probe = bgpio_probe, + .remove = __devexit_p(bgpio_remove), +}; + +static int __init bgpio_init(void) +{ + return platform_driver_register(&bgpio_driver); +} +module_init(bgpio_init); + +static void __exit bgpio_exit(void) +{ + platform_driver_unregister(&bgpio_driver); +} +module_exit(bgpio_exit); + +MODULE_DESCRIPTION("Driver for basic memory-mapped GPIO controllers"); +MODULE_AUTHOR("Anton Vorontsov <cbouatmailru@gmail.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/langwell_gpio.c b/drivers/gpio/langwell_gpio.c index 8383a8d7f99..64db9dc3a27 100644 --- a/drivers/gpio/langwell_gpio.c +++ b/drivers/gpio/langwell_gpio.c @@ -18,10 +18,12 @@ /* Supports: * Moorestown platform Langwell chip. * Medfield platform Penwell chip. + * Whitney point. */ #include <linux/module.h> #include <linux/pci.h> +#include <linux/platform_device.h> #include <linux/kernel.h> #include <linux/delay.h> #include <linux/stddef.h> @@ -158,15 +160,15 @@ static int lnw_irq_type(unsigned irq, unsigned type) spin_unlock_irqrestore(&lnw->lock, flags); return 0; -}; +} static void lnw_irq_unmask(unsigned irq) { -}; +} static void lnw_irq_mask(unsigned irq) { -}; +} static struct irq_chip lnw_irqchip = { .name = "LNW-GPIO", @@ -300,9 +302,88 @@ static struct pci_driver lnw_gpio_driver = { .probe = lnw_gpio_probe, }; + +static int __devinit wp_gpio_probe(struct platform_device *pdev) +{ + struct lnw_gpio *lnw; + struct gpio_chip *gc; + struct resource *rc; + int retval = 0; + + rc = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!rc) + return -EINVAL; + + lnw = kzalloc(sizeof(struct lnw_gpio), GFP_KERNEL); + if (!lnw) { + dev_err(&pdev->dev, + "can't allocate whitneypoint_gpio chip data\n"); + return -ENOMEM; + } + lnw->reg_base = ioremap_nocache(rc->start, resource_size(rc)); + if (lnw->reg_base == NULL) { + retval = -EINVAL; + goto err_kmalloc; + } + spin_lock_init(&lnw->lock); + gc = &lnw->chip; + gc->label = dev_name(&pdev->dev); + gc->owner = THIS_MODULE; + gc->direction_input = lnw_gpio_direction_input; + gc->direction_output = lnw_gpio_direction_output; + gc->get = lnw_gpio_get; + gc->set = lnw_gpio_set; + gc->to_irq = NULL; + gc->base = 0; + gc->ngpio = 64; + gc->can_sleep = 0; + retval = gpiochip_add(gc); + if (retval) { + dev_err(&pdev->dev, "whitneypoint gpiochip_add error %d\n", + retval); + goto err_ioremap; + } + platform_set_drvdata(pdev, lnw); + return 0; +err_ioremap: + iounmap(lnw->reg_base); +err_kmalloc: + kfree(lnw); + return retval; +} + +static int __devexit wp_gpio_remove(struct platform_device *pdev) +{ + struct lnw_gpio *lnw = platform_get_drvdata(pdev); + int err; + err = gpiochip_remove(&lnw->chip); + if (err) + dev_err(&pdev->dev, "failed to remove gpio_chip.\n"); + iounmap(lnw->reg_base); + kfree(lnw); + platform_set_drvdata(pdev, NULL); + return 0; +} + +static struct platform_driver wp_gpio_driver = { + .probe = wp_gpio_probe, + .remove = __devexit_p(wp_gpio_remove), + .driver = { + .name = "wp_gpio", + .owner = THIS_MODULE, + }, +}; + static int __init lnw_gpio_init(void) { - return pci_register_driver(&lnw_gpio_driver); + int ret; + ret = pci_register_driver(&lnw_gpio_driver); + if (ret < 0) + return ret; + ret = platform_driver_register(&wp_gpio_driver); + if (ret < 0) + pci_unregister_driver(&lnw_gpio_driver); + return ret; } device_initcall(lnw_gpio_init); diff --git a/drivers/gpio/pch_gpio.c b/drivers/gpio/pch_gpio.c new file mode 100644 index 00000000000..0eba0a75c80 --- /dev/null +++ b/drivers/gpio/pch_gpio.c @@ -0,0 +1,312 @@ +/* + * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/gpio.h> + +#define PCH_GPIO_ALL_PINS 0xfff /* Mask for GPIO pins 0 to 11 */ +#define GPIO_NUM_PINS 12 /* Specifies number of GPIO PINS GPIO0-GPIO11 */ + +struct pch_regs { + u32 ien; + u32 istatus; + u32 idisp; + u32 iclr; + u32 imask; + u32 imaskclr; + u32 po; + u32 pi; + u32 pm; + u32 im0; + u32 im1; + u32 reserved[4]; + u32 reset; +}; + +/** + * struct pch_gpio_reg_data - The register store data. + * @po_reg: To store contents of PO register. + * @pm_reg: To store contents of PM register. + */ +struct pch_gpio_reg_data { + u32 po_reg; + u32 pm_reg; +}; + +/** + * struct pch_gpio - GPIO private data structure. + * @base: PCI base address of Memory mapped I/O register. + * @reg: Memory mapped PCH GPIO register list. + * @dev: Pointer to device structure. + * @gpio: Data for GPIO infrastructure. + * @pch_gpio_reg: Memory mapped Register data is saved here + * when suspend. + */ +struct pch_gpio { + void __iomem *base; + struct pch_regs __iomem *reg; + struct device *dev; + struct gpio_chip gpio; + struct pch_gpio_reg_data pch_gpio_reg; + struct mutex lock; +}; + +static void pch_gpio_set(struct gpio_chip *gpio, unsigned nr, int val) +{ + u32 reg_val; + struct pch_gpio *chip = container_of(gpio, struct pch_gpio, gpio); + + mutex_lock(&chip->lock); + reg_val = ioread32(&chip->reg->po); + if (val) + reg_val |= (1 << nr); + else + reg_val &= ~(1 << nr); + + iowrite32(reg_val, &chip->reg->po); + mutex_unlock(&chip->lock); +} + +static int pch_gpio_get(struct gpio_chip *gpio, unsigned nr) +{ + struct pch_gpio *chip = container_of(gpio, struct pch_gpio, gpio); + + return ioread32(&chip->reg->pi) & (1 << nr); +} + +static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned nr, + int val) +{ + struct pch_gpio *chip = container_of(gpio, struct pch_gpio, gpio); + u32 pm; + u32 reg_val; + + mutex_lock(&chip->lock); + pm = ioread32(&chip->reg->pm) & PCH_GPIO_ALL_PINS; + pm |= (1 << nr); + iowrite32(pm, &chip->reg->pm); + + reg_val = ioread32(&chip->reg->po); + if (val) + reg_val |= (1 << nr); + else + reg_val &= ~(1 << nr); + + mutex_unlock(&chip->lock); + + return 0; +} + +static int pch_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) +{ + struct pch_gpio *chip = container_of(gpio, struct pch_gpio, gpio); + u32 pm; + + mutex_lock(&chip->lock); + pm = ioread32(&chip->reg->pm) & PCH_GPIO_ALL_PINS; /*bits 0-11*/ + pm &= ~(1 << nr); + iowrite32(pm, &chip->reg->pm); + mutex_unlock(&chip->lock); + + return 0; +} + +/* + * Save register configuration and disable interrupts. + */ +static void pch_gpio_save_reg_conf(struct pch_gpio *chip) +{ + chip->pch_gpio_reg.po_reg = ioread32(&chip->reg->po); + chip->pch_gpio_reg.pm_reg = ioread32(&chip->reg->pm); +} + +/* + * This function restores the register configuration of the GPIO device. + */ +static void pch_gpio_restore_reg_conf(struct pch_gpio *chip) +{ + /* to store contents of PO register */ + iowrite32(chip->pch_gpio_reg.po_reg, &chip->reg->po); + /* to store contents of PM register */ + iowrite32(chip->pch_gpio_reg.pm_reg, &chip->reg->pm); +} + +static void pch_gpio_setup(struct pch_gpio *chip) +{ + struct gpio_chip *gpio = &chip->gpio; + + gpio->label = dev_name(chip->dev); + gpio->owner = THIS_MODULE; + gpio->direction_input = pch_gpio_direction_input; + gpio->get = pch_gpio_get; + gpio->direction_output = pch_gpio_direction_output; + gpio->set = pch_gpio_set; + gpio->dbg_show = NULL; + gpio->base = -1; + gpio->ngpio = GPIO_NUM_PINS; + gpio->can_sleep = 0; +} + +static int __devinit pch_gpio_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + s32 ret; + struct pch_gpio *chip; + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + + chip->dev = &pdev->dev; + ret = pci_enable_device(pdev); + if (ret) { + dev_err(&pdev->dev, "%s : pci_enable_device FAILED", __func__); + goto err_pci_enable; + } + + ret = pci_request_regions(pdev, KBUILD_MODNAME); + if (ret) { + dev_err(&pdev->dev, "pci_request_regions FAILED-%d", ret); + goto err_request_regions; + } + + chip->base = pci_iomap(pdev, 1, 0); + if (chip->base == 0) { + dev_err(&pdev->dev, "%s : pci_iomap FAILED", __func__); + ret = -ENOMEM; + goto err_iomap; + } + + chip->reg = chip->base; + pci_set_drvdata(pdev, chip); + mutex_init(&chip->lock); + pch_gpio_setup(chip); + ret = gpiochip_add(&chip->gpio); + if (ret) { + dev_err(&pdev->dev, "PCH gpio: Failed to register GPIO\n"); + goto err_gpiochip_add; + } + + return 0; + +err_gpiochip_add: + pci_iounmap(pdev, chip->base); + +err_iomap: + pci_release_regions(pdev); + +err_request_regions: + pci_disable_device(pdev); + +err_pci_enable: + kfree(chip); + dev_err(&pdev->dev, "%s Failed returns %d\n", __func__, ret); + return ret; +} + +static void __devexit pch_gpio_remove(struct pci_dev *pdev) +{ + int err; + struct pch_gpio *chip = pci_get_drvdata(pdev); + + err = gpiochip_remove(&chip->gpio); + if (err) + dev_err(&pdev->dev, "Failed gpiochip_remove\n"); + + pci_iounmap(pdev, chip->base); + pci_release_regions(pdev); + pci_disable_device(pdev); + kfree(chip); +} + +#ifdef CONFIG_PM +static int pch_gpio_suspend(struct pci_dev *pdev, pm_message_t state) +{ + s32 ret; + struct pch_gpio *chip = pci_get_drvdata(pdev); + + pch_gpio_save_reg_conf(chip); + pch_gpio_restore_reg_conf(chip); + + ret = pci_save_state(pdev); + if (ret) { + dev_err(&pdev->dev, "pci_save_state Failed-%d\n", ret); + return ret; + } + pci_disable_device(pdev); + pci_set_power_state(pdev, PCI_D0); + ret = pci_enable_wake(pdev, PCI_D0, 1); + if (ret) + dev_err(&pdev->dev, "pci_enable_wake Failed -%d\n", ret); + + return 0; +} + +static int pch_gpio_resume(struct pci_dev *pdev) +{ + s32 ret; + struct pch_gpio *chip = pci_get_drvdata(pdev); + + ret = pci_enable_wake(pdev, PCI_D0, 0); + + pci_set_power_state(pdev, PCI_D0); + ret = pci_enable_device(pdev); + if (ret) { + dev_err(&pdev->dev, "pci_enable_device Failed-%d ", ret); + return ret; + } + pci_restore_state(pdev); + + iowrite32(0x01, &chip->reg->reset); + iowrite32(0x00, &chip->reg->reset); + pch_gpio_restore_reg_conf(chip); + + return 0; +} +#else +#define pch_gpio_suspend NULL +#define pch_gpio_resume NULL +#endif + +static DEFINE_PCI_DEVICE_TABLE(pch_gpio_pcidev_id) = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8803) }, + { 0, } +}; + +static struct pci_driver pch_gpio_driver = { + .name = "pch_gpio", + .id_table = pch_gpio_pcidev_id, + .probe = pch_gpio_probe, + .remove = __devexit_p(pch_gpio_remove), + .suspend = pch_gpio_suspend, + .resume = pch_gpio_resume +}; + +static int __init pch_gpio_pci_init(void) +{ + return pci_register_driver(&pch_gpio_driver); +} +module_init(pch_gpio_pci_init); + +static void __exit pch_gpio_pci_exit(void) +{ + pci_unregister_driver(&pch_gpio_driver); +} +module_exit(pch_gpio_pci_exit); + +MODULE_DESCRIPTION("PCH GPIO PCI Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/timbgpio.c b/drivers/gpio/timbgpio.c index ddd053108a1..45293662e95 100644 --- a/drivers/gpio/timbgpio.c +++ b/drivers/gpio/timbgpio.c @@ -47,6 +47,7 @@ struct timbgpio { spinlock_t lock; /* mutual exclusion */ struct gpio_chip gpio; int irq_base; + unsigned long last_ier; }; static int timbgpio_update_bit(struct gpio_chip *gpio, unsigned index, @@ -112,16 +113,24 @@ static void timbgpio_irq_disable(unsigned irq) { struct timbgpio *tgpio = get_irq_chip_data(irq); int offset = irq - tgpio->irq_base; + unsigned long flags; - timbgpio_update_bit(&tgpio->gpio, offset, TGPIO_IER, 0); + spin_lock_irqsave(&tgpio->lock, flags); + tgpio->last_ier &= ~(1 << offset); + iowrite32(tgpio->last_ier, tgpio->membase + TGPIO_IER); + spin_unlock_irqrestore(&tgpio->lock, flags); } static void timbgpio_irq_enable(unsigned irq) { struct timbgpio *tgpio = get_irq_chip_data(irq); int offset = irq - tgpio->irq_base; + unsigned long flags; - timbgpio_update_bit(&tgpio->gpio, offset, TGPIO_IER, 1); + spin_lock_irqsave(&tgpio->lock, flags); + tgpio->last_ier |= 1 << offset; + iowrite32(tgpio->last_ier, tgpio->membase + TGPIO_IER); + spin_unlock_irqrestore(&tgpio->lock, flags); } static int timbgpio_irq_type(unsigned irq, unsigned trigger) @@ -194,8 +203,16 @@ static void timbgpio_irq(unsigned int irq, struct irq_desc *desc) ipr = ioread32(tgpio->membase + TGPIO_IPR); iowrite32(ipr, tgpio->membase + TGPIO_ICR); + /* + * Some versions of the hardware trash the IER register if more than + * one interrupt is received simultaneously. + */ + iowrite32(0, tgpio->membase + TGPIO_IER); + for_each_set_bit(offset, &ipr, tgpio->gpio.ngpio) generic_handle_irq(timbgpio_to_irq(&tgpio->gpio, offset)); + + iowrite32(tgpio->last_ier, tgpio->membase + TGPIO_IER); } static struct irq_chip timbgpio_irqchip = { diff --git a/drivers/media/IR/lirc_dev.c b/drivers/media/IR/lirc_dev.c index 0acf6396e06..202581808bd 100644 --- a/drivers/media/IR/lirc_dev.c +++ b/drivers/media/IR/lirc_dev.c @@ -27,7 +27,6 @@ #include <linux/fs.h> #include <linux/poll.h> #include <linux/completion.h> -#include <linux/errno.h> #include <linux/mutex.h> #include <linux/wait.h> #include <linux/unistd.h> diff --git a/drivers/platform/x86/intel_pmic_gpio.c b/drivers/platform/x86/intel_pmic_gpio.c index f540ff96c53..e61db9dfebe 100644 --- a/drivers/platform/x86/intel_pmic_gpio.c +++ b/drivers/platform/x86/intel_pmic_gpio.c @@ -29,7 +29,6 @@ #include <linux/init.h> #include <linux/io.h> #include <linux/gpio.h> -#include <linux/interrupt.h> #include <asm/intel_scu_ipc.h> #include <linux/device.h> #include <linux/intel_pmic_gpio.h> diff --git a/drivers/rapidio/rio-driver.c b/drivers/rapidio/rio-driver.c index 3222fa3c808..0f4a53bdaa3 100644 --- a/drivers/rapidio/rio-driver.c +++ b/drivers/rapidio/rio-driver.c @@ -192,7 +192,7 @@ static int rio_match_bus(struct device *dev, struct device_driver *drv) out:return 0; } -static struct device rio_bus = { +struct device rio_bus = { .init_name = "rapidio", }; diff --git a/drivers/rapidio/rio-scan.c b/drivers/rapidio/rio-scan.c index 8070e074c73..1eb82c4c712 100644 --- a/drivers/rapidio/rio-scan.c +++ b/drivers/rapidio/rio-scan.c @@ -48,7 +48,7 @@ DEFINE_SPINLOCK(rio_global_list_lock); static int next_destid = 0; static int next_switchid = 0; static int next_net = 0; -static int next_comptag; +static int next_comptag = 1; static struct timer_list rio_enum_timer = TIMER_INITIALIZER(rio_enum_timeout, 0, 0); @@ -121,27 +121,6 @@ static int rio_clear_locks(struct rio_mport *port) u32 result; int ret = 0; - /* Assign component tag to all devices */ - next_comptag = 1; - rio_local_write_config_32(port, RIO_COMPONENT_TAG_CSR, next_comptag++); - - list_for_each_entry(rdev, &rio_devices, global_list) { - /* Mark device as discovered */ - rio_read_config_32(rdev, - rdev->phys_efptr + RIO_PORT_GEN_CTL_CSR, - &result); - rio_write_config_32(rdev, - rdev->phys_efptr + RIO_PORT_GEN_CTL_CSR, - result | RIO_PORT_GEN_DISCOVERED); - - rio_write_config_32(rdev, RIO_COMPONENT_TAG_CSR, next_comptag); - rdev->comp_tag = next_comptag++; - if (next_comptag >= 0x10000) { - pr_err("RIO: Component Tag Counter Overflow\n"); - break; - } - } - /* Release host device id locks */ rio_local_write_config_32(port, RIO_HOST_DID_LOCK_CSR, port->host_deviceid); @@ -162,6 +141,15 @@ static int rio_clear_locks(struct rio_mport *port) rdev->vid, rdev->did); ret = -EINVAL; } + + /* Mark device as discovered and enable master */ + rio_read_config_32(rdev, + rdev->phys_efptr + RIO_PORT_GEN_CTL_CSR, + &result); + result |= RIO_PORT_GEN_DISCOVERED | RIO_PORT_GEN_MASTER; + rio_write_config_32(rdev, + rdev->phys_efptr + RIO_PORT_GEN_CTL_CSR, + result); } return ret; @@ -420,11 +408,27 @@ static struct rio_dev __devinit *rio_setup_device(struct rio_net *net, hopcount, RIO_EFB_ERR_MGMNT); } + if (rdev->pef & (RIO_PEF_SWITCH | RIO_PEF_MULTIPORT)) { + rio_mport_read_config_32(port, destid, hopcount, + RIO_SWP_INFO_CAR, &rdev->swpinfo); + } + rio_mport_read_config_32(port, destid, hopcount, RIO_SRC_OPS_CAR, &rdev->src_ops); rio_mport_read_config_32(port, destid, hopcount, RIO_DST_OPS_CAR, &rdev->dst_ops); + if (do_enum) { + /* Assign component tag to device */ + if (next_comptag >= 0x10000) { + pr_err("RIO: Component Tag Counter Overflow\n"); + goto cleanup; + } + rio_mport_write_config_32(port, destid, hopcount, + RIO_COMPONENT_TAG_CSR, next_comptag); + rdev->comp_tag = next_comptag++; + } + if (rio_device_has_destid(port, rdev->src_ops, rdev->dst_ops)) { if (do_enum) { rio_set_device_id(port, destid, hopcount, next_destid); @@ -439,9 +443,10 @@ static struct rio_dev __devinit *rio_setup_device(struct rio_net *net, /* If a PE has both switch and other functions, show it as a switch */ if (rio_is_switch(rdev)) { - rio_mport_read_config_32(port, destid, hopcount, - RIO_SWP_INFO_CAR, &rdev->swpinfo); - rswitch = kzalloc(sizeof(struct rio_switch), GFP_KERNEL); + rswitch = kzalloc(sizeof(*rswitch) + + RIO_GET_TOTAL_PORTS(rdev->swpinfo) * + sizeof(rswitch->nextdev[0]), + GFP_KERNEL); if (!rswitch) goto cleanup; rswitch->switchid = next_switchid; @@ -458,6 +463,7 @@ static struct rio_dev __devinit *rio_setup_device(struct rio_net *net, rdid++) rswitch->route_table[rdid] = RIO_INVALID_ROUTE; rdev->rswitch = rswitch; + rswitch->rdev = rdev; dev_set_name(&rdev->dev, "%02x:s:%04x", rdev->net->id, rdev->rswitch->switchid); rio_switch_init(rdev, do_enum); @@ -478,6 +484,7 @@ static struct rio_dev __devinit *rio_setup_device(struct rio_net *net, } rdev->dev.bus = &rio_bus_type; + rdev->dev.parent = &rio_bus; device_initialize(&rdev->dev); rdev->dev.release = rio_release_dev; @@ -718,86 +725,53 @@ static u16 rio_get_host_deviceid_lock(struct rio_mport *port, u8 hopcount) } /** - * rio_get_swpinfo_inport- Gets the ingress port number - * @mport: Master port to send transaction - * @destid: Destination ID associated with the switch - * @hopcount: Number of hops to the device - * - * Returns port number being used to access the switch device. - */ -static u8 -rio_get_swpinfo_inport(struct rio_mport *mport, u16 destid, u8 hopcount) -{ - u32 result; - - rio_mport_read_config_32(mport, destid, hopcount, RIO_SWP_INFO_CAR, - &result); - - return (u8) (result & 0xff); -} - -/** - * rio_get_swpinfo_tports- Gets total number of ports on the switch - * @mport: Master port to send transaction - * @destid: Destination ID associated with the switch - * @hopcount: Number of hops to the device - * - * Returns total numbers of ports implemented by the switch device. - */ -static u8 rio_get_swpinfo_tports(struct rio_mport *mport, u16 destid, - u8 hopcount) -{ - u32 result; - - rio_mport_read_config_32(mport, destid, hopcount, RIO_SWP_INFO_CAR, - &result); - - return RIO_GET_TOTAL_PORTS(result); -} - -/** - * rio_net_add_mport- Add a master port to a RIO network - * @net: RIO network - * @port: Master port to add - * - * Adds a master port to the network list of associated master - * ports.. - */ -static void rio_net_add_mport(struct rio_net *net, struct rio_mport *port) -{ - spin_lock(&rio_global_list_lock); - list_add_tail(&port->nnode, &net->mports); - spin_unlock(&rio_global_list_lock); -} - -/** * rio_enum_peer- Recursively enumerate a RIO network through a master port * @net: RIO network being enumerated * @port: Master port to send transactions * @hopcount: Number of hops into the network + * @prev: Previous RIO device connected to the enumerated one + * @prev_port: Port on previous RIO device * * Recursively enumerates a RIO network. Transactions are sent via the * master port passed in @port. */ static int __devinit rio_enum_peer(struct rio_net *net, struct rio_mport *port, - u8 hopcount) + u8 hopcount, struct rio_dev *prev, int prev_port) { int port_num; - int num_ports; int cur_destid; int sw_destid; int sw_inport; struct rio_dev *rdev; u16 destid; + u32 regval; int tmp; + if (rio_mport_chk_dev_access(port, + RIO_ANY_DESTID(port->sys_size), hopcount)) { + pr_debug("RIO: device access check failed\n"); + return -1; + } + if (rio_get_host_deviceid_lock(port, hopcount) == port->host_deviceid) { pr_debug("RIO: PE already discovered by this host\n"); /* * Already discovered by this host. Add it as another - * master port for the current network. + * link to the existing device. */ - rio_net_add_mport(net, port); + rio_mport_read_config_32(port, RIO_ANY_DESTID(port->sys_size), + hopcount, RIO_COMPONENT_TAG_CSR, ®val); + + if (regval) { + rdev = rio_get_comptag((regval & 0xffff), NULL); + + if (rdev && prev && rio_is_switch(prev)) { + pr_debug("RIO: redundant path to %s\n", + rio_name(rdev)); + prev->rswitch->nextdev[prev_port] = rdev; + } + } + return 0; } @@ -828,13 +802,15 @@ static int __devinit rio_enum_peer(struct rio_net *net, struct rio_mport *port, if (rdev) { /* Add device to the global and bus/net specific list. */ list_add_tail(&rdev->net_list, &net->devices); + rdev->prev = prev; + if (prev && rio_is_switch(prev)) + prev->rswitch->nextdev[prev_port] = rdev; } else return -1; if (rio_is_switch(rdev)) { next_switchid++; - sw_inport = rio_get_swpinfo_inport(port, - RIO_ANY_DESTID(port->sys_size), hopcount); + sw_inport = RIO_GET_PORT_NUM(rdev->swpinfo); rio_route_add_entry(port, rdev->rswitch, RIO_GLOBAL_TABLE, port->host_deviceid, sw_inport, 0); rdev->rswitch->route_table[port->host_deviceid] = sw_inport; @@ -847,14 +823,14 @@ static int __devinit rio_enum_peer(struct rio_net *net, struct rio_mport *port, rdev->rswitch->route_table[destid] = sw_inport; } - num_ports = - rio_get_swpinfo_tports(port, RIO_ANY_DESTID(port->sys_size), - hopcount); pr_debug( "RIO: found %s (vid %4.4x did %4.4x) with %d ports\n", - rio_name(rdev), rdev->vid, rdev->did, num_ports); + rio_name(rdev), rdev->vid, rdev->did, + RIO_GET_TOTAL_PORTS(rdev->swpinfo)); sw_destid = next_destid; - for (port_num = 0; port_num < num_ports; port_num++) { + for (port_num = 0; + port_num < RIO_GET_TOTAL_PORTS(rdev->swpinfo); + port_num++) { /*Enable Input Output Port (transmitter reviever)*/ rio_enable_rx_tx_port(port, 0, RIO_ANY_DESTID(port->sys_size), @@ -879,7 +855,8 @@ static int __devinit rio_enum_peer(struct rio_net *net, struct rio_mport *port, RIO_ANY_DESTID(port->sys_size), port_num, 0); - if (rio_enum_peer(net, port, hopcount + 1) < 0) + if (rio_enum_peer(net, port, hopcount + 1, + rdev, port_num) < 0) return -1; /* Update routing tables */ @@ -945,10 +922,11 @@ static int __devinit rio_enum_peer(struct rio_net *net, struct rio_mport *port, */ static int rio_enum_complete(struct rio_mport *port) { - u32 tag_csr; + u32 regval; - rio_local_read_config_32(port, RIO_COMPONENT_TAG_CSR, &tag_csr); - return (tag_csr & 0xffff) ? 1 : 0; + rio_local_read_config_32(port, port->phys_efptr + RIO_PORT_GEN_CTL_CSR, + ®val); + return (regval & RIO_PORT_GEN_MASTER) ? 1 : 0; } /** @@ -966,7 +944,6 @@ rio_disc_peer(struct rio_net *net, struct rio_mport *port, u16 destid, u8 hopcount) { u8 port_num, route_port; - int num_ports; struct rio_dev *rdev; u16 ndestid; @@ -983,13 +960,14 @@ rio_disc_peer(struct rio_net *net, struct rio_mport *port, u16 destid, /* Associated destid is how we accessed this switch */ rdev->rswitch->destid = destid; - num_ports = rio_get_swpinfo_tports(port, destid, hopcount); pr_debug( "RIO: found %s (vid %4.4x did %4.4x) with %d ports\n", - rio_name(rdev), rdev->vid, rdev->did, num_ports); - for (port_num = 0; port_num < num_ports; port_num++) { - if (rio_get_swpinfo_inport(port, destid, hopcount) == - port_num) + rio_name(rdev), rdev->vid, rdev->did, + RIO_GET_TOTAL_PORTS(rdev->swpinfo)); + for (port_num = 0; + port_num < RIO_GET_TOTAL_PORTS(rdev->swpinfo); + port_num++) { + if (RIO_GET_PORT_NUM(rdev->swpinfo) == port_num) continue; if (rio_sport_is_active @@ -1011,6 +989,8 @@ rio_disc_peer(struct rio_net *net, struct rio_mport *port, u16 destid, break; } + if (ndestid == RIO_ANY_DESTID(port->sys_size)) + continue; rio_unlock_device(port, destid, hopcount); if (rio_disc_peer (net, port, ndestid, hopcount + 1) < 0) @@ -1108,8 +1088,7 @@ static void rio_update_route_tables(struct rio_mport *port) if (rswitch->destid == destid) continue; - sport = rio_get_swpinfo_inport(port, - rswitch->destid, rswitch->hopcount); + sport = RIO_GET_PORT_NUM(rswitch->rdev->swpinfo); if (rswitch->add_entry) { rio_route_add_entry(port, rswitch, @@ -1184,7 +1163,11 @@ int __devinit rio_enum_mport(struct rio_mport *mport) /* Enable Input Output Port (transmitter reviever) */ rio_enable_rx_tx_port(mport, 1, 0, 0, 0); - if (rio_enum_peer(net, mport, 0) < 0) { + /* Set component tag for host */ + rio_local_write_config_32(mport, RIO_COMPONENT_TAG_CSR, + next_comptag++); + + if (rio_enum_peer(net, mport, 0, NULL, 0) < 0) { /* A higher priority host won enumeration, bail. */ printk(KERN_INFO "RIO: master port %d device has lost enumeration to a remote host\n", diff --git a/drivers/rapidio/rio-sysfs.c b/drivers/rapidio/rio-sysfs.c index 00b47565835..137ed93ee33 100644 --- a/drivers/rapidio/rio-sysfs.c +++ b/drivers/rapidio/rio-sysfs.c @@ -40,9 +40,6 @@ static ssize_t routes_show(struct device *dev, struct device_attribute *attr, ch char *str = buf; int i; - if (!rdev->rswitch) - goto out; - for (i = 0; i < RIO_MAX_ROUTE_ENTRIES(rdev->net->hport->sys_size); i++) { if (rdev->rswitch->route_table[i] == RIO_INVALID_ROUTE) @@ -52,7 +49,6 @@ static ssize_t routes_show(struct device *dev, struct device_attribute *attr, ch rdev->rswitch->route_table[i]); } - out: return (str - buf); } @@ -63,10 +59,11 @@ struct device_attribute rio_dev_attrs[] = { __ATTR_RO(asm_did), __ATTR_RO(asm_vid), __ATTR_RO(asm_rev), - __ATTR_RO(routes), __ATTR_NULL, }; +static DEVICE_ATTR(routes, S_IRUGO, routes_show, NULL); + static ssize_t rio_read_config(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, @@ -218,7 +215,17 @@ int rio_create_sysfs_dev_files(struct rio_dev *rdev) { int err = 0; - err = sysfs_create_bin_file(&rdev->dev.kobj, &rio_config_attr); + err = device_create_bin_file(&rdev->dev, &rio_config_attr); + + if (!err && rdev->rswitch) { + err = device_create_file(&rdev->dev, &dev_attr_routes); + if (!err && rdev->rswitch->sw_sysfs) + err = rdev->rswitch->sw_sysfs(rdev, RIO_SW_SYSFS_CREATE); + } + + if (err) + pr_warning("RIO: Failed to create attribute file(s) for %s\n", + rio_name(rdev)); return err; } @@ -231,5 +238,10 @@ int rio_create_sysfs_dev_files(struct rio_dev *rdev) */ void rio_remove_sysfs_dev_files(struct rio_dev *rdev) { - sysfs_remove_bin_file(&rdev->dev.kobj, &rio_config_attr); + device_remove_bin_file(&rdev->dev, &rio_config_attr); + if (rdev->rswitch) { + device_remove_file(&rdev->dev, &dev_attr_routes); + if (rdev->rswitch->sw_sysfs) + rdev->rswitch->sw_sysfs(rdev, RIO_SW_SYSFS_REMOVE); + } } diff --git a/drivers/rapidio/rio.c b/drivers/rapidio/rio.c index 74e9d22d95f..68cf0c99138 100644 --- a/drivers/rapidio/rio.c +++ b/drivers/rapidio/rio.c @@ -443,7 +443,7 @@ rio_mport_get_physefb(struct rio_mport *port, int local, * @from is not %NULL, searches continue from next device on the global * list. */ -static struct rio_dev *rio_get_comptag(u32 comp_tag, struct rio_dev *from) +struct rio_dev *rio_get_comptag(u32 comp_tag, struct rio_dev *from) { struct list_head *n; struct rio_dev *rdev; @@ -495,6 +495,232 @@ int rio_set_port_lockout(struct rio_dev *rdev, u32 pnum, int lock) } /** + * rio_chk_dev_route - Validate route to the specified device. + * @rdev: RIO device failed to respond + * @nrdev: Last active device on the route to rdev + * @npnum: nrdev's port number on the route to rdev + * + * Follows a route to the specified RIO device to determine the last available + * device (and corresponding RIO port) on the route. + */ +static int +rio_chk_dev_route(struct rio_dev *rdev, struct rio_dev **nrdev, int *npnum) +{ + u32 result; + int p_port, dstid, rc = -EIO; + struct rio_dev *prev = NULL; + + /* Find switch with failed RIO link */ + while (rdev->prev && (rdev->prev->pef & RIO_PEF_SWITCH)) { + if (!rio_read_config_32(rdev->prev, RIO_DEV_ID_CAR, &result)) { + prev = rdev->prev; + break; + } + rdev = rdev->prev; + } + + if (prev == NULL) + goto err_out; + + dstid = (rdev->pef & RIO_PEF_SWITCH) ? + rdev->rswitch->destid : rdev->destid; + p_port = prev->rswitch->route_table[dstid]; + + if (p_port != RIO_INVALID_ROUTE) { + pr_debug("RIO: link failed on [%s]-P%d\n", + rio_name(prev), p_port); + *nrdev = prev; + *npnum = p_port; + rc = 0; + } else + pr_debug("RIO: failed to trace route to %s\n", rio_name(rdev)); +err_out: + return rc; +} + +/** + * rio_mport_chk_dev_access - Validate access to the specified device. + * @mport: Master port to send transactions + * @destid: Device destination ID in network + * @hopcount: Number of hops into the network + */ +int +rio_mport_chk_dev_access(struct rio_mport *mport, u16 destid, u8 hopcount) +{ + int i = 0; + u32 tmp; + + while (rio_mport_read_config_32(mport, destid, hopcount, + RIO_DEV_ID_CAR, &tmp)) { + i++; + if (i == RIO_MAX_CHK_RETRY) + return -EIO; + mdelay(1); + } + + return 0; +} + +/** + * rio_chk_dev_access - Validate access to the specified device. + * @rdev: Pointer to RIO device control structure + */ +static int rio_chk_dev_access(struct rio_dev *rdev) +{ + u8 hopcount = 0xff; + u16 destid = rdev->destid; + + if (rdev->rswitch) { + destid = rdev->rswitch->destid; + hopcount = rdev->rswitch->hopcount; + } + + return rio_mport_chk_dev_access(rdev->net->hport, destid, hopcount); +} + +/** + * rio_get_input_status - Sends a Link-Request/Input-Status control symbol and + * returns link-response (if requested). + * @rdev: RIO devive to issue Input-status command + * @pnum: Device port number to issue the command + * @lnkresp: Response from a link partner + */ +static int +rio_get_input_status(struct rio_dev *rdev, int pnum, u32 *lnkresp) +{ + struct rio_mport *mport = rdev->net->hport; + u16 destid = rdev->rswitch->destid; + u8 hopcount = rdev->rswitch->hopcount; + u32 regval; + int checkcount; + + if (lnkresp) { + /* Read from link maintenance response register + * to clear valid bit */ + rio_mport_read_config_32(mport, destid, hopcount, + rdev->phys_efptr + RIO_PORT_N_MNT_RSP_CSR(pnum), + ®val); + udelay(50); + } + + /* Issue Input-status command */ + rio_mport_write_config_32(mport, destid, hopcount, + rdev->phys_efptr + RIO_PORT_N_MNT_REQ_CSR(pnum), + RIO_MNT_REQ_CMD_IS); + + /* Exit if the response is not expected */ + if (lnkresp == NULL) + return 0; + + checkcount = 3; + while (checkcount--) { + udelay(50); + rio_mport_read_config_32(mport, destid, hopcount, + rdev->phys_efptr + RIO_PORT_N_MNT_RSP_CSR(pnum), + ®val); + if (regval & RIO_PORT_N_MNT_RSP_RVAL) { + *lnkresp = regval; + return 0; + } + } + + return -EIO; +} + +/** + * rio_clr_err_stopped - Clears port Error-stopped states. + * @rdev: Pointer to RIO device control structure + * @pnum: Switch port number to clear errors + * @err_status: port error status (if 0 reads register from device) + */ +static int rio_clr_err_stopped(struct rio_dev *rdev, u32 pnum, u32 err_status) +{ + struct rio_mport *mport = rdev->net->hport; + u16 destid = rdev->rswitch->destid; + u8 hopcount = rdev->rswitch->hopcount; + struct rio_dev *nextdev = rdev->rswitch->nextdev[pnum]; + u32 regval; + u32 far_ackid, far_linkstat, near_ackid; + + if (err_status == 0) + rio_mport_read_config_32(mport, destid, hopcount, + rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(pnum), + &err_status); + + if (err_status & RIO_PORT_N_ERR_STS_PW_OUT_ES) { + pr_debug("RIO_EM: servicing Output Error-Stopped state\n"); + /* + * Send a Link-Request/Input-Status control symbol + */ + if (rio_get_input_status(rdev, pnum, ®val)) { + pr_debug("RIO_EM: Input-status response timeout\n"); + goto rd_err; + } + + pr_debug("RIO_EM: SP%d Input-status response=0x%08x\n", + pnum, regval); + far_ackid = (regval & RIO_PORT_N_MNT_RSP_ASTAT) >> 5; + far_linkstat = regval & RIO_PORT_N_MNT_RSP_LSTAT; + rio_mport_read_config_32(mport, destid, hopcount, + rdev->phys_efptr + RIO_PORT_N_ACK_STS_CSR(pnum), + ®val); + pr_debug("RIO_EM: SP%d_ACK_STS_CSR=0x%08x\n", pnum, regval); + near_ackid = (regval & RIO_PORT_N_ACK_INBOUND) >> 24; + pr_debug("RIO_EM: SP%d far_ackID=0x%02x far_linkstat=0x%02x" \ + " near_ackID=0x%02x\n", + pnum, far_ackid, far_linkstat, near_ackid); + + /* + * If required, synchronize ackIDs of near and + * far sides. + */ + if ((far_ackid != ((regval & RIO_PORT_N_ACK_OUTSTAND) >> 8)) || + (far_ackid != (regval & RIO_PORT_N_ACK_OUTBOUND))) { + /* Align near outstanding/outbound ackIDs with + * far inbound. + */ + rio_mport_write_config_32(mport, destid, + hopcount, rdev->phys_efptr + + RIO_PORT_N_ACK_STS_CSR(pnum), + (near_ackid << 24) | + (far_ackid << 8) | far_ackid); + /* Align far outstanding/outbound ackIDs with + * near inbound. + */ + far_ackid++; + if (nextdev) + rio_write_config_32(nextdev, + nextdev->phys_efptr + + RIO_PORT_N_ACK_STS_CSR(RIO_GET_PORT_NUM(nextdev->swpinfo)), + (far_ackid << 24) | + (near_ackid << 8) | near_ackid); + else + pr_debug("RIO_EM: Invalid nextdev pointer (NULL)\n"); + } +rd_err: + rio_mport_read_config_32(mport, destid, hopcount, + rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(pnum), + &err_status); + pr_debug("RIO_EM: SP%d_ERR_STS_CSR=0x%08x\n", pnum, err_status); + } + + if ((err_status & RIO_PORT_N_ERR_STS_PW_INP_ES) && nextdev) { + pr_debug("RIO_EM: servicing Input Error-Stopped state\n"); + rio_get_input_status(nextdev, + RIO_GET_PORT_NUM(nextdev->swpinfo), NULL); + udelay(50); + + rio_mport_read_config_32(mport, destid, hopcount, + rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(pnum), + &err_status); + pr_debug("RIO_EM: SP%d_ERR_STS_CSR=0x%08x\n", pnum, err_status); + } + + return (err_status & (RIO_PORT_N_ERR_STS_PW_OUT_ES | + RIO_PORT_N_ERR_STS_PW_INP_ES)) ? 1 : 0; +} + +/** * rio_inb_pwrite_handler - process inbound port-write message * @pw_msg: pointer to inbound port-write message * @@ -507,13 +733,13 @@ int rio_inb_pwrite_handler(union rio_pw_msg *pw_msg) struct rio_mport *mport; u8 hopcount; u16 destid; - u32 err_status; + u32 err_status, em_perrdet, em_ltlerrdet; int rc, portnum; rdev = rio_get_comptag(pw_msg->em.comptag, NULL); if (rdev == NULL) { - /* Someting bad here (probably enumeration error) */ - pr_err("RIO: %s No matching device for CTag 0x%08x\n", + /* Device removed or enumeration error */ + pr_debug("RIO: %s No matching device for CTag 0x%08x\n", __func__, pw_msg->em.comptag); return -EIO; } @@ -524,12 +750,11 @@ int rio_inb_pwrite_handler(union rio_pw_msg *pw_msg) { u32 i; for (i = 0; i < RIO_PW_MSG_SIZE/sizeof(u32);) { - pr_debug("0x%02x: %08x %08x %08x %08x", + pr_debug("0x%02x: %08x %08x %08x %08x\n", i*4, pw_msg->raw[i], pw_msg->raw[i + 1], pw_msg->raw[i + 2], pw_msg->raw[i + 3]); i += 4; } - pr_debug("\n"); } #endif @@ -545,6 +770,26 @@ int rio_inb_pwrite_handler(union rio_pw_msg *pw_msg) return 0; } + portnum = pw_msg->em.is_port & 0xFF; + + /* Check if device and route to it are functional: + * Sometimes devices may send PW message(s) just before being + * powered down (or link being lost). + */ + if (rio_chk_dev_access(rdev)) { + pr_debug("RIO: device access failed - get link partner\n"); + /* Scan route to the device and identify failed link. + * This will replace device and port reported in PW message. + * PW message should not be used after this point. + */ + if (rio_chk_dev_route(rdev, &rdev, &portnum)) { + pr_err("RIO: Route trace for %s failed\n", + rio_name(rdev)); + return -EIO; + } + pw_msg = NULL; + } + /* For End-point devices processing stops here */ if (!(rdev->pef & RIO_PEF_SWITCH)) return 0; @@ -562,9 +807,6 @@ int rio_inb_pwrite_handler(union rio_pw_msg *pw_msg) /* * Process the port-write notification from switch */ - - portnum = pw_msg->em.is_port & 0xFF; - if (rdev->rswitch->em_handle) rdev->rswitch->em_handle(rdev, portnum); @@ -573,29 +815,28 @@ int rio_inb_pwrite_handler(union rio_pw_msg *pw_msg) &err_status); pr_debug("RIO_PW: SP%d_ERR_STS_CSR=0x%08x\n", portnum, err_status); - if (pw_msg->em.errdetect) { - pr_debug("RIO_PW: RIO_EM_P%d_ERR_DETECT=0x%08x\n", - portnum, pw_msg->em.errdetect); - /* Clear EM Port N Error Detect CSR */ - rio_mport_write_config_32(mport, destid, hopcount, - rdev->em_efptr + RIO_EM_PN_ERR_DETECT(portnum), 0); - } + if (err_status & RIO_PORT_N_ERR_STS_PORT_OK) { - if (pw_msg->em.ltlerrdet) { - pr_debug("RIO_PW: RIO_EM_LTL_ERR_DETECT=0x%08x\n", - pw_msg->em.ltlerrdet); - /* Clear EM L/T Layer Error Detect CSR */ - rio_mport_write_config_32(mport, destid, hopcount, - rdev->em_efptr + RIO_EM_LTL_ERR_DETECT, 0); - } + if (!(rdev->rswitch->port_ok & (1 << portnum))) { + rdev->rswitch->port_ok |= (1 << portnum); + rio_set_port_lockout(rdev, portnum, 0); + /* Schedule Insertion Service */ + pr_debug("RIO_PW: Device Insertion on [%s]-P%d\n", + rio_name(rdev), portnum); + } - /* Clear Port Errors */ - rio_mport_write_config_32(mport, destid, hopcount, - rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(portnum), - err_status & RIO_PORT_N_ERR_STS_CLR_MASK); + /* Clear error-stopped states (if reported). + * Depending on the link partner state, two attempts + * may be needed for successful recovery. + */ + if (err_status & (RIO_PORT_N_ERR_STS_PW_OUT_ES | + RIO_PORT_N_ERR_STS_PW_INP_ES)) { + if (rio_clr_err_stopped(rdev, portnum, err_status)) + rio_clr_err_stopped(rdev, portnum, 0); + } + } else { /* if (err_status & RIO_PORT_N_ERR_STS_PORT_UNINIT) */ - if (rdev->rswitch->port_ok & (1 << portnum)) { - if (err_status & RIO_PORT_N_ERR_STS_PORT_UNINIT) { + if (rdev->rswitch->port_ok & (1 << portnum)) { rdev->rswitch->port_ok &= ~(1 << portnum); rio_set_port_lockout(rdev, portnum, 1); @@ -608,21 +849,32 @@ int rio_inb_pwrite_handler(union rio_pw_msg *pw_msg) pr_debug("RIO_PW: Device Extraction on [%s]-P%d\n", rio_name(rdev), portnum); } - } else { - if (err_status & RIO_PORT_N_ERR_STS_PORT_OK) { - rdev->rswitch->port_ok |= (1 << portnum); - rio_set_port_lockout(rdev, portnum, 0); + } - /* Schedule Insertion Service */ - pr_debug("RIO_PW: Device Insertion on [%s]-P%d\n", - rio_name(rdev), portnum); - } + rio_mport_read_config_32(mport, destid, hopcount, + rdev->em_efptr + RIO_EM_PN_ERR_DETECT(portnum), &em_perrdet); + if (em_perrdet) { + pr_debug("RIO_PW: RIO_EM_P%d_ERR_DETECT=0x%08x\n", + portnum, em_perrdet); + /* Clear EM Port N Error Detect CSR */ + rio_mport_write_config_32(mport, destid, hopcount, + rdev->em_efptr + RIO_EM_PN_ERR_DETECT(portnum), 0); + } + + rio_mport_read_config_32(mport, destid, hopcount, + rdev->em_efptr + RIO_EM_LTL_ERR_DETECT, &em_ltlerrdet); + if (em_ltlerrdet) { + pr_debug("RIO_PW: RIO_EM_LTL_ERR_DETECT=0x%08x\n", + em_ltlerrdet); + /* Clear EM L/T Layer Error Detect CSR */ + rio_mport_write_config_32(mport, destid, hopcount, + rdev->em_efptr + RIO_EM_LTL_ERR_DETECT, 0); } - /* Clear Port-Write Pending bit */ + /* Clear remaining error bits and Port-Write Pending bit */ rio_mport_write_config_32(mport, destid, hopcount, rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(portnum), - RIO_PORT_N_ERR_STS_PW_PEND); + err_status); return 0; } diff --git a/drivers/rapidio/rio.h b/drivers/rapidio/rio.h index f27b7a9c47d..b1af414f15e 100644 --- a/drivers/rapidio/rio.h +++ b/drivers/rapidio/rio.h @@ -14,6 +14,8 @@ #include <linux/list.h> #include <linux/rio.h> +#define RIO_MAX_CHK_RETRY 3 + /* Functions internal to the RIO core code */ extern u32 rio_mport_get_feature(struct rio_mport *mport, int local, u16 destid, @@ -22,6 +24,8 @@ extern u32 rio_mport_get_physefb(struct rio_mport *port, int local, u16 destid, u8 hopcount); extern u32 rio_mport_get_efb(struct rio_mport *port, int local, u16 destid, u8 hopcount, u32 from); +extern int rio_mport_chk_dev_access(struct rio_mport *mport, u16 destid, + u8 hopcount); extern int rio_create_sysfs_dev_files(struct rio_dev *rdev); extern int rio_enum_mport(struct rio_mport *mport); extern int rio_disc_mport(struct rio_mport *mport); @@ -34,6 +38,7 @@ extern int rio_std_route_get_entry(struct rio_mport *mport, u16 destid, extern int rio_std_route_clr_table(struct rio_mport *mport, u16 destid, u8 hopcount, u16 table); extern int rio_set_port_lockout(struct rio_dev *rdev, u32 pnum, int lock); +extern struct rio_dev *rio_get_comptag(u32 comp_tag, struct rio_dev *from); /* Structures internal to the RIO core code */ extern struct device_attribute rio_dev_attrs[]; diff --git a/drivers/rapidio/switches/Kconfig b/drivers/rapidio/switches/Kconfig index 2b4e9b2b663..f47fee5d456 100644 --- a/drivers/rapidio/switches/Kconfig +++ b/drivers/rapidio/switches/Kconfig @@ -20,6 +20,13 @@ config RAPIDIO_TSI568 ---help--- Includes support for IDT Tsi568 serial RapidIO switch. +config RAPIDIO_CPS_GEN2 + bool "IDT CPS Gen.2 SRIO switch support" + depends on RAPIDIO + default n + ---help--- + Includes support for ITD CPS Gen.2 serial RapidIO switches. + config RAPIDIO_TSI500 bool "Tsi500 Parallel RapidIO switch support" depends on RAPIDIO diff --git a/drivers/rapidio/switches/Makefile b/drivers/rapidio/switches/Makefile index fe4adc3e8d5..48d67a6b98c 100644 --- a/drivers/rapidio/switches/Makefile +++ b/drivers/rapidio/switches/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_RAPIDIO_TSI57X) += tsi57x.o obj-$(CONFIG_RAPIDIO_CPS_XX) += idtcps.o obj-$(CONFIG_RAPIDIO_TSI568) += tsi568.o obj-$(CONFIG_RAPIDIO_TSI500) += tsi500.o +obj-$(CONFIG_RAPIDIO_CPS_GEN2) += idt_gen2.o ifeq ($(CONFIG_RAPIDIO_DEBUG),y) EXTRA_CFLAGS += -DDEBUG diff --git a/drivers/rapidio/switches/idt_gen2.c b/drivers/rapidio/switches/idt_gen2.c new file mode 100644 index 00000000000..0bb871cb5c4 --- /dev/null +++ b/drivers/rapidio/switches/idt_gen2.c @@ -0,0 +1,447 @@ +/* + * IDT CPS Gen.2 Serial RapidIO switch family support + * + * Copyright 2010 Integrated Device Technology, Inc. + * Alexandre Bounine <alexandre.bounine@idt.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/rio.h> +#include <linux/rio_drv.h> +#include <linux/rio_ids.h> +#include <linux/delay.h> +#include "../rio.h" + +#define LOCAL_RTE_CONF_DESTID_SEL 0x010070 +#define LOCAL_RTE_CONF_DESTID_SEL_PSEL 0x0000001f + +#define IDT_LT_ERR_REPORT_EN 0x03100c + +#define IDT_PORT_ERR_REPORT_EN(n) (0x031044 + (n)*0x40) +#define IDT_PORT_ERR_REPORT_EN_BC 0x03ff04 + +#define IDT_PORT_ISERR_REPORT_EN(n) (0x03104C + (n)*0x40) +#define IDT_PORT_ISERR_REPORT_EN_BC 0x03ff0c +#define IDT_PORT_INIT_TX_ACQUIRED 0x00000020 + +#define IDT_LANE_ERR_REPORT_EN(n) (0x038010 + (n)*0x100) +#define IDT_LANE_ERR_REPORT_EN_BC 0x03ff10 + +#define IDT_DEV_CTRL_1 0xf2000c +#define IDT_DEV_CTRL_1_GENPW 0x02000000 +#define IDT_DEV_CTRL_1_PRSTBEH 0x00000001 + +#define IDT_CFGBLK_ERR_CAPTURE_EN 0x020008 +#define IDT_CFGBLK_ERR_REPORT 0xf20014 +#define IDT_CFGBLK_ERR_REPORT_GENPW 0x00000002 + +#define IDT_AUX_PORT_ERR_CAP_EN 0x020000 +#define IDT_AUX_ERR_REPORT_EN 0xf20018 +#define IDT_AUX_PORT_ERR_LOG_I2C 0x00000002 +#define IDT_AUX_PORT_ERR_LOG_JTAG 0x00000001 + +#define IDT_ISLTL_ADDRESS_CAP 0x021014 + +#define IDT_RIO_DOMAIN 0xf20020 +#define IDT_RIO_DOMAIN_MASK 0x000000ff + +#define IDT_PW_INFO_CSR 0xf20024 + +#define IDT_SOFT_RESET 0xf20040 +#define IDT_SOFT_RESET_REQ 0x00030097 + +#define IDT_I2C_MCTRL 0xf20050 +#define IDT_I2C_MCTRL_GENPW 0x04000000 + +#define IDT_JTAG_CTRL 0xf2005c +#define IDT_JTAG_CTRL_GENPW 0x00000002 + +#define IDT_LANE_CTRL(n) (0xff8000 + (n)*0x100) +#define IDT_LANE_CTRL_BC 0xffff00 +#define IDT_LANE_CTRL_GENPW 0x00200000 +#define IDT_LANE_DFE_1_BC 0xffff18 +#define IDT_LANE_DFE_2_BC 0xffff1c + +#define IDT_PORT_OPS(n) (0xf40004 + (n)*0x100) +#define IDT_PORT_OPS_GENPW 0x08000000 +#define IDT_PORT_OPS_PL_ELOG 0x00000040 +#define IDT_PORT_OPS_LL_ELOG 0x00000020 +#define IDT_PORT_OPS_LT_ELOG 0x00000010 +#define IDT_PORT_OPS_BC 0xf4ff04 + +#define IDT_PORT_ISERR_DET(n) (0xf40008 + (n)*0x100) + +#define IDT_ERR_CAP 0xfd0000 +#define IDT_ERR_CAP_LOG_OVERWR 0x00000004 + +#define IDT_ERR_RD 0xfd0004 + +#define IDT_DEFAULT_ROUTE 0xde +#define IDT_NO_ROUTE 0xdf + +static int +idtg2_route_add_entry(struct rio_mport *mport, u16 destid, u8 hopcount, + u16 table, u16 route_destid, u8 route_port) +{ + /* + * Select routing table to update + */ + if (table == RIO_GLOBAL_TABLE) + table = 0; + else + table++; + + rio_mport_write_config_32(mport, destid, hopcount, + LOCAL_RTE_CONF_DESTID_SEL, table); + + /* + * Program destination port for the specified destID + */ + rio_mport_write_config_32(mport, destid, hopcount, + RIO_STD_RTE_CONF_DESTID_SEL_CSR, + (u32)route_destid); + + rio_mport_write_config_32(mport, destid, hopcount, + RIO_STD_RTE_CONF_PORT_SEL_CSR, + (u32)route_port); + udelay(10); + + return 0; +} + +static int +idtg2_route_get_entry(struct rio_mport *mport, u16 destid, u8 hopcount, + u16 table, u16 route_destid, u8 *route_port) +{ + u32 result; + + /* + * Select routing table to read + */ + if (table == RIO_GLOBAL_TABLE) + table = 0; + else + table++; + + rio_mport_write_config_32(mport, destid, hopcount, + LOCAL_RTE_CONF_DESTID_SEL, table); + + rio_mport_write_config_32(mport, destid, hopcount, + RIO_STD_RTE_CONF_DESTID_SEL_CSR, + route_destid); + + rio_mport_read_config_32(mport, destid, hopcount, + RIO_STD_RTE_CONF_PORT_SEL_CSR, &result); + + if (IDT_DEFAULT_ROUTE == (u8)result || IDT_NO_ROUTE == (u8)result) + *route_port = RIO_INVALID_ROUTE; + else + *route_port = (u8)result; + + return 0; +} + +static int +idtg2_route_clr_table(struct rio_mport *mport, u16 destid, u8 hopcount, + u16 table) +{ + u32 i; + + /* + * Select routing table to read + */ + if (table == RIO_GLOBAL_TABLE) + table = 0; + else + table++; + + rio_mport_write_config_32(mport, destid, hopcount, + LOCAL_RTE_CONF_DESTID_SEL, table); + + for (i = RIO_STD_RTE_CONF_EXTCFGEN; + i <= (RIO_STD_RTE_CONF_EXTCFGEN | 0xff);) { + rio_mport_write_config_32(mport, destid, hopcount, + RIO_STD_RTE_CONF_DESTID_SEL_CSR, i); + rio_mport_write_config_32(mport, destid, hopcount, + RIO_STD_RTE_CONF_PORT_SEL_CSR, + (IDT_DEFAULT_ROUTE << 24) | (IDT_DEFAULT_ROUTE << 16) | + (IDT_DEFAULT_ROUTE << 8) | IDT_DEFAULT_ROUTE); + i += 4; + } + + return 0; +} + + +static int +idtg2_set_domain(struct rio_mport *mport, u16 destid, u8 hopcount, + u8 sw_domain) +{ + /* + * Switch domain configuration operates only at global level + */ + rio_mport_write_config_32(mport, destid, hopcount, + IDT_RIO_DOMAIN, (u32)sw_domain); + return 0; +} + +static int +idtg2_get_domain(struct rio_mport *mport, u16 destid, u8 hopcount, + u8 *sw_domain) +{ + u32 regval; + + /* + * Switch domain configuration operates only at global level + */ + rio_mport_read_config_32(mport, destid, hopcount, + IDT_RIO_DOMAIN, ®val); + + *sw_domain = (u8)(regval & 0xff); + + return 0; +} + +static int +idtg2_em_init(struct rio_dev *rdev) +{ + struct rio_mport *mport = rdev->net->hport; + u16 destid = rdev->rswitch->destid; + u8 hopcount = rdev->rswitch->hopcount; + u32 regval; + int i, tmp; + + /* + * This routine performs device-specific initialization only. + * All standard EM configuration should be performed at upper level. + */ + + pr_debug("RIO: %s [%d:%d]\n", __func__, destid, hopcount); + + /* Set Port-Write info CSR: PRIO=3 and CRF=1 */ + rio_mport_write_config_32(mport, destid, hopcount, + IDT_PW_INFO_CSR, 0x0000e000); + + /* + * Configure LT LAYER error reporting. + */ + + /* Enable standard (RIO.p8) error reporting */ + rio_mport_write_config_32(mport, destid, hopcount, + IDT_LT_ERR_REPORT_EN, + REM_LTL_ERR_ILLTRAN | REM_LTL_ERR_UNSOLR | + REM_LTL_ERR_UNSUPTR); + + /* Use Port-Writes for LT layer error reporting. + * Enable per-port reset + */ + rio_mport_read_config_32(mport, destid, hopcount, + IDT_DEV_CTRL_1, ®val); + rio_mport_write_config_32(mport, destid, hopcount, + IDT_DEV_CTRL_1, + regval | IDT_DEV_CTRL_1_GENPW | IDT_DEV_CTRL_1_PRSTBEH); + + /* + * Configure PORT error reporting. + */ + + /* Report all RIO.p8 errors supported by device */ + rio_mport_write_config_32(mport, destid, hopcount, + IDT_PORT_ERR_REPORT_EN_BC, 0x807e8037); + + /* Configure reporting of implementation specific errors/events */ + rio_mport_write_config_32(mport, destid, hopcount, + IDT_PORT_ISERR_REPORT_EN_BC, IDT_PORT_INIT_TX_ACQUIRED); + + /* Use Port-Writes for port error reporting and enable error logging */ + tmp = RIO_GET_TOTAL_PORTS(rdev->swpinfo); + for (i = 0; i < tmp; i++) { + rio_mport_read_config_32(mport, destid, hopcount, + IDT_PORT_OPS(i), ®val); + rio_mport_write_config_32(mport, destid, hopcount, + IDT_PORT_OPS(i), regval | IDT_PORT_OPS_GENPW | + IDT_PORT_OPS_PL_ELOG | + IDT_PORT_OPS_LL_ELOG | + IDT_PORT_OPS_LT_ELOG); + } + /* Overwrite error log if full */ + rio_mport_write_config_32(mport, destid, hopcount, + IDT_ERR_CAP, IDT_ERR_CAP_LOG_OVERWR); + + /* + * Configure LANE error reporting. + */ + + /* Disable line error reporting */ + rio_mport_write_config_32(mport, destid, hopcount, + IDT_LANE_ERR_REPORT_EN_BC, 0); + + /* Use Port-Writes for lane error reporting (when enabled) + * (do per-lane update because lanes may have different configuration) + */ + tmp = (rdev->did == RIO_DID_IDTCPS1848) ? 48 : 16; + for (i = 0; i < tmp; i++) { + rio_mport_read_config_32(mport, destid, hopcount, + IDT_LANE_CTRL(i), ®val); + rio_mport_write_config_32(mport, destid, hopcount, + IDT_LANE_CTRL(i), regval | IDT_LANE_CTRL_GENPW); + } + + /* + * Configure AUX error reporting. + */ + + /* Disable JTAG and I2C Error capture */ + rio_mport_write_config_32(mport, destid, hopcount, + IDT_AUX_PORT_ERR_CAP_EN, 0); + + /* Disable JTAG and I2C Error reporting/logging */ + rio_mport_write_config_32(mport, destid, hopcount, + IDT_AUX_ERR_REPORT_EN, 0); + + /* Disable Port-Write notification from JTAG */ + rio_mport_write_config_32(mport, destid, hopcount, + IDT_JTAG_CTRL, 0); + + /* Disable Port-Write notification from I2C */ + rio_mport_read_config_32(mport, destid, hopcount, + IDT_I2C_MCTRL, ®val); + rio_mport_write_config_32(mport, destid, hopcount, + IDT_I2C_MCTRL, + regval & ~IDT_I2C_MCTRL_GENPW); + + /* + * Configure CFG_BLK error reporting. + */ + + /* Disable Configuration Block error capture */ + rio_mport_write_config_32(mport, destid, hopcount, + IDT_CFGBLK_ERR_CAPTURE_EN, 0); + + /* Disable Port-Writes for Configuration Block error reporting */ + rio_mport_read_config_32(mport, destid, hopcount, + IDT_CFGBLK_ERR_REPORT, ®val); + rio_mport_write_config_32(mport, destid, hopcount, + IDT_CFGBLK_ERR_REPORT, + regval & ~IDT_CFGBLK_ERR_REPORT_GENPW); + + /* set TVAL = ~50us */ + rio_mport_write_config_32(mport, destid, hopcount, + rdev->phys_efptr + RIO_PORT_LINKTO_CTL_CSR, 0x8e << 8); + + return 0; +} + +static int +idtg2_em_handler(struct rio_dev *rdev, u8 portnum) +{ + struct rio_mport *mport = rdev->net->hport; + u16 destid = rdev->rswitch->destid; + u8 hopcount = rdev->rswitch->hopcount; + u32 regval, em_perrdet, em_ltlerrdet; + + rio_mport_read_config_32(mport, destid, hopcount, + rdev->em_efptr + RIO_EM_LTL_ERR_DETECT, &em_ltlerrdet); + if (em_ltlerrdet) { + /* Service Logical/Transport Layer Error(s) */ + if (em_ltlerrdet & REM_LTL_ERR_IMPSPEC) { + /* Implementation specific error reported */ + rio_mport_read_config_32(mport, destid, hopcount, + IDT_ISLTL_ADDRESS_CAP, ®val); + + pr_debug("RIO: %s Implementation Specific LTL errors" \ + " 0x%x @(0x%x)\n", + rio_name(rdev), em_ltlerrdet, regval); + + /* Clear implementation specific address capture CSR */ + rio_mport_write_config_32(mport, destid, hopcount, + IDT_ISLTL_ADDRESS_CAP, 0); + + } + } + + rio_mport_read_config_32(mport, destid, hopcount, + rdev->em_efptr + RIO_EM_PN_ERR_DETECT(portnum), &em_perrdet); + if (em_perrdet) { + /* Service Port-Level Error(s) */ + if (em_perrdet & REM_PED_IMPL_SPEC) { + /* Implementation Specific port error reported */ + + /* Get IS errors reported */ + rio_mport_read_config_32(mport, destid, hopcount, + IDT_PORT_ISERR_DET(portnum), ®val); + + pr_debug("RIO: %s Implementation Specific Port" \ + " errors 0x%x\n", rio_name(rdev), regval); + + /* Clear all implementation specific events */ + rio_mport_write_config_32(mport, destid, hopcount, + IDT_PORT_ISERR_DET(portnum), 0); + } + } + + return 0; +} + +static ssize_t +idtg2_show_errlog(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct rio_dev *rdev = to_rio_dev(dev); + struct rio_mport *mport = rdev->net->hport; + u16 destid = rdev->rswitch->destid; + u8 hopcount = rdev->rswitch->hopcount; + ssize_t len = 0; + u32 regval; + + while (!rio_mport_read_config_32(mport, destid, hopcount, + IDT_ERR_RD, ®val)) { + if (!regval) /* 0 = end of log */ + break; + len += snprintf(buf + len, PAGE_SIZE - len, + "%08x\n", regval); + if (len >= (PAGE_SIZE - 10)) + break; + } + + return len; +} + +static DEVICE_ATTR(errlog, S_IRUGO, idtg2_show_errlog, NULL); + +static int idtg2_sysfs(struct rio_dev *rdev, int create) +{ + struct device *dev = &rdev->dev; + int err = 0; + + if (create == RIO_SW_SYSFS_CREATE) { + /* Initialize sysfs entries */ + err = device_create_file(dev, &dev_attr_errlog); + if (err) + dev_err(dev, "Unable create sysfs errlog file\n"); + } else + device_remove_file(dev, &dev_attr_errlog); + + return err; +} + +static int idtg2_switch_init(struct rio_dev *rdev, int do_enum) +{ + pr_debug("RIO: %s for %s\n", __func__, rio_name(rdev)); + rdev->rswitch->add_entry = idtg2_route_add_entry; + rdev->rswitch->get_entry = idtg2_route_get_entry; + rdev->rswitch->clr_table = idtg2_route_clr_table; + rdev->rswitch->set_domain = idtg2_set_domain; + rdev->rswitch->get_domain = idtg2_get_domain; + rdev->rswitch->em_init = idtg2_em_init; + rdev->rswitch->em_handle = idtg2_em_handler; + rdev->rswitch->sw_sysfs = idtg2_sysfs; + + return 0; +} + +DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTCPS1848, idtg2_switch_init); +DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTCPS1616, idtg2_switch_init); diff --git a/drivers/rapidio/switches/idtcps.c b/drivers/rapidio/switches/idtcps.c index 2c790c144f8..fc9f6374f75 100644 --- a/drivers/rapidio/switches/idtcps.c +++ b/drivers/rapidio/switches/idtcps.c @@ -117,6 +117,10 @@ idtcps_get_domain(struct rio_mport *mport, u16 destid, u8 hopcount, static int idtcps_switch_init(struct rio_dev *rdev, int do_enum) { + struct rio_mport *mport = rdev->net->hport; + u16 destid = rdev->rswitch->destid; + u8 hopcount = rdev->rswitch->hopcount; + pr_debug("RIO: %s for %s\n", __func__, rio_name(rdev)); rdev->rswitch->add_entry = idtcps_route_add_entry; rdev->rswitch->get_entry = idtcps_route_get_entry; @@ -126,6 +130,12 @@ static int idtcps_switch_init(struct rio_dev *rdev, int do_enum) rdev->rswitch->em_init = NULL; rdev->rswitch->em_handle = NULL; + if (do_enum) { + /* set TVAL = ~50us */ + rio_mport_write_config_32(mport, destid, hopcount, + rdev->phys_efptr + RIO_PORT_LINKTO_CTL_CSR, 0x8e << 8); + } + return 0; } diff --git a/drivers/rapidio/switches/tsi568.c b/drivers/rapidio/switches/tsi568.c index f7fd7898606..b9a389b9f81 100644 --- a/drivers/rapidio/switches/tsi568.c +++ b/drivers/rapidio/switches/tsi568.c @@ -29,7 +29,7 @@ #define SPP_ROUTE_CFG_DESTID(n) (0x11070 + 0x100*n) #define SPP_ROUTE_CFG_PORT(n) (0x11074 + 0x100*n) -#define TSI568_SP_MODE_BC 0x10004 +#define TSI568_SP_MODE(n) (0x11004 + 0x100*n) #define TSI568_SP_MODE_PW_DIS 0x08000000 static int @@ -117,14 +117,19 @@ tsi568_em_init(struct rio_dev *rdev) u16 destid = rdev->rswitch->destid; u8 hopcount = rdev->rswitch->hopcount; u32 regval; + int portnum; pr_debug("TSI568 %s [%d:%d]\n", __func__, destid, hopcount); /* Make sure that Port-Writes are disabled (for all ports) */ - rio_mport_read_config_32(mport, destid, hopcount, - TSI568_SP_MODE_BC, ®val); - rio_mport_write_config_32(mport, destid, hopcount, - TSI568_SP_MODE_BC, regval | TSI568_SP_MODE_PW_DIS); + for (portnum = 0; + portnum < RIO_GET_TOTAL_PORTS(rdev->swpinfo); portnum++) { + rio_mport_read_config_32(mport, destid, hopcount, + TSI568_SP_MODE(portnum), ®val); + rio_mport_write_config_32(mport, destid, hopcount, + TSI568_SP_MODE(portnum), + regval | TSI568_SP_MODE_PW_DIS); + } return 0; } diff --git a/drivers/rapidio/switches/tsi57x.c b/drivers/rapidio/switches/tsi57x.c index d34df722d95..2003fb63c40 100644 --- a/drivers/rapidio/switches/tsi57x.c +++ b/drivers/rapidio/switches/tsi57x.c @@ -166,7 +166,8 @@ tsi57x_em_init(struct rio_dev *rdev) pr_debug("TSI578 %s [%d:%d]\n", __func__, destid, hopcount); - for (portnum = 0; portnum < 16; portnum++) { + for (portnum = 0; + portnum < RIO_GET_TOTAL_PORTS(rdev->swpinfo); portnum++) { /* Make sure that Port-Writes are enabled (for all ports) */ rio_mport_read_config_32(mport, destid, hopcount, TSI578_SP_MODE(portnum), ®val); @@ -205,6 +206,10 @@ tsi57x_em_init(struct rio_dev *rdev) portnum++; } + /* set TVAL = ~50us */ + rio_mport_write_config_32(mport, destid, hopcount, + rdev->phys_efptr + RIO_PORT_LINKTO_CTL_CSR, 0x9a << 8); + return 0; } diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 2785a0f16c9..6a77437d4f5 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -171,7 +171,8 @@ config RTC_DRV_DS3232 depends on RTC_CLASS && I2C help If you say yes here you get support for Dallas Semiconductor - DS3232 real-time clock chips. + DS3232 real-time clock chips. If an interrupt is associated + with the device, the alarm functionality is supported. This driver can also be built as a module. If so, the module will be called rtc-ds3232. @@ -952,4 +953,13 @@ config RTC_DRV_JZ4740 This driver can also be buillt as a module. If so, the module will be called rtc-jz4740. +config RTC_DRV_LPC32XX + depends on ARCH_LPC32XX + tristate "NXP LPC32XX RTC" + help + This enables support for the NXP RTC in the LPC32XX + + This driver can also be buillt as a module. If so, the module + will be called rtc-lpc32xx. + endif # RTC_CLASS diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 0f207b3b583..7a7cb3228a1 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -51,6 +51,7 @@ obj-$(CONFIG_RTC_DRV_IMXDI) += rtc-imxdi.o obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o obj-$(CONFIG_RTC_DRV_ISL12022) += rtc-isl12022.o obj-$(CONFIG_RTC_DRV_JZ4740) += rtc-jz4740.o +obj-$(CONFIG_RTC_DRV_LPC32XX) += rtc-lpc32xx.o obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o obj-$(CONFIG_RTC_DRV_M48T35) += rtc-m48t35.o diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index 565562ba6ac..e6539cbabb3 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -158,8 +158,10 @@ struct rtc_device *rtc_device_register(const char *name, struct device *dev, rtc_dev_prepare(rtc); err = device_register(&rtc->dev); - if (err) + if (err) { + put_device(&rtc->dev); goto exit_kfree; + } rtc_dev_add_device(rtc); rtc_sysfs_add_device(rtc); diff --git a/drivers/rtc/rtc-bfin.c b/drivers/rtc/rtc-bfin.c index d4fb82d85e9..b4b6087f223 100644 --- a/drivers/rtc/rtc-bfin.c +++ b/drivers/rtc/rtc-bfin.c @@ -2,7 +2,7 @@ * Blackfin On-Chip Real Time Clock Driver * Supports BF51x/BF52x/BF53[123]/BF53[467]/BF54x * - * Copyright 2004-2009 Analog Devices Inc. + * Copyright 2004-2010 Analog Devices Inc. * * Enter bugs at http://blackfin.uclinux.org/ * @@ -183,29 +183,33 @@ static irqreturn_t bfin_rtc_interrupt(int irq, void *dev_id) struct bfin_rtc *rtc = dev_get_drvdata(dev); unsigned long events = 0; bool write_complete = false; - u16 rtc_istat, rtc_ictl; + u16 rtc_istat, rtc_istat_clear, rtc_ictl, bits; dev_dbg_stamp(dev); rtc_istat = bfin_read_RTC_ISTAT(); rtc_ictl = bfin_read_RTC_ICTL(); + rtc_istat_clear = 0; - if (rtc_istat & RTC_ISTAT_WRITE_COMPLETE) { - bfin_write_RTC_ISTAT(RTC_ISTAT_WRITE_COMPLETE); + bits = RTC_ISTAT_WRITE_COMPLETE; + if (rtc_istat & bits) { + rtc_istat_clear |= bits; write_complete = true; complete(&bfin_write_complete); } - if (rtc_ictl & (RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY)) { - if (rtc_istat & (RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY)) { - bfin_write_RTC_ISTAT(RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY); + bits = (RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY); + if (rtc_ictl & bits) { + if (rtc_istat & bits) { + rtc_istat_clear |= bits; events |= RTC_AF | RTC_IRQF; } } - if (rtc_ictl & RTC_ISTAT_SEC) { - if (rtc_istat & RTC_ISTAT_SEC) { - bfin_write_RTC_ISTAT(RTC_ISTAT_SEC); + bits = RTC_ISTAT_SEC; + if (rtc_ictl & bits) { + if (rtc_istat & bits) { + rtc_istat_clear |= bits; events |= RTC_UF | RTC_IRQF; } } @@ -213,9 +217,10 @@ static irqreturn_t bfin_rtc_interrupt(int irq, void *dev_id) if (events) rtc_update_irq(rtc->rtc_dev, 1, events); - if (write_complete || events) + if (write_complete || events) { + bfin_write_RTC_ISTAT(rtc_istat_clear); return IRQ_HANDLED; - else + } else return IRQ_NONE; } @@ -422,9 +427,13 @@ static int __devexit bfin_rtc_remove(struct platform_device *pdev) #ifdef CONFIG_PM static int bfin_rtc_suspend(struct platform_device *pdev, pm_message_t state) { - if (device_may_wakeup(&pdev->dev)) { + struct device *dev = &pdev->dev; + + dev_dbg_stamp(dev); + + if (device_may_wakeup(dev)) { enable_irq_wake(IRQ_RTC); - bfin_rtc_sync_pending(&pdev->dev); + bfin_rtc_sync_pending(dev); } else bfin_rtc_int_clear(0); @@ -433,7 +442,11 @@ static int bfin_rtc_suspend(struct platform_device *pdev, pm_message_t state) static int bfin_rtc_resume(struct platform_device *pdev) { - if (device_may_wakeup(&pdev->dev)) + struct device *dev = &pdev->dev; + + dev_dbg_stamp(dev); + + if (device_may_wakeup(dev)) disable_irq_wake(IRQ_RTC); /* diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c index 9de8516e353..57063552d3b 100644 --- a/drivers/rtc/rtc-ds3232.c +++ b/drivers/rtc/rtc-ds3232.c @@ -2,6 +2,7 @@ * RTC client/driver for the Maxim/Dallas DS3232 Real-Time Clock over I2C * * Copyright (C) 2009-2010 Freescale Semiconductor. + * Author: Jack Lan <jack.lan@freescale.com> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -175,6 +176,182 @@ static int ds3232_set_time(struct device *dev, struct rtc_time *time) DS3232_REG_SECONDS, 7, buf); } +/* + * DS3232 has two alarm, we only use alarm1 + * According to linux specification, only support one-shot alarm + * no periodic alarm mode + */ +static int ds3232_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ds3232 *ds3232 = i2c_get_clientdata(client); + int control, stat; + int ret; + u8 buf[4]; + + mutex_lock(&ds3232->mutex); + + ret = i2c_smbus_read_byte_data(client, DS3232_REG_SR); + if (ret < 0) + goto out; + stat = ret; + ret = i2c_smbus_read_byte_data(client, DS3232_REG_CR); + if (ret < 0) + goto out; + control = ret; + ret = i2c_smbus_read_i2c_block_data(client, DS3232_REG_ALARM1, 4, buf); + if (ret < 0) + goto out; + + alarm->time.tm_sec = bcd2bin(buf[0] & 0x7F); + alarm->time.tm_min = bcd2bin(buf[1] & 0x7F); + alarm->time.tm_hour = bcd2bin(buf[2] & 0x7F); + alarm->time.tm_mday = bcd2bin(buf[3] & 0x7F); + + alarm->time.tm_mon = -1; + alarm->time.tm_year = -1; + alarm->time.tm_wday = -1; + alarm->time.tm_yday = -1; + alarm->time.tm_isdst = -1; + + alarm->enabled = !!(control & DS3232_REG_CR_A1IE); + alarm->pending = !!(stat & DS3232_REG_SR_A1F); + + ret = 0; +out: + mutex_unlock(&ds3232->mutex); + return ret; +} + +/* + * linux rtc-module does not support wday alarm + * and only 24h time mode supported indeed + */ +static int ds3232_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ds3232 *ds3232 = i2c_get_clientdata(client); + int control, stat; + int ret; + u8 buf[4]; + + if (client->irq <= 0) + return -EINVAL; + + mutex_lock(&ds3232->mutex); + + buf[0] = bin2bcd(alarm->time.tm_sec); + buf[1] = bin2bcd(alarm->time.tm_min); + buf[2] = bin2bcd(alarm->time.tm_hour); + buf[3] = bin2bcd(alarm->time.tm_mday); + + /* clear alarm interrupt enable bit */ + ret = i2c_smbus_read_byte_data(client, DS3232_REG_CR); + if (ret < 0) + goto out; + control = ret; + control &= ~(DS3232_REG_CR_A1IE | DS3232_REG_CR_A2IE); + ret = i2c_smbus_write_byte_data(client, DS3232_REG_CR, control); + if (ret < 0) + goto out; + + /* clear any pending alarm flag */ + ret = i2c_smbus_read_byte_data(client, DS3232_REG_SR); + if (ret < 0) + goto out; + stat = ret; + stat &= ~(DS3232_REG_SR_A1F | DS3232_REG_SR_A2F); + ret = i2c_smbus_write_byte_data(client, DS3232_REG_SR, stat); + if (ret < 0) + goto out; + + ret = i2c_smbus_write_i2c_block_data(client, DS3232_REG_ALARM1, 4, buf); + + if (alarm->enabled) { + control |= DS3232_REG_CR_A1IE; + ret = i2c_smbus_write_byte_data(client, DS3232_REG_CR, control); + } +out: + mutex_unlock(&ds3232->mutex); + return ret; +} + +static void ds3232_update_alarm(struct i2c_client *client) +{ + struct ds3232 *ds3232 = i2c_get_clientdata(client); + int control; + int ret; + u8 buf[4]; + + mutex_lock(&ds3232->mutex); + + ret = i2c_smbus_read_i2c_block_data(client, DS3232_REG_ALARM1, 4, buf); + if (ret < 0) + goto unlock; + + buf[0] = bcd2bin(buf[0]) < 0 || (ds3232->rtc->irq_data & RTC_UF) ? + 0x80 : buf[0]; + buf[1] = bcd2bin(buf[1]) < 0 || (ds3232->rtc->irq_data & RTC_UF) ? + 0x80 : buf[1]; + buf[2] = bcd2bin(buf[2]) < 0 || (ds3232->rtc->irq_data & RTC_UF) ? + 0x80 : buf[2]; + buf[3] = bcd2bin(buf[3]) < 0 || (ds3232->rtc->irq_data & RTC_UF) ? + 0x80 : buf[3]; + + ret = i2c_smbus_write_i2c_block_data(client, DS3232_REG_ALARM1, 4, buf); + if (ret < 0) + goto unlock; + + control = i2c_smbus_read_byte_data(client, DS3232_REG_CR); + if (control < 0) + goto unlock; + + if (ds3232->rtc->irq_data & (RTC_AF | RTC_UF)) + /* enable alarm1 interrupt */ + control |= DS3232_REG_CR_A1IE; + else + /* disable alarm1 interrupt */ + control &= ~(DS3232_REG_CR_A1IE); + i2c_smbus_write_byte_data(client, DS3232_REG_CR, control); + +unlock: + mutex_unlock(&ds3232->mutex); +} + +static int ds3232_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ds3232 *ds3232 = i2c_get_clientdata(client); + + if (client->irq <= 0) + return -EINVAL; + + if (enabled) + ds3232->rtc->irq_data |= RTC_AF; + else + ds3232->rtc->irq_data &= ~RTC_AF; + + ds3232_update_alarm(client); + return 0; +} + +static int ds3232_update_irq_enable(struct device *dev, unsigned int enabled) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ds3232 *ds3232 = i2c_get_clientdata(client); + + if (client->irq <= 0) + return -EINVAL; + + if (enabled) + ds3232->rtc->irq_data |= RTC_UF; + else + ds3232->rtc->irq_data &= ~RTC_UF; + + ds3232_update_alarm(client); + return 0; +} + static irqreturn_t ds3232_irq(int irq, void *dev_id) { struct i2c_client *client = dev_id; @@ -222,6 +399,10 @@ unlock: static const struct rtc_class_ops ds3232_rtc_ops = { .read_time = ds3232_read_time, .set_time = ds3232_set_time, + .read_alarm = ds3232_read_alarm, + .set_alarm = ds3232_set_alarm, + .alarm_irq_enable = ds3232_alarm_irq_enable, + .update_irq_enable = ds3232_update_irq_enable, }; static int __devinit ds3232_probe(struct i2c_client *client, diff --git a/drivers/rtc/rtc-jz4740.c b/drivers/rtc/rtc-jz4740.c index 2619d57b91d..2e16f72c905 100644 --- a/drivers/rtc/rtc-jz4740.c +++ b/drivers/rtc/rtc-jz4740.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> + * Copyright (C) 2010, Paul Cercueil <paul@crapouillou.net> * JZ4740 SoC RTC driver * * This program is free software; you can redistribute it and/or modify it @@ -161,7 +162,8 @@ static int jz4740_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC_ALARM, secs); if (!ret) - ret = jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_AE, alrm->enabled); + ret = jz4740_rtc_ctrl_set_bits(rtc, + JZ_RTC_CTRL_AE | JZ_RTC_CTRL_AF_IRQ, alrm->enabled); return ret; } @@ -258,6 +260,8 @@ static int __devinit jz4740_rtc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, rtc); + device_init_wakeup(&pdev->dev, 1); + rtc->rtc = rtc_device_register(pdev->name, &pdev->dev, &jz4740_rtc_ops, THIS_MODULE); if (IS_ERR(rtc->rtc)) { @@ -318,12 +322,43 @@ static int __devexit jz4740_rtc_remove(struct platform_device *pdev) return 0; } + +#ifdef CONFIG_PM +static int jz4740_rtc_suspend(struct device *dev) +{ + struct jz4740_rtc *rtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(rtc->irq); + return 0; +} + +static int jz4740_rtc_resume(struct device *dev) +{ + struct jz4740_rtc *rtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(rtc->irq); + return 0; +} + +static const struct dev_pm_ops jz4740_pm_ops = { + .suspend = jz4740_rtc_suspend, + .resume = jz4740_rtc_resume, +}; +#define JZ4740_RTC_PM_OPS (&jz4740_pm_ops) + +#else +#define JZ4740_RTC_PM_OPS NULL +#endif /* CONFIG_PM */ + struct platform_driver jz4740_rtc_driver = { - .probe = jz4740_rtc_probe, - .remove = __devexit_p(jz4740_rtc_remove), - .driver = { - .name = "jz4740-rtc", + .probe = jz4740_rtc_probe, + .remove = __devexit_p(jz4740_rtc_remove), + .driver = { + .name = "jz4740-rtc", .owner = THIS_MODULE, + .pm = JZ4740_RTC_PM_OPS, }, }; diff --git a/drivers/rtc/rtc-lpc32xx.c b/drivers/rtc/rtc-lpc32xx.c new file mode 100644 index 00000000000..ec8701ce99f --- /dev/null +++ b/drivers/rtc/rtc-lpc32xx.c @@ -0,0 +1,414 @@ +/* + * Copyright (C) 2010 NXP Semiconductors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> +#include <linux/rtc.h> +#include <linux/slab.h> +#include <linux/io.h> + +/* + * Clock and Power control register offsets + */ +#define LPC32XX_RTC_UCOUNT 0x00 +#define LPC32XX_RTC_DCOUNT 0x04 +#define LPC32XX_RTC_MATCH0 0x08 +#define LPC32XX_RTC_MATCH1 0x0C +#define LPC32XX_RTC_CTRL 0x10 +#define LPC32XX_RTC_INTSTAT 0x14 +#define LPC32XX_RTC_KEY 0x18 +#define LPC32XX_RTC_SRAM 0x80 + +#define LPC32XX_RTC_CTRL_MATCH0 (1 << 0) +#define LPC32XX_RTC_CTRL_MATCH1 (1 << 1) +#define LPC32XX_RTC_CTRL_ONSW_MATCH0 (1 << 2) +#define LPC32XX_RTC_CTRL_ONSW_MATCH1 (1 << 3) +#define LPC32XX_RTC_CTRL_SW_RESET (1 << 4) +#define LPC32XX_RTC_CTRL_CNTR_DIS (1 << 6) +#define LPC32XX_RTC_CTRL_ONSW_FORCE_HI (1 << 7) + +#define LPC32XX_RTC_INTSTAT_MATCH0 (1 << 0) +#define LPC32XX_RTC_INTSTAT_MATCH1 (1 << 1) +#define LPC32XX_RTC_INTSTAT_ONSW (1 << 2) + +#define LPC32XX_RTC_KEY_ONSW_LOADVAL 0xB5C13F27 + +#define RTC_NAME "rtc-lpc32xx" + +#define rtc_readl(dev, reg) \ + __raw_readl((dev)->rtc_base + (reg)) +#define rtc_writel(dev, reg, val) \ + __raw_writel((val), (dev)->rtc_base + (reg)) + +struct lpc32xx_rtc { + void __iomem *rtc_base; + int irq; + unsigned char alarm_enabled; + struct rtc_device *rtc; + spinlock_t lock; +}; + +static int lpc32xx_rtc_read_time(struct device *dev, struct rtc_time *time) +{ + unsigned long elapsed_sec; + struct lpc32xx_rtc *rtc = dev_get_drvdata(dev); + + elapsed_sec = rtc_readl(rtc, LPC32XX_RTC_UCOUNT); + rtc_time_to_tm(elapsed_sec, time); + + return rtc_valid_tm(time); +} + +static int lpc32xx_rtc_set_mmss(struct device *dev, unsigned long secs) +{ + struct lpc32xx_rtc *rtc = dev_get_drvdata(dev); + u32 tmp; + + spin_lock_irq(&rtc->lock); + + /* RTC must be disabled during count update */ + tmp = rtc_readl(rtc, LPC32XX_RTC_CTRL); + rtc_writel(rtc, LPC32XX_RTC_CTRL, tmp | LPC32XX_RTC_CTRL_CNTR_DIS); + rtc_writel(rtc, LPC32XX_RTC_UCOUNT, secs); + rtc_writel(rtc, LPC32XX_RTC_DCOUNT, 0xFFFFFFFF - secs); + rtc_writel(rtc, LPC32XX_RTC_CTRL, tmp &= ~LPC32XX_RTC_CTRL_CNTR_DIS); + + spin_unlock_irq(&rtc->lock); + + return 0; +} + +static int lpc32xx_rtc_read_alarm(struct device *dev, + struct rtc_wkalrm *wkalrm) +{ + struct lpc32xx_rtc *rtc = dev_get_drvdata(dev); + + rtc_time_to_tm(rtc_readl(rtc, LPC32XX_RTC_MATCH0), &wkalrm->time); + wkalrm->enabled = rtc->alarm_enabled; + wkalrm->pending = !!(rtc_readl(rtc, LPC32XX_RTC_INTSTAT) & + LPC32XX_RTC_INTSTAT_MATCH0); + + return rtc_valid_tm(&wkalrm->time); +} + +static int lpc32xx_rtc_set_alarm(struct device *dev, + struct rtc_wkalrm *wkalrm) +{ + struct lpc32xx_rtc *rtc = dev_get_drvdata(dev); + unsigned long alarmsecs; + u32 tmp; + int ret; + + ret = rtc_tm_to_time(&wkalrm->time, &alarmsecs); + if (ret < 0) { + dev_warn(dev, "Failed to convert time: %d\n", ret); + return ret; + } + + spin_lock_irq(&rtc->lock); + + /* Disable alarm during update */ + tmp = rtc_readl(rtc, LPC32XX_RTC_CTRL); + rtc_writel(rtc, LPC32XX_RTC_CTRL, tmp & ~LPC32XX_RTC_CTRL_MATCH0); + + rtc_writel(rtc, LPC32XX_RTC_MATCH0, alarmsecs); + + rtc->alarm_enabled = wkalrm->enabled; + if (wkalrm->enabled) { + rtc_writel(rtc, LPC32XX_RTC_INTSTAT, + LPC32XX_RTC_INTSTAT_MATCH0); + rtc_writel(rtc, LPC32XX_RTC_CTRL, tmp | + LPC32XX_RTC_CTRL_MATCH0); + } + + spin_unlock_irq(&rtc->lock); + + return 0; +} + +static int lpc32xx_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct lpc32xx_rtc *rtc = dev_get_drvdata(dev); + u32 tmp; + + spin_lock_irq(&rtc->lock); + tmp = rtc_readl(rtc, LPC32XX_RTC_CTRL); + + if (enabled) { + rtc->alarm_enabled = 1; + tmp |= LPC32XX_RTC_CTRL_MATCH0; + } else { + rtc->alarm_enabled = 0; + tmp &= ~LPC32XX_RTC_CTRL_MATCH0; + } + + rtc_writel(rtc, LPC32XX_RTC_CTRL, tmp); + spin_unlock_irq(&rtc->lock); + + return 0; +} + +static irqreturn_t lpc32xx_rtc_alarm_interrupt(int irq, void *dev) +{ + struct lpc32xx_rtc *rtc = dev; + + spin_lock(&rtc->lock); + + /* Disable alarm interrupt */ + rtc_writel(rtc, LPC32XX_RTC_CTRL, + rtc_readl(rtc, LPC32XX_RTC_CTRL) & + ~LPC32XX_RTC_CTRL_MATCH0); + rtc->alarm_enabled = 0; + + /* + * Write a large value to the match value so the RTC won't + * keep firing the match status + */ + rtc_writel(rtc, LPC32XX_RTC_MATCH0, 0xFFFFFFFF); + rtc_writel(rtc, LPC32XX_RTC_INTSTAT, LPC32XX_RTC_INTSTAT_MATCH0); + + spin_unlock(&rtc->lock); + + rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF); + + return IRQ_HANDLED; +} + +static const struct rtc_class_ops lpc32xx_rtc_ops = { + .read_time = lpc32xx_rtc_read_time, + .set_mmss = lpc32xx_rtc_set_mmss, + .read_alarm = lpc32xx_rtc_read_alarm, + .set_alarm = lpc32xx_rtc_set_alarm, + .alarm_irq_enable = lpc32xx_rtc_alarm_irq_enable, +}; + +static int __devinit lpc32xx_rtc_probe(struct platform_device *pdev) +{ + struct resource *res; + struct lpc32xx_rtc *rtc; + resource_size_t size; + int rtcirq; + u32 tmp; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Can't get memory resource\n"); + return -ENOENT; + } + + rtcirq = platform_get_irq(pdev, 0); + if (rtcirq < 0 || rtcirq >= NR_IRQS) { + dev_warn(&pdev->dev, "Can't get interrupt resource\n"); + rtcirq = -1; + } + + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); + if (unlikely(!rtc)) { + dev_err(&pdev->dev, "Can't allocate memory\n"); + return -ENOMEM; + } + rtc->irq = rtcirq; + + size = resource_size(res); + + if (!devm_request_mem_region(&pdev->dev, res->start, size, + pdev->name)) { + dev_err(&pdev->dev, "RTC registers are not free\n"); + return -EBUSY; + } + + rtc->rtc_base = devm_ioremap(&pdev->dev, res->start, size); + if (!rtc->rtc_base) { + dev_err(&pdev->dev, "Can't map memory\n"); + return -ENOMEM; + } + + spin_lock_init(&rtc->lock); + + /* + * The RTC is on a seperate power domain and can keep it's state + * across a chip power cycle. If the RTC has never been previously + * setup, then set it up now for the first time. + */ + tmp = rtc_readl(rtc, LPC32XX_RTC_CTRL); + if (rtc_readl(rtc, LPC32XX_RTC_KEY) != LPC32XX_RTC_KEY_ONSW_LOADVAL) { + tmp &= ~(LPC32XX_RTC_CTRL_SW_RESET | + LPC32XX_RTC_CTRL_CNTR_DIS | + LPC32XX_RTC_CTRL_MATCH0 | + LPC32XX_RTC_CTRL_MATCH1 | + LPC32XX_RTC_CTRL_ONSW_MATCH0 | + LPC32XX_RTC_CTRL_ONSW_MATCH1 | + LPC32XX_RTC_CTRL_ONSW_FORCE_HI); + rtc_writel(rtc, LPC32XX_RTC_CTRL, tmp); + + /* Clear latched interrupt states */ + rtc_writel(rtc, LPC32XX_RTC_MATCH0, 0xFFFFFFFF); + rtc_writel(rtc, LPC32XX_RTC_INTSTAT, + LPC32XX_RTC_INTSTAT_MATCH0 | + LPC32XX_RTC_INTSTAT_MATCH1 | + LPC32XX_RTC_INTSTAT_ONSW); + + /* Write key value to RTC so it won't reload on reset */ + rtc_writel(rtc, LPC32XX_RTC_KEY, + LPC32XX_RTC_KEY_ONSW_LOADVAL); + } else { + rtc_writel(rtc, LPC32XX_RTC_CTRL, + tmp & ~LPC32XX_RTC_CTRL_MATCH0); + } + + platform_set_drvdata(pdev, rtc); + + rtc->rtc = rtc_device_register(RTC_NAME, &pdev->dev, &lpc32xx_rtc_ops, + THIS_MODULE); + if (IS_ERR(rtc->rtc)) { + dev_err(&pdev->dev, "Can't get RTC\n"); + platform_set_drvdata(pdev, NULL); + return PTR_ERR(rtc->rtc); + } + + /* + * IRQ is enabled after device registration in case alarm IRQ + * is pending upon suspend exit. + */ + if (rtc->irq >= 0) { + if (devm_request_irq(&pdev->dev, rtc->irq, + lpc32xx_rtc_alarm_interrupt, + IRQF_DISABLED, pdev->name, rtc) < 0) { + dev_warn(&pdev->dev, "Can't request interrupt.\n"); + rtc->irq = -1; + } else { + device_init_wakeup(&pdev->dev, 1); + } + } + + return 0; +} + +static int __devexit lpc32xx_rtc_remove(struct platform_device *pdev) +{ + struct lpc32xx_rtc *rtc = platform_get_drvdata(pdev); + + if (rtc->irq >= 0) + device_init_wakeup(&pdev->dev, 0); + + platform_set_drvdata(pdev, NULL); + rtc_device_unregister(rtc->rtc); + + return 0; +} + +#ifdef CONFIG_PM +static int lpc32xx_rtc_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct lpc32xx_rtc *rtc = platform_get_drvdata(pdev); + + if (rtc->irq >= 0) { + if (device_may_wakeup(&pdev->dev)) + enable_irq_wake(rtc->irq); + else + disable_irq_wake(rtc->irq); + } + + return 0; +} + +static int lpc32xx_rtc_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct lpc32xx_rtc *rtc = platform_get_drvdata(pdev); + + if (rtc->irq >= 0 && device_may_wakeup(&pdev->dev)) + disable_irq_wake(rtc->irq); + + return 0; +} + +/* Unconditionally disable the alarm */ +static int lpc32xx_rtc_freeze(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct lpc32xx_rtc *rtc = platform_get_drvdata(pdev); + + spin_lock_irq(&rtc->lock); + + rtc_writel(rtc, LPC32XX_RTC_CTRL, + rtc_readl(rtc, LPC32XX_RTC_CTRL) & + ~LPC32XX_RTC_CTRL_MATCH0); + + spin_unlock_irq(&rtc->lock); + + return 0; +} + +static int lpc32xx_rtc_thaw(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct lpc32xx_rtc *rtc = platform_get_drvdata(pdev); + + if (rtc->alarm_enabled) { + spin_lock_irq(&rtc->lock); + + rtc_writel(rtc, LPC32XX_RTC_CTRL, + rtc_readl(rtc, LPC32XX_RTC_CTRL) | + LPC32XX_RTC_CTRL_MATCH0); + + spin_unlock_irq(&rtc->lock); + } + + return 0; +} + +static const struct dev_pm_ops lpc32xx_rtc_pm_ops = { + .suspend = lpc32xx_rtc_suspend, + .resume = lpc32xx_rtc_resume, + .freeze = lpc32xx_rtc_freeze, + .thaw = lpc32xx_rtc_thaw, + .restore = lpc32xx_rtc_resume +}; + +#define LPC32XX_RTC_PM_OPS (&lpc32xx_rtc_pm_ops) +#else +#define LPC32XX_RTC_PM_OPS NULL +#endif + +static struct platform_driver lpc32xx_rtc_driver = { + .probe = lpc32xx_rtc_probe, + .remove = __devexit_p(lpc32xx_rtc_remove), + .driver = { + .name = RTC_NAME, + .owner = THIS_MODULE, + .pm = LPC32XX_RTC_PM_OPS + }, +}; + +static int __init lpc32xx_rtc_init(void) +{ + return platform_driver_register(&lpc32xx_rtc_driver); +} +module_init(lpc32xx_rtc_init); + +static void __exit lpc32xx_rtc_exit(void) +{ + platform_driver_unregister(&lpc32xx_rtc_driver); +} +module_exit(lpc32xx_rtc_exit); + +MODULE_AUTHOR("Kevin Wells <wellsk40@gmail.com"); +MODULE_DESCRIPTION("RTC driver for the LPC32xx SoC"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:rtc-lpc32xx"); diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c index 64d9727b722..73377b0d65d 100644 --- a/drivers/rtc/rtc-omap.c +++ b/drivers/rtc/rtc-omap.c @@ -34,7 +34,8 @@ * Board-specific wiring options include using split power mode with * RTC_OFF_NOFF used as the reset signal (so the RTC won't be reset), * and wiring RTC_WAKE_INT (so the RTC alarm can wake the system from - * low power modes). See the BOARD-SPECIFIC CUSTOMIZATION comment. + * low power modes) for OMAP1 boards (OMAP-L138 has this built into + * the SoC). See the BOARD-SPECIFIC CUSTOMIZATION comment. */ #define OMAP_RTC_BASE 0xfffb4800 @@ -401,16 +402,17 @@ static int __init omap_rtc_probe(struct platform_device *pdev) /* BOARD-SPECIFIC CUSTOMIZATION CAN GO HERE: * - * - Boards wired so that RTC_WAKE_INT does something, and muxed - * right (W13_1610_RTC_WAKE_INT is the default after chip reset), - * should initialize the device wakeup flag appropriately. + * - Device wake-up capability setting should come through chip + * init logic. OMAP1 boards should initialize the "wakeup capable" + * flag in the platform device if the board is wired right for + * being woken up by RTC alarm. For OMAP-L138, this capability + * is built into the SoC by the "Deep Sleep" capability. * * - Boards wired so RTC_ON_nOFF is used as the reset signal, * rather than nPWRON_RESET, should forcibly enable split * power mode. (Some chip errata report that RTC_CTRL_SPLIT * is write-only, and always reads as zero...) */ - device_init_wakeup(&pdev->dev, 0); if (new_ctrl & (u8) OMAP_RTC_CTRL_SPLIT) pr_info("%s: split power mode\n", pdev->name); diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index f57a87f4ae9..cf953ecbfca 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -100,7 +100,7 @@ static int s3c_rtc_setpie(struct device *dev, int enabled) spin_lock_irq(&s3c_rtc_pie_lock); if (s3c_rtc_cpu_type == TYPE_S3C64XX) { - tmp = readb(s3c_rtc_base + S3C2410_RTCCON); + tmp = readw(s3c_rtc_base + S3C2410_RTCCON); tmp &= ~S3C64XX_RTCCON_TICEN; if (enabled) @@ -171,8 +171,8 @@ static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) goto retry_get_time; } - pr_debug("read time %02x.%02x.%02x %02x/%02x/%02x\n", - rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday, + pr_debug("read time %04d.%02d.%02d %02d:%02d:%02d\n", + 1900 + rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday, rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec); rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec); @@ -185,7 +185,7 @@ static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) rtc_tm->tm_year += 100; rtc_tm->tm_mon -= 1; - return 0; + return rtc_valid_tm(rtc_tm); } static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm) @@ -193,8 +193,8 @@ static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm) void __iomem *base = s3c_rtc_base; int year = tm->tm_year - 100; - pr_debug("set time %02d.%02d.%02d %02d/%02d/%02d\n", - tm->tm_year, tm->tm_mon, tm->tm_mday, + pr_debug("set time %04d.%02d.%02d %02d:%02d:%02d\n", + 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); /* we get around y2k by simply not supporting it */ @@ -231,9 +231,9 @@ static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm) alrm->enabled = (alm_en & S3C2410_RTCALM_ALMEN) ? 1 : 0; - pr_debug("read alarm %02x %02x.%02x.%02x %02x/%02x/%02x\n", + pr_debug("read alarm %d, %04d.%02d.%02d %02d:%02d:%02d\n", alm_en, - alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday, + 1900 + alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday, alm_tm->tm_hour, alm_tm->tm_min, alm_tm->tm_sec); @@ -242,34 +242,34 @@ static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm) if (alm_en & S3C2410_RTCALM_SECEN) alm_tm->tm_sec = bcd2bin(alm_tm->tm_sec); else - alm_tm->tm_sec = 0xff; + alm_tm->tm_sec = -1; if (alm_en & S3C2410_RTCALM_MINEN) alm_tm->tm_min = bcd2bin(alm_tm->tm_min); else - alm_tm->tm_min = 0xff; + alm_tm->tm_min = -1; if (alm_en & S3C2410_RTCALM_HOUREN) alm_tm->tm_hour = bcd2bin(alm_tm->tm_hour); else - alm_tm->tm_hour = 0xff; + alm_tm->tm_hour = -1; if (alm_en & S3C2410_RTCALM_DAYEN) alm_tm->tm_mday = bcd2bin(alm_tm->tm_mday); else - alm_tm->tm_mday = 0xff; + alm_tm->tm_mday = -1; if (alm_en & S3C2410_RTCALM_MONEN) { alm_tm->tm_mon = bcd2bin(alm_tm->tm_mon); alm_tm->tm_mon -= 1; } else { - alm_tm->tm_mon = 0xff; + alm_tm->tm_mon = -1; } if (alm_en & S3C2410_RTCALM_YEAREN) alm_tm->tm_year = bcd2bin(alm_tm->tm_year); else - alm_tm->tm_year = 0xffff; + alm_tm->tm_year = -1; return 0; } @@ -280,10 +280,10 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) void __iomem *base = s3c_rtc_base; unsigned int alrm_en; - pr_debug("s3c_rtc_setalarm: %d, %02x/%02x/%02x %02x.%02x.%02x\n", + pr_debug("s3c_rtc_setalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\n", alrm->enabled, - tm->tm_mday & 0xff, tm->tm_mon & 0xff, tm->tm_year & 0xff, - tm->tm_hour & 0xff, tm->tm_min & 0xff, tm->tm_sec); + 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); alrm_en = readb(base + S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN; @@ -318,7 +318,7 @@ static int s3c_rtc_proc(struct device *dev, struct seq_file *seq) unsigned int ticnt; if (s3c_rtc_cpu_type == TYPE_S3C64XX) { - ticnt = readb(s3c_rtc_base + S3C2410_RTCCON); + ticnt = readw(s3c_rtc_base + S3C2410_RTCCON); ticnt &= S3C64XX_RTCCON_TICEN; } else { ticnt = readb(s3c_rtc_base + S3C2410_TICNT); @@ -379,7 +379,8 @@ static const struct rtc_class_ops s3c_rtcops = { .set_alarm = s3c_rtc_setalarm, .irq_set_freq = s3c_rtc_setfreq, .irq_set_state = s3c_rtc_setpie, - .proc = s3c_rtc_proc, + .proc = s3c_rtc_proc, + .alarm_irq_enable = s3c_rtc_setaie, }; static void s3c_rtc_enable(struct platform_device *pdev, int en) @@ -391,11 +392,11 @@ static void s3c_rtc_enable(struct platform_device *pdev, int en) return; if (!en) { - tmp = readb(base + S3C2410_RTCCON); + tmp = readw(base + S3C2410_RTCCON); if (s3c_rtc_cpu_type == TYPE_S3C64XX) tmp &= ~S3C64XX_RTCCON_TICEN; tmp &= ~S3C2410_RTCCON_RTCEN; - writeb(tmp, base + S3C2410_RTCCON); + writew(tmp, base + S3C2410_RTCCON); if (s3c_rtc_cpu_type == TYPE_S3C2410) { tmp = readb(base + S3C2410_TICNT); @@ -405,25 +406,28 @@ static void s3c_rtc_enable(struct platform_device *pdev, int en) } else { /* re-enable the device, and check it is ok */ - if ((readb(base+S3C2410_RTCCON) & S3C2410_RTCCON_RTCEN) == 0){ + if ((readw(base+S3C2410_RTCCON) & S3C2410_RTCCON_RTCEN) == 0) { dev_info(&pdev->dev, "rtc disabled, re-enabling\n"); - tmp = readb(base + S3C2410_RTCCON); - writeb(tmp|S3C2410_RTCCON_RTCEN, base+S3C2410_RTCCON); + tmp = readw(base + S3C2410_RTCCON); + writew(tmp | S3C2410_RTCCON_RTCEN, + base + S3C2410_RTCCON); } - if ((readb(base + S3C2410_RTCCON) & S3C2410_RTCCON_CNTSEL)){ + if ((readw(base + S3C2410_RTCCON) & S3C2410_RTCCON_CNTSEL)) { dev_info(&pdev->dev, "removing RTCCON_CNTSEL\n"); - tmp = readb(base + S3C2410_RTCCON); - writeb(tmp& ~S3C2410_RTCCON_CNTSEL, base+S3C2410_RTCCON); + tmp = readw(base + S3C2410_RTCCON); + writew(tmp & ~S3C2410_RTCCON_CNTSEL, + base + S3C2410_RTCCON); } - if ((readb(base + S3C2410_RTCCON) & S3C2410_RTCCON_CLKRST)){ + if ((readw(base + S3C2410_RTCCON) & S3C2410_RTCCON_CLKRST)) { dev_info(&pdev->dev, "removing RTCCON_CLKRST\n"); - tmp = readb(base + S3C2410_RTCCON); - writeb(tmp & ~S3C2410_RTCCON_CLKRST, base+S3C2410_RTCCON); + tmp = readw(base + S3C2410_RTCCON); + writew(tmp & ~S3C2410_RTCCON_CLKRST, + base + S3C2410_RTCCON); } } } @@ -452,8 +456,8 @@ static int __devexit s3c_rtc_remove(struct platform_device *dev) static int __devinit s3c_rtc_probe(struct platform_device *pdev) { struct rtc_device *rtc; + struct rtc_time rtc_tm; struct resource *res; - unsigned int tmp, i; int ret; pr_debug("%s: probe=%p\n", __func__, pdev); @@ -514,8 +518,8 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev) s3c_rtc_enable(pdev, 1); - pr_debug("s3c2410_rtc: RTCCON=%02x\n", - readb(s3c_rtc_base + S3C2410_RTCCON)); + pr_debug("s3c2410_rtc: RTCCON=%02x\n", + readw(s3c_rtc_base + S3C2410_RTCCON)); device_init_wakeup(&pdev->dev, 1); @@ -534,11 +538,19 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev) /* Check RTC Time */ - for (i = S3C2410_RTCSEC; i <= S3C2410_RTCYEAR; i += 0x4) { - tmp = readb(s3c_rtc_base + i); + s3c_rtc_gettime(NULL, &rtc_tm); + + if (rtc_valid_tm(&rtc_tm)) { + rtc_tm.tm_year = 100; + rtc_tm.tm_mon = 0; + rtc_tm.tm_mday = 1; + rtc_tm.tm_hour = 0; + rtc_tm.tm_min = 0; + rtc_tm.tm_sec = 0; + + s3c_rtc_settime(NULL, &rtc_tm); - if ((tmp & 0xf) > 0x9 || ((tmp >> 4) & 0xf) > 0x9) - writeb(0, s3c_rtc_base + i); + dev_warn(&pdev->dev, "warning: invalid RTC value so initializing it\n"); } if (s3c_rtc_cpu_type == TYPE_S3C64XX) @@ -578,7 +590,7 @@ static int s3c_rtc_suspend(struct platform_device *pdev, pm_message_t state) /* save TICNT for anyone using periodic interrupts */ ticnt_save = readb(s3c_rtc_base + S3C2410_TICNT); if (s3c_rtc_cpu_type == TYPE_S3C64XX) { - ticnt_en_save = readb(s3c_rtc_base + S3C2410_RTCCON); + ticnt_en_save = readw(s3c_rtc_base + S3C2410_RTCCON); ticnt_en_save &= S3C64XX_RTCCON_TICEN; } s3c_rtc_enable(pdev, 0); @@ -596,8 +608,8 @@ static int s3c_rtc_resume(struct platform_device *pdev) s3c_rtc_enable(pdev, 1); writeb(ticnt_save, s3c_rtc_base + S3C2410_TICNT); if (s3c_rtc_cpu_type == TYPE_S3C64XX && ticnt_en_save) { - tmp = readb(s3c_rtc_base + S3C2410_RTCCON); - writeb(tmp | ticnt_en_save, s3c_rtc_base + S3C2410_RTCCON); + tmp = readw(s3c_rtc_base + S3C2410_RTCCON); + writew(tmp | ticnt_en_save, s3c_rtc_base + S3C2410_RTCCON); } if (device_may_wakeup(&pdev->dev)) diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index 42e303ff862..0e6aa3d96a4 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -697,9 +697,9 @@ fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) struct inode *inode = file->f_path.dentry->d_inode; int fbidx = iminor(inode); struct fb_info *info = registered_fb[fbidx]; - u32 *buffer, *dst; - u32 __iomem *src; - int c, i, cnt = 0, err = 0; + u8 *buffer, *dst; + u8 __iomem *src; + int c, cnt = 0, err = 0; unsigned long total_size; if (!info || ! info->screen_base) @@ -730,7 +730,7 @@ fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) if (!buffer) return -ENOMEM; - src = (u32 __iomem *) (info->screen_base + p); + src = (u8 __iomem *) (info->screen_base + p); if (info->fbops->fb_sync) info->fbops->fb_sync(info); @@ -738,17 +738,9 @@ fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) while (count) { c = (count > PAGE_SIZE) ? PAGE_SIZE : count; dst = buffer; - for (i = c >> 2; i--; ) - *dst++ = fb_readl(src++); - if (c & 3) { - u8 *dst8 = (u8 *) dst; - u8 __iomem *src8 = (u8 __iomem *) src; - - for (i = c & 3; i--;) - *dst8++ = fb_readb(src8++); - - src = (u32 __iomem *) src8; - } + fb_memcpy_fromfb(dst, src, c); + dst += c; + src += c; if (copy_to_user(buf, buffer, c)) { err = -EFAULT; @@ -772,9 +764,9 @@ fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) struct inode *inode = file->f_path.dentry->d_inode; int fbidx = iminor(inode); struct fb_info *info = registered_fb[fbidx]; - u32 *buffer, *src; - u32 __iomem *dst; - int c, i, cnt = 0, err = 0; + u8 *buffer, *src; + u8 __iomem *dst; + int c, cnt = 0, err = 0; unsigned long total_size; if (!info || !info->screen_base) @@ -811,7 +803,7 @@ fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) if (!buffer) return -ENOMEM; - dst = (u32 __iomem *) (info->screen_base + p); + dst = (u8 __iomem *) (info->screen_base + p); if (info->fbops->fb_sync) info->fbops->fb_sync(info); @@ -825,19 +817,9 @@ fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) break; } - for (i = c >> 2; i--; ) - fb_writel(*src++, dst++); - - if (c & 3) { - u8 *src8 = (u8 *) src; - u8 __iomem *dst8 = (u8 __iomem *) dst; - - for (i = c & 3; i--; ) - fb_writeb(*src8++, dst8++); - - dst = (u32 __iomem *) dst8; - } - + fb_memcpy_tofb(dst, src, c); + dst += c; + src += c; *ppos += c; buf += c; cnt += c; @@ -877,13 +859,13 @@ fb_pan_display(struct fb_info *info, struct fb_var_screeninfo *var) if ((err = info->fbops->fb_pan_display(var, info))) return err; - info->var.xoffset = var->xoffset; - info->var.yoffset = var->yoffset; - if (var->vmode & FB_VMODE_YWRAP) - info->var.vmode |= FB_VMODE_YWRAP; - else - info->var.vmode &= ~FB_VMODE_YWRAP; - return 0; + info->var.xoffset = var->xoffset; + info->var.yoffset = var->yoffset; + if (var->vmode & FB_VMODE_YWRAP) + info->var.vmode |= FB_VMODE_YWRAP; + else + info->var.vmode &= ~FB_VMODE_YWRAP; + return 0; } static int fb_check_caps(struct fb_info *info, struct fb_var_screeninfo *var, diff --git a/drivers/video/gbefb.c b/drivers/video/gbefb.c index ca3355e430b..933899dca33 100644 --- a/drivers/video/gbefb.c +++ b/drivers/video/gbefb.c @@ -1143,8 +1143,10 @@ static int __devinit gbefb_probe(struct platform_device *p_dev) return -ENOMEM; #ifndef MODULE - if (fb_get_options("gbefb", &options)) - return -ENODEV; + if (fb_get_options("gbefb", &options)) { + ret = -ENODEV; + goto out_release_framebuffer; + } gbefb_setup(options); #endif diff --git a/drivers/video/matrox/matroxfb_DAC1064.c b/drivers/video/matrox/matroxfb_DAC1064.c index f9fa0fd0029..1717623aabc 100644 --- a/drivers/video/matrox/matroxfb_DAC1064.c +++ b/drivers/video/matrox/matroxfb_DAC1064.c @@ -869,12 +869,9 @@ static int MGAG100_preinit(struct matrox_fb_info *minfo) minfo->capable.plnwt = minfo->devflags.accelerator == FB_ACCEL_MATROX_MGAG100 ? minfo->devflags.sgram : 1; -#ifdef CONFIG_FB_MATROX_G if (minfo->devflags.g450dac) { minfo->outputs[0].output = &g450out; - } else -#endif - { + } else { minfo->outputs[0].output = &m1064; } minfo->outputs[0].src = minfo->outputs[0].default_src; diff --git a/drivers/video/matrox/matroxfb_maven.c b/drivers/video/matrox/matroxfb_maven.c index 1e3e8f19783..31b8f67477b 100644 --- a/drivers/video/matrox/matroxfb_maven.c +++ b/drivers/video/matrox/matroxfb_maven.c @@ -280,7 +280,7 @@ static int matroxfb_PLL_mavenclock(const struct matrox_pll_features2* pll, return fxtal * (*feed) / (*in) * ctl->den; } -static unsigned int matroxfb_mavenclock(const struct matrox_pll_ctl* ctl, +static int matroxfb_mavenclock(const struct matrox_pll_ctl *ctl, unsigned int htotal, unsigned int vtotal, unsigned int* in, unsigned int* feed, unsigned int* post, unsigned int* htotal2) { diff --git a/drivers/video/omap/blizzard.c b/drivers/video/omap/blizzard.c index 2ffb34af4c5..87785c215a5 100644 --- a/drivers/video/omap/blizzard.c +++ b/drivers/video/omap/blizzard.c @@ -1590,7 +1590,7 @@ static int blizzard_init(struct omapfb_device *fbdev, int ext_mode, blizzard.auto_update_window.width = fbdev->panel->x_res; blizzard.auto_update_window.height = fbdev->panel->y_res; blizzard.auto_update_window.out_x = 0; - blizzard.auto_update_window.out_x = 0; + blizzard.auto_update_window.out_y = 0; blizzard.auto_update_window.out_width = fbdev->panel->x_res; blizzard.auto_update_window.out_height = fbdev->panel->y_res; blizzard.auto_update_window.format = 0; diff --git a/drivers/video/savage/savagefb-i2c.c b/drivers/video/savage/savagefb-i2c.c index ed371c868b3..b16e6138fdd 100644 --- a/drivers/video/savage/savagefb-i2c.c +++ b/drivers/video/savage/savagefb-i2c.c @@ -181,6 +181,15 @@ void savagefb_create_i2c_busses(struct fb_info *info) par->chan.algo.getscl = prosavage_gpio_getscl; break; case FB_ACCEL_SAVAGE4: + par->chan.reg = CR_SERIAL1; + if (par->pcidev->revision > 1 && !(VGArCR(0xa6, par) & 0x40)) + par->chan.reg = CR_SERIAL2; + par->chan.ioaddr = par->mmio.vbase; + par->chan.algo.setsda = prosavage_gpio_setsda; + par->chan.algo.setscl = prosavage_gpio_setscl; + par->chan.algo.getsda = prosavage_gpio_getsda; + par->chan.algo.getscl = prosavage_gpio_getscl; + break; case FB_ACCEL_SAVAGE2000: par->chan.reg = 0xff20; par->chan.ioaddr = par->mmio.vbase; diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c index 2839e281cd6..b7b5014ff71 100644 --- a/drivers/w1/w1.c +++ b/drivers/w1/w1.c @@ -517,10 +517,10 @@ static W1_MASTER_ATTR_RO(max_slave_count, S_IRUGO); static W1_MASTER_ATTR_RO(attempts, S_IRUGO); static W1_MASTER_ATTR_RO(timeout, S_IRUGO); static W1_MASTER_ATTR_RO(pointer, S_IRUGO); -static W1_MASTER_ATTR_RW(search, S_IRUGO | S_IWUGO); -static W1_MASTER_ATTR_RW(pullup, S_IRUGO | S_IWUGO); -static W1_MASTER_ATTR_RW(add, S_IRUGO | S_IWUGO); -static W1_MASTER_ATTR_RW(remove, S_IRUGO | S_IWUGO); +static W1_MASTER_ATTR_RW(search, S_IRUGO | S_IWUSR | S_IWGRP); +static W1_MASTER_ATTR_RW(pullup, S_IRUGO | S_IWUSR | S_IWGRP); +static W1_MASTER_ATTR_RW(add, S_IRUGO | S_IWUSR | S_IWGRP); +static W1_MASTER_ATTR_RW(remove, S_IRUGO | S_IWUSR | S_IWGRP); static struct attribute *w1_master_default_attrs[] = { &w1_master_attribute_name.attr, |