diff options
Diffstat (limited to 'arch/powerpc/platforms/44x/warp.c')
-rw-r--r-- | arch/powerpc/platforms/44x/warp.c | 318 |
1 files changed, 318 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/44x/warp.c b/arch/powerpc/platforms/44x/warp.c new file mode 100644 index 000000000..a886c2c22 --- /dev/null +++ b/arch/powerpc/platforms/44x/warp.c @@ -0,0 +1,318 @@ +/* + * PIKA Warp(tm) board specific routines + * + * Copyright (c) 2008-2009 PIKA Technologies + * Sean MacLennan <smaclennan@pikatech.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/init.h> +#include <linux/of_platform.h> +#include <linux/kthread.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/of_gpio.h> +#include <linux/slab.h> +#include <linux/export.h> + +#include <asm/machdep.h> +#include <asm/prom.h> +#include <asm/udbg.h> +#include <asm/time.h> +#include <asm/uic.h> +#include <asm/ppc4xx.h> +#include <asm/dma.h> + + +static const struct of_device_id warp_of_bus[] __initconst = { + { .compatible = "ibm,plb4", }, + { .compatible = "ibm,opb", }, + { .compatible = "ibm,ebc", }, + {}, +}; + +static int __init warp_device_probe(void) +{ + of_platform_bus_probe(NULL, warp_of_bus, NULL); + return 0; +} +machine_device_initcall(warp, warp_device_probe); + +static int __init warp_probe(void) +{ + if (!of_machine_is_compatible("pika,warp")) + return 0; + + /* For __dma_alloc_coherent */ + ISA_DMA_THRESHOLD = ~0L; + + return 1; +} + +define_machine(warp) { + .name = "Warp", + .probe = warp_probe, + .progress = udbg_progress, + .init_IRQ = uic_init_tree, + .get_irq = uic_get_irq, + .restart = ppc4xx_reset_system, + .calibrate_decr = generic_calibrate_decr, +}; + + +static int __init warp_post_info(void) +{ + struct device_node *np; + void __iomem *fpga; + u32 post1, post2; + + /* Sighhhh... POST information is in the sd area. */ + np = of_find_compatible_node(NULL, NULL, "pika,fpga-sd"); + if (np == NULL) + return -ENOENT; + + fpga = of_iomap(np, 0); + of_node_put(np); + if (fpga == NULL) + return -ENOENT; + + post1 = in_be32(fpga + 0x40); + post2 = in_be32(fpga + 0x44); + + iounmap(fpga); + + if (post1 || post2) + printk(KERN_INFO "Warp POST %08x %08x\n", post1, post2); + else + printk(KERN_INFO "Warp POST OK\n"); + + return 0; +} + + +#ifdef CONFIG_SENSORS_AD7414 + +static LIST_HEAD(dtm_shutdown_list); +static void __iomem *dtm_fpga; +static unsigned green_led, red_led; + + +struct dtm_shutdown { + struct list_head list; + void (*func)(void *arg); + void *arg; +}; + + +int pika_dtm_register_shutdown(void (*func)(void *arg), void *arg) +{ + struct dtm_shutdown *shutdown; + + shutdown = kmalloc(sizeof(struct dtm_shutdown), GFP_KERNEL); + if (shutdown == NULL) + return -ENOMEM; + + shutdown->func = func; + shutdown->arg = arg; + + list_add(&shutdown->list, &dtm_shutdown_list); + + return 0; +} + +int pika_dtm_unregister_shutdown(void (*func)(void *arg), void *arg) +{ + struct dtm_shutdown *shutdown; + + list_for_each_entry(shutdown, &dtm_shutdown_list, list) + if (shutdown->func == func && shutdown->arg == arg) { + list_del(&shutdown->list); + kfree(shutdown); + return 0; + } + + return -EINVAL; +} + +static irqreturn_t temp_isr(int irq, void *context) +{ + struct dtm_shutdown *shutdown; + int value = 1; + + local_irq_disable(); + + gpio_set_value(green_led, 0); + + /* Run through the shutdown list. */ + list_for_each_entry(shutdown, &dtm_shutdown_list, list) + shutdown->func(shutdown->arg); + + printk(KERN_EMERG "\n\nCritical Temperature Shutdown\n\n"); + + while (1) { + if (dtm_fpga) { + unsigned reset = in_be32(dtm_fpga + 0x14); + out_be32(dtm_fpga + 0x14, reset); + } + + gpio_set_value(red_led, value); + value ^= 1; + mdelay(500); + } + + /* Not reached */ + return IRQ_HANDLED; +} + +static int pika_setup_leds(void) +{ + struct device_node *np, *child; + + np = of_find_compatible_node(NULL, NULL, "gpio-leds"); + if (!np) { + printk(KERN_ERR __FILE__ ": Unable to find leds\n"); + return -ENOENT; + } + + for_each_child_of_node(np, child) + if (strcmp(child->name, "green") == 0) + green_led = of_get_gpio(child, 0); + else if (strcmp(child->name, "red") == 0) + red_led = of_get_gpio(child, 0); + + of_node_put(np); + + return 0; +} + +static void pika_setup_critical_temp(struct device_node *np, + struct i2c_client *client) +{ + int irq, rc; + + /* Do this before enabling critical temp interrupt since we + * may immediately interrupt. + */ + pika_setup_leds(); + + /* These registers are in 1 degree increments. */ + i2c_smbus_write_byte_data(client, 2, 65); /* Thigh */ + i2c_smbus_write_byte_data(client, 3, 0); /* Tlow */ + + irq = irq_of_parse_and_map(np, 0); + if (!irq) { + printk(KERN_ERR __FILE__ ": Unable to get ad7414 irq\n"); + return; + } + + rc = request_irq(irq, temp_isr, 0, "ad7414", NULL); + if (rc) { + printk(KERN_ERR __FILE__ + ": Unable to request ad7414 irq %d = %d\n", irq, rc); + return; + } +} + +static inline void pika_dtm_check_fan(void __iomem *fpga) +{ + static int fan_state; + u32 fan = in_be32(fpga + 0x34) & (1 << 14); + + if (fan_state != fan) { + fan_state = fan; + if (fan) + printk(KERN_WARNING "Fan rotation error detected." + " Please check hardware.\n"); + } +} + +static int pika_dtm_thread(void __iomem *fpga) +{ + struct device_node *np; + struct i2c_client *client; + + np = of_find_compatible_node(NULL, NULL, "adi,ad7414"); + if (np == NULL) + return -ENOENT; + + client = of_find_i2c_device_by_node(np); + if (client == NULL) { + of_node_put(np); + return -ENOENT; + } + + pika_setup_critical_temp(np, client); + + of_node_put(np); + + printk(KERN_INFO "Warp DTM thread running.\n"); + + while (!kthread_should_stop()) { + int val; + + val = i2c_smbus_read_word_data(client, 0); + if (val < 0) + dev_dbg(&client->dev, "DTM read temp failed.\n"); + else { + s16 temp = swab16(val); + out_be32(fpga + 0x20, temp); + } + + pika_dtm_check_fan(fpga); + + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ); + } + + return 0; +} + +static int __init pika_dtm_start(void) +{ + struct task_struct *dtm_thread; + struct device_node *np; + + np = of_find_compatible_node(NULL, NULL, "pika,fpga"); + if (np == NULL) + return -ENOENT; + + dtm_fpga = of_iomap(np, 0); + of_node_put(np); + if (dtm_fpga == NULL) + return -ENOENT; + + /* Must get post info before thread starts. */ + warp_post_info(); + + dtm_thread = kthread_run(pika_dtm_thread, dtm_fpga, "pika-dtm"); + if (IS_ERR(dtm_thread)) { + iounmap(dtm_fpga); + return PTR_ERR(dtm_thread); + } + + return 0; +} +machine_late_initcall(warp, pika_dtm_start); + +#else /* !CONFIG_SENSORS_AD7414 */ + +int pika_dtm_register_shutdown(void (*func)(void *arg), void *arg) +{ + return 0; +} + +int pika_dtm_unregister_shutdown(void (*func)(void *arg), void *arg) +{ + return 0; +} + +machine_late_initcall(warp, warp_post_info); + +#endif + +EXPORT_SYMBOL(pika_dtm_register_shutdown); +EXPORT_SYMBOL(pika_dtm_unregister_shutdown); |