18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * PIKA Warp(tm) board specific routines 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2008-2009 PIKA Technologies 68c2ecf20Sopenharmony_ci * Sean MacLennan <smaclennan@pikatech.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci#include <linux/init.h> 98c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 108c2ecf20Sopenharmony_ci#include <linux/kthread.h> 118c2ecf20Sopenharmony_ci#include <linux/i2c.h> 128c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 138c2ecf20Sopenharmony_ci#include <linux/delay.h> 148c2ecf20Sopenharmony_ci#include <linux/of_gpio.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/export.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <asm/machdep.h> 198c2ecf20Sopenharmony_ci#include <asm/prom.h> 208c2ecf20Sopenharmony_ci#include <asm/udbg.h> 218c2ecf20Sopenharmony_ci#include <asm/time.h> 228c2ecf20Sopenharmony_ci#include <asm/uic.h> 238c2ecf20Sopenharmony_ci#include <asm/ppc4xx.h> 248c2ecf20Sopenharmony_ci#include <asm/dma.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic const struct of_device_id warp_of_bus[] __initconst = { 288c2ecf20Sopenharmony_ci { .compatible = "ibm,plb4", }, 298c2ecf20Sopenharmony_ci { .compatible = "ibm,opb", }, 308c2ecf20Sopenharmony_ci { .compatible = "ibm,ebc", }, 318c2ecf20Sopenharmony_ci {}, 328c2ecf20Sopenharmony_ci}; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic int __init warp_device_probe(void) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci of_platform_bus_probe(NULL, warp_of_bus, NULL); 378c2ecf20Sopenharmony_ci return 0; 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_cimachine_device_initcall(warp, warp_device_probe); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic int __init warp_probe(void) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci if (!of_machine_is_compatible("pika,warp")) 448c2ecf20Sopenharmony_ci return 0; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci return 1; 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cidefine_machine(warp) { 508c2ecf20Sopenharmony_ci .name = "Warp", 518c2ecf20Sopenharmony_ci .probe = warp_probe, 528c2ecf20Sopenharmony_ci .progress = udbg_progress, 538c2ecf20Sopenharmony_ci .init_IRQ = uic_init_tree, 548c2ecf20Sopenharmony_ci .get_irq = uic_get_irq, 558c2ecf20Sopenharmony_ci .restart = ppc4xx_reset_system, 568c2ecf20Sopenharmony_ci .calibrate_decr = generic_calibrate_decr, 578c2ecf20Sopenharmony_ci}; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic int __init warp_post_info(void) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci struct device_node *np; 638c2ecf20Sopenharmony_ci void __iomem *fpga; 648c2ecf20Sopenharmony_ci u32 post1, post2; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci /* Sighhhh... POST information is in the sd area. */ 678c2ecf20Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "pika,fpga-sd"); 688c2ecf20Sopenharmony_ci if (np == NULL) 698c2ecf20Sopenharmony_ci return -ENOENT; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci fpga = of_iomap(np, 0); 728c2ecf20Sopenharmony_ci of_node_put(np); 738c2ecf20Sopenharmony_ci if (fpga == NULL) 748c2ecf20Sopenharmony_ci return -ENOENT; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci post1 = in_be32(fpga + 0x40); 778c2ecf20Sopenharmony_ci post2 = in_be32(fpga + 0x44); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci iounmap(fpga); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci if (post1 || post2) 828c2ecf20Sopenharmony_ci printk(KERN_INFO "Warp POST %08x %08x\n", post1, post2); 838c2ecf20Sopenharmony_ci else 848c2ecf20Sopenharmony_ci printk(KERN_INFO "Warp POST OK\n"); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci return 0; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci#ifdef CONFIG_SENSORS_AD7414 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic LIST_HEAD(dtm_shutdown_list); 938c2ecf20Sopenharmony_cistatic void __iomem *dtm_fpga; 948c2ecf20Sopenharmony_cistatic unsigned green_led, red_led; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistruct dtm_shutdown { 988c2ecf20Sopenharmony_ci struct list_head list; 998c2ecf20Sopenharmony_ci void (*func)(void *arg); 1008c2ecf20Sopenharmony_ci void *arg; 1018c2ecf20Sopenharmony_ci}; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ciint pika_dtm_register_shutdown(void (*func)(void *arg), void *arg) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci struct dtm_shutdown *shutdown; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci shutdown = kmalloc(sizeof(struct dtm_shutdown), GFP_KERNEL); 1098c2ecf20Sopenharmony_ci if (shutdown == NULL) 1108c2ecf20Sopenharmony_ci return -ENOMEM; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci shutdown->func = func; 1138c2ecf20Sopenharmony_ci shutdown->arg = arg; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci list_add(&shutdown->list, &dtm_shutdown_list); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci return 0; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ciint pika_dtm_unregister_shutdown(void (*func)(void *arg), void *arg) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci struct dtm_shutdown *shutdown; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci list_for_each_entry(shutdown, &dtm_shutdown_list, list) 1258c2ecf20Sopenharmony_ci if (shutdown->func == func && shutdown->arg == arg) { 1268c2ecf20Sopenharmony_ci list_del(&shutdown->list); 1278c2ecf20Sopenharmony_ci kfree(shutdown); 1288c2ecf20Sopenharmony_ci return 0; 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci return -EINVAL; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic irqreturn_t temp_isr(int irq, void *context) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci struct dtm_shutdown *shutdown; 1378c2ecf20Sopenharmony_ci int value = 1; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci local_irq_disable(); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci gpio_set_value(green_led, 0); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci /* Run through the shutdown list. */ 1448c2ecf20Sopenharmony_ci list_for_each_entry(shutdown, &dtm_shutdown_list, list) 1458c2ecf20Sopenharmony_ci shutdown->func(shutdown->arg); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci printk(KERN_EMERG "\n\nCritical Temperature Shutdown\n\n"); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci while (1) { 1508c2ecf20Sopenharmony_ci if (dtm_fpga) { 1518c2ecf20Sopenharmony_ci unsigned reset = in_be32(dtm_fpga + 0x14); 1528c2ecf20Sopenharmony_ci out_be32(dtm_fpga + 0x14, reset); 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci gpio_set_value(red_led, value); 1568c2ecf20Sopenharmony_ci value ^= 1; 1578c2ecf20Sopenharmony_ci mdelay(500); 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci /* Not reached */ 1618c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic int pika_setup_leds(void) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci struct device_node *np, *child; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "gpio-leds"); 1698c2ecf20Sopenharmony_ci if (!np) { 1708c2ecf20Sopenharmony_ci printk(KERN_ERR __FILE__ ": Unable to find leds\n"); 1718c2ecf20Sopenharmony_ci return -ENOENT; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci for_each_child_of_node(np, child) 1758c2ecf20Sopenharmony_ci if (of_node_name_eq(child, "green")) 1768c2ecf20Sopenharmony_ci green_led = of_get_gpio(child, 0); 1778c2ecf20Sopenharmony_ci else if (of_node_name_eq(child, "red")) 1788c2ecf20Sopenharmony_ci red_led = of_get_gpio(child, 0); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci of_node_put(np); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci return 0; 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic void pika_setup_critical_temp(struct device_node *np, 1868c2ecf20Sopenharmony_ci struct i2c_client *client) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci int irq, rc; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci /* Do this before enabling critical temp interrupt since we 1918c2ecf20Sopenharmony_ci * may immediately interrupt. 1928c2ecf20Sopenharmony_ci */ 1938c2ecf20Sopenharmony_ci pika_setup_leds(); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci /* These registers are in 1 degree increments. */ 1968c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(client, 2, 65); /* Thigh */ 1978c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(client, 3, 0); /* Tlow */ 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci irq = irq_of_parse_and_map(np, 0); 2008c2ecf20Sopenharmony_ci if (!irq) { 2018c2ecf20Sopenharmony_ci printk(KERN_ERR __FILE__ ": Unable to get ad7414 irq\n"); 2028c2ecf20Sopenharmony_ci return; 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci rc = request_irq(irq, temp_isr, 0, "ad7414", NULL); 2068c2ecf20Sopenharmony_ci if (rc) { 2078c2ecf20Sopenharmony_ci printk(KERN_ERR __FILE__ 2088c2ecf20Sopenharmony_ci ": Unable to request ad7414 irq %d = %d\n", irq, rc); 2098c2ecf20Sopenharmony_ci return; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic inline void pika_dtm_check_fan(void __iomem *fpga) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci static int fan_state; 2168c2ecf20Sopenharmony_ci u32 fan = in_be32(fpga + 0x34) & (1 << 14); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci if (fan_state != fan) { 2198c2ecf20Sopenharmony_ci fan_state = fan; 2208c2ecf20Sopenharmony_ci if (fan) 2218c2ecf20Sopenharmony_ci printk(KERN_WARNING "Fan rotation error detected." 2228c2ecf20Sopenharmony_ci " Please check hardware.\n"); 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic int pika_dtm_thread(void __iomem *fpga) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci struct device_node *np; 2298c2ecf20Sopenharmony_ci struct i2c_client *client; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "adi,ad7414"); 2328c2ecf20Sopenharmony_ci if (np == NULL) 2338c2ecf20Sopenharmony_ci return -ENOENT; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci client = of_find_i2c_device_by_node(np); 2368c2ecf20Sopenharmony_ci if (client == NULL) { 2378c2ecf20Sopenharmony_ci of_node_put(np); 2388c2ecf20Sopenharmony_ci return -ENOENT; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci pika_setup_critical_temp(np, client); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci of_node_put(np); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci printk(KERN_INFO "Warp DTM thread running.\n"); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci while (!kthread_should_stop()) { 2488c2ecf20Sopenharmony_ci int val; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci val = i2c_smbus_read_word_data(client, 0); 2518c2ecf20Sopenharmony_ci if (val < 0) 2528c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "DTM read temp failed.\n"); 2538c2ecf20Sopenharmony_ci else { 2548c2ecf20Sopenharmony_ci s16 temp = swab16(val); 2558c2ecf20Sopenharmony_ci out_be32(fpga + 0x20, temp); 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci pika_dtm_check_fan(fpga); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 2618c2ecf20Sopenharmony_ci schedule_timeout(HZ); 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci return 0; 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic int __init pika_dtm_start(void) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci struct task_struct *dtm_thread; 2708c2ecf20Sopenharmony_ci struct device_node *np; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "pika,fpga"); 2738c2ecf20Sopenharmony_ci if (np == NULL) 2748c2ecf20Sopenharmony_ci return -ENOENT; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci dtm_fpga = of_iomap(np, 0); 2778c2ecf20Sopenharmony_ci of_node_put(np); 2788c2ecf20Sopenharmony_ci if (dtm_fpga == NULL) 2798c2ecf20Sopenharmony_ci return -ENOENT; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci /* Must get post info before thread starts. */ 2828c2ecf20Sopenharmony_ci warp_post_info(); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci dtm_thread = kthread_run(pika_dtm_thread, dtm_fpga, "pika-dtm"); 2858c2ecf20Sopenharmony_ci if (IS_ERR(dtm_thread)) { 2868c2ecf20Sopenharmony_ci iounmap(dtm_fpga); 2878c2ecf20Sopenharmony_ci return PTR_ERR(dtm_thread); 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci return 0; 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_cimachine_late_initcall(warp, pika_dtm_start); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci#else /* !CONFIG_SENSORS_AD7414 */ 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ciint pika_dtm_register_shutdown(void (*func)(void *arg), void *arg) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci return 0; 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ciint pika_dtm_unregister_shutdown(void (*func)(void *arg), void *arg) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci return 0; 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cimachine_late_initcall(warp, warp_post_info); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci#endif 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pika_dtm_register_shutdown); 3118c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pika_dtm_unregister_shutdown); 312