Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Apple Motion Sensor driver (PMU variant)
0004  *
0005  * Copyright (C) 2006 Michael Hanselmann (linux-kernel@hansmi.ch)
0006  */
0007 
0008 #include <linux/module.h>
0009 #include <linux/types.h>
0010 #include <linux/errno.h>
0011 #include <linux/init.h>
0012 #include <linux/adb.h>
0013 #include <linux/pmu.h>
0014 
0015 #include "ams.h"
0016 
0017 /* Attitude */
0018 #define AMS_X           0x00
0019 #define AMS_Y           0x01
0020 #define AMS_Z           0x02
0021 
0022 /* Not exactly known, maybe chip vendor */
0023 #define AMS_VENDOR      0x03
0024 
0025 /* Freefall registers */
0026 #define AMS_FF_CLEAR        0x04
0027 #define AMS_FF_ENABLE       0x05
0028 #define AMS_FF_LOW_LIMIT    0x06
0029 #define AMS_FF_DEBOUNCE     0x07
0030 
0031 /* Shock registers */
0032 #define AMS_SHOCK_CLEAR     0x08
0033 #define AMS_SHOCK_ENABLE    0x09
0034 #define AMS_SHOCK_HIGH_LIMIT    0x0a
0035 #define AMS_SHOCK_DEBOUNCE  0x0b
0036 
0037 /* Global interrupt and power control register */
0038 #define AMS_CONTROL     0x0c
0039 
0040 static u8 ams_pmu_cmd;
0041 
0042 static void ams_pmu_req_complete(struct adb_request *req)
0043 {
0044     complete((struct completion *)req->arg);
0045 }
0046 
0047 /* Only call this function from task context */
0048 static void ams_pmu_set_register(u8 reg, u8 value)
0049 {
0050     static struct adb_request req;
0051     DECLARE_COMPLETION(req_complete);
0052 
0053     req.arg = &req_complete;
0054     if (pmu_request(&req, ams_pmu_req_complete, 4, ams_pmu_cmd, 0x00, reg, value))
0055         return;
0056 
0057     wait_for_completion(&req_complete);
0058 }
0059 
0060 /* Only call this function from task context */
0061 static u8 ams_pmu_get_register(u8 reg)
0062 {
0063     static struct adb_request req;
0064     DECLARE_COMPLETION(req_complete);
0065 
0066     req.arg = &req_complete;
0067     if (pmu_request(&req, ams_pmu_req_complete, 3, ams_pmu_cmd, 0x01, reg))
0068         return 0;
0069 
0070     wait_for_completion(&req_complete);
0071 
0072     if (req.reply_len > 0)
0073         return req.reply[0];
0074     else
0075         return 0;
0076 }
0077 
0078 /* Enables or disables the specified interrupts */
0079 static void ams_pmu_set_irq(enum ams_irq reg, char enable)
0080 {
0081     if (reg & AMS_IRQ_FREEFALL) {
0082         u8 val = ams_pmu_get_register(AMS_FF_ENABLE);
0083         if (enable)
0084             val |= 0x80;
0085         else
0086             val &= ~0x80;
0087         ams_pmu_set_register(AMS_FF_ENABLE, val);
0088     }
0089 
0090     if (reg & AMS_IRQ_SHOCK) {
0091         u8 val = ams_pmu_get_register(AMS_SHOCK_ENABLE);
0092         if (enable)
0093             val |= 0x80;
0094         else
0095             val &= ~0x80;
0096         ams_pmu_set_register(AMS_SHOCK_ENABLE, val);
0097     }
0098 
0099     if (reg & AMS_IRQ_GLOBAL) {
0100         u8 val = ams_pmu_get_register(AMS_CONTROL);
0101         if (enable)
0102             val |= 0x80;
0103         else
0104             val &= ~0x80;
0105         ams_pmu_set_register(AMS_CONTROL, val);
0106     }
0107 }
0108 
0109 static void ams_pmu_clear_irq(enum ams_irq reg)
0110 {
0111     if (reg & AMS_IRQ_FREEFALL)
0112         ams_pmu_set_register(AMS_FF_CLEAR, 0x00);
0113 
0114     if (reg & AMS_IRQ_SHOCK)
0115         ams_pmu_set_register(AMS_SHOCK_CLEAR, 0x00);
0116 }
0117 
0118 static u8 ams_pmu_get_vendor(void)
0119 {
0120     return ams_pmu_get_register(AMS_VENDOR);
0121 }
0122 
0123 static void ams_pmu_get_xyz(s8 *x, s8 *y, s8 *z)
0124 {
0125     *x = ams_pmu_get_register(AMS_X);
0126     *y = ams_pmu_get_register(AMS_Y);
0127     *z = ams_pmu_get_register(AMS_Z);
0128 }
0129 
0130 static void ams_pmu_exit(void)
0131 {
0132     ams_sensor_detach();
0133 
0134     /* Disable interrupts */
0135     ams_pmu_set_irq(AMS_IRQ_ALL, 0);
0136 
0137     /* Clear interrupts */
0138     ams_pmu_clear_irq(AMS_IRQ_ALL);
0139 
0140     ams_info.has_device = 0;
0141 
0142     printk(KERN_INFO "ams: Unloading\n");
0143 }
0144 
0145 int __init ams_pmu_init(struct device_node *np)
0146 {
0147     const u32 *prop;
0148     int result;
0149 
0150     /* Set implementation stuff */
0151     ams_info.of_node = np;
0152     ams_info.exit = ams_pmu_exit;
0153     ams_info.get_vendor = ams_pmu_get_vendor;
0154     ams_info.get_xyz = ams_pmu_get_xyz;
0155     ams_info.clear_irq = ams_pmu_clear_irq;
0156     ams_info.bustype = BUS_HOST;
0157 
0158     /* Get PMU command, should be 0x4e, but we can never know */
0159     prop = of_get_property(ams_info.of_node, "reg", NULL);
0160     if (!prop)
0161         return -ENODEV;
0162 
0163     ams_pmu_cmd = ((*prop) >> 8) & 0xff;
0164 
0165     /* Disable interrupts */
0166     ams_pmu_set_irq(AMS_IRQ_ALL, 0);
0167 
0168     /* Clear interrupts */
0169     ams_pmu_clear_irq(AMS_IRQ_ALL);
0170 
0171     result = ams_sensor_attach();
0172     if (result < 0)
0173         return result;
0174 
0175     /* Set default values */
0176     ams_pmu_set_register(AMS_FF_LOW_LIMIT, 0x15);
0177     ams_pmu_set_register(AMS_FF_ENABLE, 0x08);
0178     ams_pmu_set_register(AMS_FF_DEBOUNCE, 0x14);
0179 
0180     ams_pmu_set_register(AMS_SHOCK_HIGH_LIMIT, 0x60);
0181     ams_pmu_set_register(AMS_SHOCK_ENABLE, 0x0f);
0182     ams_pmu_set_register(AMS_SHOCK_DEBOUNCE, 0x14);
0183 
0184     ams_pmu_set_register(AMS_CONTROL, 0x4f);
0185 
0186     /* Clear interrupts */
0187     ams_pmu_clear_irq(AMS_IRQ_ALL);
0188 
0189     ams_info.has_device = 1;
0190 
0191     /* Enable interrupts */
0192     ams_pmu_set_irq(AMS_IRQ_ALL, 1);
0193 
0194     printk(KERN_INFO "ams: Found PMU based motion sensor\n");
0195 
0196     return 0;
0197 }