1/* 2 * Northstar coma mode. 3 * 4 * Copyright (C) 2013, Broadcom Corporation. All Rights Reserved. 5 * 6 * Permission to use, copy, modify, and/or distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 13 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 15 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 * 18 * $Id: hndcoma.c 406135 2013-06-06 04:57:34Z $ 19 */ 20 21#include <linux/proc_fs.h> 22#include <typedefs.h> 23#include <bcmutils.h> 24#include <siutils.h> 25#include <bcmdevs.h> 26#include <bcmnvram.h> 27#include <asm/uaccess.h> 28#include <linux/platform_device.h> 29 30#define DMU_BASE 0x1800c000 31/* Offset */ 32#define CRU_INTERRUPT 0x18c 33#define PCU_AOPC_CONTROL 0x20 34#define PVTMON_CONTROL0 0x2c0 35#define CRU_RESET 0x184 36 37#define MAX_COMA_SLEEP_TIME 0x003fffe0 38 39/* Global SB handle */ 40extern si_t *bcm947xx_sih; 41extern spinlock_t bcm947xx_sih_lock; 42 43/* Convenience */ 44#define sih bcm947xx_sih 45#define sih_lock bcm947xx_sih_lock 46 47static uint32 dmu_base; 48static struct delayed_work coma_work; 49static struct platform_device *coma_pdev; 50 51static void coma_gpioout(uint32 gpio); 52 53static void 54coma_gpioout(uint32 gpio) 55{ 56 int enable_gpio_mask; 57 58 enable_gpio_mask = 1 << gpio; 59 60 /* low active */ 61 si_gpioout(sih, enable_gpio_mask, 0, GPIO_DRV_PRIORITY); 62 si_gpioouten(sih, enable_gpio_mask, enable_gpio_mask, GPIO_DRV_PRIORITY); 63} 64 65static void 66coma_polling(struct work_struct *work) 67{ 68 uint32 cru_interrupt; 69 70 cru_interrupt = readl(dmu_base + CRU_INTERRUPT); 71 if (cru_interrupt & 0x01) { 72 coma_pdev = platform_device_register_simple("coma_dev", 0, NULL, 0); 73 return; 74 } 75 schedule_delayed_work(&coma_work, msecs_to_jiffies(1000)); 76} 77 78static int 79coma_write_proc(struct file *file, const char __user *buf, 80 unsigned long count, void *data) 81{ 82 char *buffer, *var; 83 uint32 pcu_aopc_control; 84 uint32 pvtmon_control0; 85 uint32 gpio; 86 uint32 sleep_time; 87 88 buffer = kmalloc(count + 1, GFP_KERNEL); 89 if (!buffer) { 90 printk("%s: kmalloc failed.\n", __FUNCTION__); 91 goto out; 92 } 93 94 if (copy_from_user(buffer, buf, count)) { 95 printk("%s: copy_from_user failed.\n", __FUNCTION__); 96 goto out; 97 } 98 99 /* enter coma mode */ 100 if (buffer[0] == '2') { 101 /* PVT monitor power-down */ 102 pvtmon_control0 = readl(dmu_base + PVTMON_CONTROL0) | 0x1; 103 writel(pvtmon_control0, dmu_base + PVTMON_CONTROL0); 104 105 /* Put ROBOSW, USBPHY, OPT, PVTMON in reset mode */ 106 writel(0x6, dmu_base + CRU_RESET); 107 108 /* Drive GPIO pin to low to disable on-board switching regulator */ 109 gpio = getgpiopin(NULL, "coma_swreg", GPIO_PIN_NOTDEFINED); 110 if (gpio != GPIO_PIN_NOTDEFINED) { 111 coma_gpioout(gpio); 112 } 113 114 /* Clear bit0 (PWR_DIS_LATCH_EN) of PCU_AOPC_CONTROL */ 115 pcu_aopc_control = readl(dmu_base + PCU_AOPC_CONTROL); 116 pcu_aopc_control &= 0xfffffffe; 117 writel(pcu_aopc_control, dmu_base + PCU_AOPC_CONTROL); 118 119 /* set timer to auto-wakeup */ 120 pcu_aopc_control = readl(dmu_base + PCU_AOPC_CONTROL); 121 var = getvar(NULL, "coma_sleep_time"); 122 if (var) 123 sleep_time = bcm_strtoul(var, NULL, 0); 124 else 125 sleep_time = 0; 126 sleep_time = sleep_time << 5; 127 if ((sleep_time > 0) && (sleep_time <= MAX_COMA_SLEEP_TIME)) 128 pcu_aopc_control |= sleep_time; 129 else 130 pcu_aopc_control |= MAX_COMA_SLEEP_TIME; 131 132 /* Config power down setting */ 133 pcu_aopc_control |= 0x8040001f; 134 writel(pcu_aopc_control, dmu_base + PCU_AOPC_CONTROL); 135 } else if (buffer[0] == '0') { 136 /* cancel polling work */ 137 cancel_delayed_work(&coma_work); 138 } else if (buffer[0] == '1') { 139 /* resume polling work */ 140 schedule_delayed_work(&coma_work, msecs_to_jiffies(1000)); 141 } 142out: 143 if (buffer) 144 kfree(buffer); 145 146 return count; 147} 148 149static void __init 150coma_proc_init(void) 151{ 152 struct proc_dir_entry *coma_proc_dir, *coma; 153 char *var; 154 uint32 coma_disabled; 155 156 var = getvar(NULL, "coma_disable"); 157 if (var) { 158 coma_disabled = bcm_strtoul(var, NULL, 0); 159 if (coma_disabled == 1) 160 return; 161 } 162 163 coma_proc_dir = proc_mkdir("bcm947xx", NULL); 164 165 if (!coma_proc_dir) { 166 printk(KERN_ERR "%s: Create proc directory failed.\n", __FUNCTION__); 167 return; 168 } 169 170 coma = create_proc_entry("bcm947xx/coma", 0, NULL); 171 if (!coma) { 172 printk(KERN_ERR "%s: Create proc entry failed.\n", __FUNCTION__); 173 return; 174 } 175 176 dmu_base = (uint32)REG_MAP(DMU_BASE, 4096); 177 coma->write_proc = coma_write_proc; 178 179 INIT_DELAYED_WORK(&coma_work, coma_polling); 180 schedule_delayed_work(&coma_work, msecs_to_jiffies(1000)); 181} 182 183static void __exit 184coma_proc_exit(void) 185{ 186 REG_UNMAP((void *)dmu_base); 187 cancel_delayed_work(&coma_work); 188 remove_proc_entry("bcm947xx/coma", NULL); 189} 190 191module_init(coma_proc_init); 192module_exit(coma_proc_exit); 193