1bd5f47ecSJean Delvare /* 2bd5f47ecSJean Delvare * Apple Motion Sensor driver (PMU variant) 3bd5f47ecSJean Delvare * 4bd5f47ecSJean Delvare * Copyright (C) 2006 Michael Hanselmann (linux-kernel@hansmi.ch) 5bd5f47ecSJean Delvare * 6bd5f47ecSJean Delvare * This program is free software; you can redistribute it and/or modify 7bd5f47ecSJean Delvare * it under the terms of the GNU General Public License as published by 8bd5f47ecSJean Delvare * the Free Software Foundation; either version 2 of the License, or 9bd5f47ecSJean Delvare * (at your option) any later version. 10bd5f47ecSJean Delvare */ 11bd5f47ecSJean Delvare 12bd5f47ecSJean Delvare #include <linux/module.h> 13bd5f47ecSJean Delvare #include <linux/types.h> 14bd5f47ecSJean Delvare #include <linux/errno.h> 15bd5f47ecSJean Delvare #include <linux/init.h> 16bd5f47ecSJean Delvare #include <linux/adb.h> 17bd5f47ecSJean Delvare #include <linux/pmu.h> 18bd5f47ecSJean Delvare 19bd5f47ecSJean Delvare #include "ams.h" 20bd5f47ecSJean Delvare 21bd5f47ecSJean Delvare /* Attitude */ 22bd5f47ecSJean Delvare #define AMS_X 0x00 23bd5f47ecSJean Delvare #define AMS_Y 0x01 24bd5f47ecSJean Delvare #define AMS_Z 0x02 25bd5f47ecSJean Delvare 26bd5f47ecSJean Delvare /* Not exactly known, maybe chip vendor */ 27bd5f47ecSJean Delvare #define AMS_VENDOR 0x03 28bd5f47ecSJean Delvare 29bd5f47ecSJean Delvare /* Freefall registers */ 30bd5f47ecSJean Delvare #define AMS_FF_CLEAR 0x04 31bd5f47ecSJean Delvare #define AMS_FF_ENABLE 0x05 32bd5f47ecSJean Delvare #define AMS_FF_LOW_LIMIT 0x06 33bd5f47ecSJean Delvare #define AMS_FF_DEBOUNCE 0x07 34bd5f47ecSJean Delvare 35bd5f47ecSJean Delvare /* Shock registers */ 36bd5f47ecSJean Delvare #define AMS_SHOCK_CLEAR 0x08 37bd5f47ecSJean Delvare #define AMS_SHOCK_ENABLE 0x09 38bd5f47ecSJean Delvare #define AMS_SHOCK_HIGH_LIMIT 0x0a 39bd5f47ecSJean Delvare #define AMS_SHOCK_DEBOUNCE 0x0b 40bd5f47ecSJean Delvare 41bd5f47ecSJean Delvare /* Global interrupt and power control register */ 42bd5f47ecSJean Delvare #define AMS_CONTROL 0x0c 43bd5f47ecSJean Delvare 44bd5f47ecSJean Delvare static u8 ams_pmu_cmd; 45bd5f47ecSJean Delvare 46bd5f47ecSJean Delvare static void ams_pmu_req_complete(struct adb_request *req) 47bd5f47ecSJean Delvare { 48bd5f47ecSJean Delvare complete((struct completion *)req->arg); 49bd5f47ecSJean Delvare } 50bd5f47ecSJean Delvare 51bd5f47ecSJean Delvare /* Only call this function from task context */ 52bd5f47ecSJean Delvare static void ams_pmu_set_register(u8 reg, u8 value) 53bd5f47ecSJean Delvare { 54bd5f47ecSJean Delvare static struct adb_request req; 55bd5f47ecSJean Delvare DECLARE_COMPLETION(req_complete); 56bd5f47ecSJean Delvare 57bd5f47ecSJean Delvare req.arg = &req_complete; 58bd5f47ecSJean Delvare if (pmu_request(&req, ams_pmu_req_complete, 4, ams_pmu_cmd, 0x00, reg, value)) 59bd5f47ecSJean Delvare return; 60bd5f47ecSJean Delvare 61bd5f47ecSJean Delvare wait_for_completion(&req_complete); 62bd5f47ecSJean Delvare } 63bd5f47ecSJean Delvare 64bd5f47ecSJean Delvare /* Only call this function from task context */ 65bd5f47ecSJean Delvare static u8 ams_pmu_get_register(u8 reg) 66bd5f47ecSJean Delvare { 67bd5f47ecSJean Delvare static struct adb_request req; 68bd5f47ecSJean Delvare DECLARE_COMPLETION(req_complete); 69bd5f47ecSJean Delvare 70bd5f47ecSJean Delvare req.arg = &req_complete; 71bd5f47ecSJean Delvare if (pmu_request(&req, ams_pmu_req_complete, 3, ams_pmu_cmd, 0x01, reg)) 72bd5f47ecSJean Delvare return 0; 73bd5f47ecSJean Delvare 74bd5f47ecSJean Delvare wait_for_completion(&req_complete); 75bd5f47ecSJean Delvare 76bd5f47ecSJean Delvare if (req.reply_len > 0) 77bd5f47ecSJean Delvare return req.reply[0]; 78bd5f47ecSJean Delvare else 79bd5f47ecSJean Delvare return 0; 80bd5f47ecSJean Delvare } 81bd5f47ecSJean Delvare 82bd5f47ecSJean Delvare /* Enables or disables the specified interrupts */ 83bd5f47ecSJean Delvare static void ams_pmu_set_irq(enum ams_irq reg, char enable) 84bd5f47ecSJean Delvare { 85bd5f47ecSJean Delvare if (reg & AMS_IRQ_FREEFALL) { 86bd5f47ecSJean Delvare u8 val = ams_pmu_get_register(AMS_FF_ENABLE); 87bd5f47ecSJean Delvare if (enable) 88bd5f47ecSJean Delvare val |= 0x80; 89bd5f47ecSJean Delvare else 90bd5f47ecSJean Delvare val &= ~0x80; 91bd5f47ecSJean Delvare ams_pmu_set_register(AMS_FF_ENABLE, val); 92bd5f47ecSJean Delvare } 93bd5f47ecSJean Delvare 94bd5f47ecSJean Delvare if (reg & AMS_IRQ_SHOCK) { 95bd5f47ecSJean Delvare u8 val = ams_pmu_get_register(AMS_SHOCK_ENABLE); 96bd5f47ecSJean Delvare if (enable) 97bd5f47ecSJean Delvare val |= 0x80; 98bd5f47ecSJean Delvare else 99bd5f47ecSJean Delvare val &= ~0x80; 100bd5f47ecSJean Delvare ams_pmu_set_register(AMS_SHOCK_ENABLE, val); 101bd5f47ecSJean Delvare } 102bd5f47ecSJean Delvare 103bd5f47ecSJean Delvare if (reg & AMS_IRQ_GLOBAL) { 104bd5f47ecSJean Delvare u8 val = ams_pmu_get_register(AMS_CONTROL); 105bd5f47ecSJean Delvare if (enable) 106bd5f47ecSJean Delvare val |= 0x80; 107bd5f47ecSJean Delvare else 108bd5f47ecSJean Delvare val &= ~0x80; 109bd5f47ecSJean Delvare ams_pmu_set_register(AMS_CONTROL, val); 110bd5f47ecSJean Delvare } 111bd5f47ecSJean Delvare } 112bd5f47ecSJean Delvare 113bd5f47ecSJean Delvare static void ams_pmu_clear_irq(enum ams_irq reg) 114bd5f47ecSJean Delvare { 115bd5f47ecSJean Delvare if (reg & AMS_IRQ_FREEFALL) 116bd5f47ecSJean Delvare ams_pmu_set_register(AMS_FF_CLEAR, 0x00); 117bd5f47ecSJean Delvare 118bd5f47ecSJean Delvare if (reg & AMS_IRQ_SHOCK) 119bd5f47ecSJean Delvare ams_pmu_set_register(AMS_SHOCK_CLEAR, 0x00); 120bd5f47ecSJean Delvare } 121bd5f47ecSJean Delvare 122bd5f47ecSJean Delvare static u8 ams_pmu_get_vendor(void) 123bd5f47ecSJean Delvare { 124bd5f47ecSJean Delvare return ams_pmu_get_register(AMS_VENDOR); 125bd5f47ecSJean Delvare } 126bd5f47ecSJean Delvare 127bd5f47ecSJean Delvare static void ams_pmu_get_xyz(s8 *x, s8 *y, s8 *z) 128bd5f47ecSJean Delvare { 129bd5f47ecSJean Delvare *x = ams_pmu_get_register(AMS_X); 130bd5f47ecSJean Delvare *y = ams_pmu_get_register(AMS_Y); 131bd5f47ecSJean Delvare *z = ams_pmu_get_register(AMS_Z); 132bd5f47ecSJean Delvare } 133bd5f47ecSJean Delvare 134bd5f47ecSJean Delvare static void ams_pmu_exit(void) 135bd5f47ecSJean Delvare { 136bd5f47ecSJean Delvare ams_sensor_detach(); 137bd5f47ecSJean Delvare 138bd5f47ecSJean Delvare /* Disable interrupts */ 139bd5f47ecSJean Delvare ams_pmu_set_irq(AMS_IRQ_ALL, 0); 140bd5f47ecSJean Delvare 141bd5f47ecSJean Delvare /* Clear interrupts */ 142bd5f47ecSJean Delvare ams_pmu_clear_irq(AMS_IRQ_ALL); 143bd5f47ecSJean Delvare 144bd5f47ecSJean Delvare ams_info.has_device = 0; 145bd5f47ecSJean Delvare 146bd5f47ecSJean Delvare printk(KERN_INFO "ams: Unloading\n"); 147bd5f47ecSJean Delvare } 148bd5f47ecSJean Delvare 149bd5f47ecSJean Delvare int __init ams_pmu_init(struct device_node *np) 150bd5f47ecSJean Delvare { 151bd5f47ecSJean Delvare const u32 *prop; 152bd5f47ecSJean Delvare int result; 153bd5f47ecSJean Delvare 154bd5f47ecSJean Delvare /* Set implementation stuff */ 155bd5f47ecSJean Delvare ams_info.of_node = np; 156bd5f47ecSJean Delvare ams_info.exit = ams_pmu_exit; 157bd5f47ecSJean Delvare ams_info.get_vendor = ams_pmu_get_vendor; 158bd5f47ecSJean Delvare ams_info.get_xyz = ams_pmu_get_xyz; 159bd5f47ecSJean Delvare ams_info.clear_irq = ams_pmu_clear_irq; 160bd5f47ecSJean Delvare ams_info.bustype = BUS_HOST; 161bd5f47ecSJean Delvare 162bd5f47ecSJean Delvare /* Get PMU command, should be 0x4e, but we can never know */ 163bd5f47ecSJean Delvare prop = of_get_property(ams_info.of_node, "reg", NULL); 164bd5f47ecSJean Delvare if (!prop) 165bd5f47ecSJean Delvare return -ENODEV; 166bd5f47ecSJean Delvare 167bd5f47ecSJean Delvare ams_pmu_cmd = ((*prop) >> 8) & 0xff; 168bd5f47ecSJean Delvare 169bd5f47ecSJean Delvare /* Disable interrupts */ 170bd5f47ecSJean Delvare ams_pmu_set_irq(AMS_IRQ_ALL, 0); 171bd5f47ecSJean Delvare 172bd5f47ecSJean Delvare /* Clear interrupts */ 173bd5f47ecSJean Delvare ams_pmu_clear_irq(AMS_IRQ_ALL); 174bd5f47ecSJean Delvare 175bd5f47ecSJean Delvare result = ams_sensor_attach(); 176bd5f47ecSJean Delvare if (result < 0) 177bd5f47ecSJean Delvare return result; 178bd5f47ecSJean Delvare 179bd5f47ecSJean Delvare /* Set default values */ 180bd5f47ecSJean Delvare ams_pmu_set_register(AMS_FF_LOW_LIMIT, 0x15); 181bd5f47ecSJean Delvare ams_pmu_set_register(AMS_FF_ENABLE, 0x08); 182bd5f47ecSJean Delvare ams_pmu_set_register(AMS_FF_DEBOUNCE, 0x14); 183bd5f47ecSJean Delvare 184bd5f47ecSJean Delvare ams_pmu_set_register(AMS_SHOCK_HIGH_LIMIT, 0x60); 185bd5f47ecSJean Delvare ams_pmu_set_register(AMS_SHOCK_ENABLE, 0x0f); 186bd5f47ecSJean Delvare ams_pmu_set_register(AMS_SHOCK_DEBOUNCE, 0x14); 187bd5f47ecSJean Delvare 188bd5f47ecSJean Delvare ams_pmu_set_register(AMS_CONTROL, 0x4f); 189bd5f47ecSJean Delvare 190bd5f47ecSJean Delvare /* Clear interrupts */ 191bd5f47ecSJean Delvare ams_pmu_clear_irq(AMS_IRQ_ALL); 192bd5f47ecSJean Delvare 193bd5f47ecSJean Delvare ams_info.has_device = 1; 194bd5f47ecSJean Delvare 195bd5f47ecSJean Delvare /* Enable interrupts */ 196bd5f47ecSJean Delvare ams_pmu_set_irq(AMS_IRQ_ALL, 1); 197bd5f47ecSJean Delvare 198bd5f47ecSJean Delvare printk(KERN_INFO "ams: Found PMU based motion sensor\n"); 199bd5f47ecSJean Delvare 200bd5f47ecSJean Delvare return 0; 201bd5f47ecSJean Delvare } 202