Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Apple Motion Sensor driver (I2C variant)
0004  *
0005  * Copyright (C) 2005 Stelian Pop (stelian@popies.net)
0006  * Copyright (C) 2006 Michael Hanselmann (linux-kernel@hansmi.ch)
0007  *
0008  * Clean room implementation based on the reverse engineered Mac OS X driver by
0009  * Johannes Berg <johannes@sipsolutions.net>, documentation available at
0010  * http://johannes.sipsolutions.net/PowerBook/Apple_Motion_Sensor_Specification
0011  */
0012 
0013 #include <linux/module.h>
0014 #include <linux/types.h>
0015 #include <linux/errno.h>
0016 #include <linux/init.h>
0017 #include <linux/delay.h>
0018 
0019 #include "ams.h"
0020 
0021 /* AMS registers */
0022 #define AMS_COMMAND 0x00    /* command register */
0023 #define AMS_STATUS  0x01    /* status register */
0024 #define AMS_CTRL1   0x02    /* read control 1 (number of values) */
0025 #define AMS_CTRL2   0x03    /* read control 2 (offset?) */
0026 #define AMS_CTRL3   0x04    /* read control 3 (size of each value?) */
0027 #define AMS_DATA1   0x05    /* read data 1 */
0028 #define AMS_DATA2   0x06    /* read data 2 */
0029 #define AMS_DATA3   0x07    /* read data 3 */
0030 #define AMS_DATA4   0x08    /* read data 4 */
0031 #define AMS_DATAX   0x20    /* data X */
0032 #define AMS_DATAY   0x21    /* data Y */
0033 #define AMS_DATAZ   0x22    /* data Z */
0034 #define AMS_FREEFALL    0x24    /* freefall int control */
0035 #define AMS_SHOCK   0x25    /* shock int control */
0036 #define AMS_SENSLOW 0x26    /* sensitivity low limit */
0037 #define AMS_SENSHIGH    0x27    /* sensitivity high limit */
0038 #define AMS_CTRLX   0x28    /* control X */
0039 #define AMS_CTRLY   0x29    /* control Y */
0040 #define AMS_CTRLZ   0x2A    /* control Z */
0041 #define AMS_UNKNOWN1    0x2B    /* unknown 1 */
0042 #define AMS_UNKNOWN2    0x2C    /* unknown 2 */
0043 #define AMS_UNKNOWN3    0x2D    /* unknown 3 */
0044 #define AMS_VENDOR  0x2E    /* vendor */
0045 
0046 /* AMS commands - use with the AMS_COMMAND register */
0047 enum ams_i2c_cmd {
0048     AMS_CMD_NOOP = 0,
0049     AMS_CMD_VERSION,
0050     AMS_CMD_READMEM,
0051     AMS_CMD_WRITEMEM,
0052     AMS_CMD_ERASEMEM,
0053     AMS_CMD_READEE,
0054     AMS_CMD_WRITEEE,
0055     AMS_CMD_RESET,
0056     AMS_CMD_START,
0057 };
0058 
0059 static int ams_i2c_probe(struct i2c_client *client,
0060              const struct i2c_device_id *id);
0061 static int ams_i2c_remove(struct i2c_client *client);
0062 
0063 static const struct i2c_device_id ams_id[] = {
0064     { "MAC,accelerometer_1", 0 },
0065     { }
0066 };
0067 MODULE_DEVICE_TABLE(i2c, ams_id);
0068 
0069 static struct i2c_driver ams_i2c_driver = {
0070     .driver = {
0071         .name   = "ams",
0072     },
0073     .probe          = ams_i2c_probe,
0074     .remove         = ams_i2c_remove,
0075     .id_table       = ams_id,
0076 };
0077 
0078 static s32 ams_i2c_read(u8 reg)
0079 {
0080     return i2c_smbus_read_byte_data(ams_info.i2c_client, reg);
0081 }
0082 
0083 static int ams_i2c_write(u8 reg, u8 value)
0084 {
0085     return i2c_smbus_write_byte_data(ams_info.i2c_client, reg, value);
0086 }
0087 
0088 static int ams_i2c_cmd(enum ams_i2c_cmd cmd)
0089 {
0090     s32 result;
0091     int count = 3;
0092 
0093     ams_i2c_write(AMS_COMMAND, cmd);
0094     msleep(5);
0095 
0096     while (count--) {
0097         result = ams_i2c_read(AMS_COMMAND);
0098         if (result == 0 || result & 0x80)
0099             return 0;
0100 
0101         schedule_timeout_uninterruptible(HZ / 20);
0102     }
0103 
0104     return -1;
0105 }
0106 
0107 static void ams_i2c_set_irq(enum ams_irq reg, char enable)
0108 {
0109     if (reg & AMS_IRQ_FREEFALL) {
0110         u8 val = ams_i2c_read(AMS_CTRLX);
0111         if (enable)
0112             val |= 0x80;
0113         else
0114             val &= ~0x80;
0115         ams_i2c_write(AMS_CTRLX, val);
0116     }
0117 
0118     if (reg & AMS_IRQ_SHOCK) {
0119         u8 val = ams_i2c_read(AMS_CTRLY);
0120         if (enable)
0121             val |= 0x80;
0122         else
0123             val &= ~0x80;
0124         ams_i2c_write(AMS_CTRLY, val);
0125     }
0126 
0127     if (reg & AMS_IRQ_GLOBAL) {
0128         u8 val = ams_i2c_read(AMS_CTRLZ);
0129         if (enable)
0130             val |= 0x80;
0131         else
0132             val &= ~0x80;
0133         ams_i2c_write(AMS_CTRLZ, val);
0134     }
0135 }
0136 
0137 static void ams_i2c_clear_irq(enum ams_irq reg)
0138 {
0139     if (reg & AMS_IRQ_FREEFALL)
0140         ams_i2c_write(AMS_FREEFALL, 0);
0141 
0142     if (reg & AMS_IRQ_SHOCK)
0143         ams_i2c_write(AMS_SHOCK, 0);
0144 }
0145 
0146 static u8 ams_i2c_get_vendor(void)
0147 {
0148     return ams_i2c_read(AMS_VENDOR);
0149 }
0150 
0151 static void ams_i2c_get_xyz(s8 *x, s8 *y, s8 *z)
0152 {
0153     *x = ams_i2c_read(AMS_DATAX);
0154     *y = ams_i2c_read(AMS_DATAY);
0155     *z = ams_i2c_read(AMS_DATAZ);
0156 }
0157 
0158 static int ams_i2c_probe(struct i2c_client *client,
0159              const struct i2c_device_id *id)
0160 {
0161     int vmaj, vmin;
0162     int result;
0163 
0164     /* There can be only one */
0165     if (unlikely(ams_info.has_device))
0166         return -ENODEV;
0167 
0168     ams_info.i2c_client = client;
0169 
0170     if (ams_i2c_cmd(AMS_CMD_RESET)) {
0171         printk(KERN_INFO "ams: Failed to reset the device\n");
0172         return -ENODEV;
0173     }
0174 
0175     if (ams_i2c_cmd(AMS_CMD_START)) {
0176         printk(KERN_INFO "ams: Failed to start the device\n");
0177         return -ENODEV;
0178     }
0179 
0180     /* get version/vendor information */
0181     ams_i2c_write(AMS_CTRL1, 0x02);
0182     ams_i2c_write(AMS_CTRL2, 0x85);
0183     ams_i2c_write(AMS_CTRL3, 0x01);
0184 
0185     ams_i2c_cmd(AMS_CMD_READMEM);
0186 
0187     vmaj = ams_i2c_read(AMS_DATA1);
0188     vmin = ams_i2c_read(AMS_DATA2);
0189     if (vmaj != 1 || vmin != 52) {
0190         printk(KERN_INFO "ams: Incorrect device version (%d.%d)\n",
0191             vmaj, vmin);
0192         return -ENODEV;
0193     }
0194 
0195     ams_i2c_cmd(AMS_CMD_VERSION);
0196 
0197     vmaj = ams_i2c_read(AMS_DATA1);
0198     vmin = ams_i2c_read(AMS_DATA2);
0199     if (vmaj != 0 || vmin != 1) {
0200         printk(KERN_INFO "ams: Incorrect firmware version (%d.%d)\n",
0201             vmaj, vmin);
0202         return -ENODEV;
0203     }
0204 
0205     /* Disable interrupts */
0206     ams_i2c_set_irq(AMS_IRQ_ALL, 0);
0207 
0208     result = ams_sensor_attach();
0209     if (result < 0)
0210         return result;
0211 
0212     /* Set default values */
0213     ams_i2c_write(AMS_SENSLOW, 0x15);
0214     ams_i2c_write(AMS_SENSHIGH, 0x60);
0215     ams_i2c_write(AMS_CTRLX, 0x08);
0216     ams_i2c_write(AMS_CTRLY, 0x0F);
0217     ams_i2c_write(AMS_CTRLZ, 0x4F);
0218     ams_i2c_write(AMS_UNKNOWN1, 0x14);
0219 
0220     /* Clear interrupts */
0221     ams_i2c_clear_irq(AMS_IRQ_ALL);
0222 
0223     ams_info.has_device = 1;
0224 
0225     /* Enable interrupts */
0226     ams_i2c_set_irq(AMS_IRQ_ALL, 1);
0227 
0228     printk(KERN_INFO "ams: Found I2C based motion sensor\n");
0229 
0230     return 0;
0231 }
0232 
0233 static int ams_i2c_remove(struct i2c_client *client)
0234 {
0235     if (ams_info.has_device) {
0236         ams_sensor_detach();
0237 
0238         /* Disable interrupts */
0239         ams_i2c_set_irq(AMS_IRQ_ALL, 0);
0240 
0241         /* Clear interrupts */
0242         ams_i2c_clear_irq(AMS_IRQ_ALL);
0243 
0244         printk(KERN_INFO "ams: Unloading\n");
0245 
0246         ams_info.has_device = 0;
0247     }
0248 
0249     return 0;
0250 }
0251 
0252 static void ams_i2c_exit(void)
0253 {
0254     i2c_del_driver(&ams_i2c_driver);
0255 }
0256 
0257 int __init ams_i2c_init(struct device_node *np)
0258 {
0259     /* Set implementation stuff */
0260     ams_info.of_node = np;
0261     ams_info.exit = ams_i2c_exit;
0262     ams_info.get_vendor = ams_i2c_get_vendor;
0263     ams_info.get_xyz = ams_i2c_get_xyz;
0264     ams_info.clear_irq = ams_i2c_clear_irq;
0265     ams_info.bustype = BUS_I2C;
0266 
0267     return i2c_add_driver(&ams_i2c_driver);
0268 }