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