root/drivers/macintosh/ams/ams-i2c.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ams_i2c_read
  2. ams_i2c_write
  3. ams_i2c_cmd
  4. ams_i2c_set_irq
  5. ams_i2c_clear_irq
  6. ams_i2c_get_vendor
  7. ams_i2c_get_xyz
  8. ams_i2c_probe
  9. ams_i2c_remove
  10. ams_i2c_exit
  11. ams_i2c_init

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  * Apple Motion Sensor driver (I2C variant)
   4  *
   5  * Copyright (C) 2005 Stelian Pop (stelian@popies.net)
   6  * Copyright (C) 2006 Michael Hanselmann (linux-kernel@hansmi.ch)
   7  *
   8  * Clean room implementation based on the reverse engineered Mac OS X driver by
   9  * Johannes Berg <johannes@sipsolutions.net>, documentation available at
  10  * http://johannes.sipsolutions.net/PowerBook/Apple_Motion_Sensor_Specification
  11  */
  12 
  13 #include <linux/module.h>
  14 #include <linux/types.h>
  15 #include <linux/errno.h>
  16 #include <linux/init.h>
  17 #include <linux/delay.h>
  18 
  19 #include "ams.h"
  20 
  21 /* AMS registers */
  22 #define AMS_COMMAND     0x00    /* command register */
  23 #define AMS_STATUS      0x01    /* status register */
  24 #define AMS_CTRL1       0x02    /* read control 1 (number of values) */
  25 #define AMS_CTRL2       0x03    /* read control 2 (offset?) */
  26 #define AMS_CTRL3       0x04    /* read control 3 (size of each value?) */
  27 #define AMS_DATA1       0x05    /* read data 1 */
  28 #define AMS_DATA2       0x06    /* read data 2 */
  29 #define AMS_DATA3       0x07    /* read data 3 */
  30 #define AMS_DATA4       0x08    /* read data 4 */
  31 #define AMS_DATAX       0x20    /* data X */
  32 #define AMS_DATAY       0x21    /* data Y */
  33 #define AMS_DATAZ       0x22    /* data Z */
  34 #define AMS_FREEFALL    0x24    /* freefall int control */
  35 #define AMS_SHOCK       0x25    /* shock int control */
  36 #define AMS_SENSLOW     0x26    /* sensitivity low limit */
  37 #define AMS_SENSHIGH    0x27    /* sensitivity high limit */
  38 #define AMS_CTRLX       0x28    /* control X */
  39 #define AMS_CTRLY       0x29    /* control Y */
  40 #define AMS_CTRLZ       0x2A    /* control Z */
  41 #define AMS_UNKNOWN1    0x2B    /* unknown 1 */
  42 #define AMS_UNKNOWN2    0x2C    /* unknown 2 */
  43 #define AMS_UNKNOWN3    0x2D    /* unknown 3 */
  44 #define AMS_VENDOR      0x2E    /* vendor */
  45 
  46 /* AMS commands - use with the AMS_COMMAND register */
  47 enum ams_i2c_cmd {
  48         AMS_CMD_NOOP = 0,
  49         AMS_CMD_VERSION,
  50         AMS_CMD_READMEM,
  51         AMS_CMD_WRITEMEM,
  52         AMS_CMD_ERASEMEM,
  53         AMS_CMD_READEE,
  54         AMS_CMD_WRITEEE,
  55         AMS_CMD_RESET,
  56         AMS_CMD_START,
  57 };
  58 
  59 static int ams_i2c_probe(struct i2c_client *client,
  60                          const struct i2c_device_id *id);
  61 static int ams_i2c_remove(struct i2c_client *client);
  62 
  63 static const struct i2c_device_id ams_id[] = {
  64         { "MAC,accelerometer_1", 0 },
  65         { }
  66 };
  67 MODULE_DEVICE_TABLE(i2c, ams_id);
  68 
  69 static struct i2c_driver ams_i2c_driver = {
  70         .driver = {
  71                 .name   = "ams",
  72         },
  73         .probe          = ams_i2c_probe,
  74         .remove         = ams_i2c_remove,
  75         .id_table       = ams_id,
  76 };
  77 
  78 static s32 ams_i2c_read(u8 reg)
  79 {
  80         return i2c_smbus_read_byte_data(ams_info.i2c_client, reg);
  81 }
  82 
  83 static int ams_i2c_write(u8 reg, u8 value)
  84 {
  85         return i2c_smbus_write_byte_data(ams_info.i2c_client, reg, value);
  86 }
  87 
  88 static int ams_i2c_cmd(enum ams_i2c_cmd cmd)
  89 {
  90         s32 result;
  91         int count = 3;
  92 
  93         ams_i2c_write(AMS_COMMAND, cmd);
  94         msleep(5);
  95 
  96         while (count--) {
  97                 result = ams_i2c_read(AMS_COMMAND);
  98                 if (result == 0 || result & 0x80)
  99                         return 0;
 100 
 101                 schedule_timeout_uninterruptible(HZ / 20);
 102         }
 103 
 104         return -1;
 105 }
 106 
 107 static void ams_i2c_set_irq(enum ams_irq reg, char enable)
 108 {
 109         if (reg & AMS_IRQ_FREEFALL) {
 110                 u8 val = ams_i2c_read(AMS_CTRLX);
 111                 if (enable)
 112                         val |= 0x80;
 113                 else
 114                         val &= ~0x80;
 115                 ams_i2c_write(AMS_CTRLX, val);
 116         }
 117 
 118         if (reg & AMS_IRQ_SHOCK) {
 119                 u8 val = ams_i2c_read(AMS_CTRLY);
 120                 if (enable)
 121                         val |= 0x80;
 122                 else
 123                         val &= ~0x80;
 124                 ams_i2c_write(AMS_CTRLY, val);
 125         }
 126 
 127         if (reg & AMS_IRQ_GLOBAL) {
 128                 u8 val = ams_i2c_read(AMS_CTRLZ);
 129                 if (enable)
 130                         val |= 0x80;
 131                 else
 132                         val &= ~0x80;
 133                 ams_i2c_write(AMS_CTRLZ, val);
 134         }
 135 }
 136 
 137 static void ams_i2c_clear_irq(enum ams_irq reg)
 138 {
 139         if (reg & AMS_IRQ_FREEFALL)
 140                 ams_i2c_write(AMS_FREEFALL, 0);
 141 
 142         if (reg & AMS_IRQ_SHOCK)
 143                 ams_i2c_write(AMS_SHOCK, 0);
 144 }
 145 
 146 static u8 ams_i2c_get_vendor(void)
 147 {
 148         return ams_i2c_read(AMS_VENDOR);
 149 }
 150 
 151 static void ams_i2c_get_xyz(s8 *x, s8 *y, s8 *z)
 152 {
 153         *x = ams_i2c_read(AMS_DATAX);
 154         *y = ams_i2c_read(AMS_DATAY);
 155         *z = ams_i2c_read(AMS_DATAZ);
 156 }
 157 
 158 static int ams_i2c_probe(struct i2c_client *client,
 159                          const struct i2c_device_id *id)
 160 {
 161         int vmaj, vmin;
 162         int result;
 163 
 164         /* There can be only one */
 165         if (unlikely(ams_info.has_device))
 166                 return -ENODEV;
 167 
 168         ams_info.i2c_client = client;
 169 
 170         if (ams_i2c_cmd(AMS_CMD_RESET)) {
 171                 printk(KERN_INFO "ams: Failed to reset the device\n");
 172                 return -ENODEV;
 173         }
 174 
 175         if (ams_i2c_cmd(AMS_CMD_START)) {
 176                 printk(KERN_INFO "ams: Failed to start the device\n");
 177                 return -ENODEV;
 178         }
 179 
 180         /* get version/vendor information */
 181         ams_i2c_write(AMS_CTRL1, 0x02);
 182         ams_i2c_write(AMS_CTRL2, 0x85);
 183         ams_i2c_write(AMS_CTRL3, 0x01);
 184 
 185         ams_i2c_cmd(AMS_CMD_READMEM);
 186 
 187         vmaj = ams_i2c_read(AMS_DATA1);
 188         vmin = ams_i2c_read(AMS_DATA2);
 189         if (vmaj != 1 || vmin != 52) {
 190                 printk(KERN_INFO "ams: Incorrect device version (%d.%d)\n",
 191                         vmaj, vmin);
 192                 return -ENODEV;
 193         }
 194 
 195         ams_i2c_cmd(AMS_CMD_VERSION);
 196 
 197         vmaj = ams_i2c_read(AMS_DATA1);
 198         vmin = ams_i2c_read(AMS_DATA2);
 199         if (vmaj != 0 || vmin != 1) {
 200                 printk(KERN_INFO "ams: Incorrect firmware version (%d.%d)\n",
 201                         vmaj, vmin);
 202                 return -ENODEV;
 203         }
 204 
 205         /* Disable interrupts */
 206         ams_i2c_set_irq(AMS_IRQ_ALL, 0);
 207 
 208         result = ams_sensor_attach();
 209         if (result < 0)
 210                 return result;
 211 
 212         /* Set default values */
 213         ams_i2c_write(AMS_SENSLOW, 0x15);
 214         ams_i2c_write(AMS_SENSHIGH, 0x60);
 215         ams_i2c_write(AMS_CTRLX, 0x08);
 216         ams_i2c_write(AMS_CTRLY, 0x0F);
 217         ams_i2c_write(AMS_CTRLZ, 0x4F);
 218         ams_i2c_write(AMS_UNKNOWN1, 0x14);
 219 
 220         /* Clear interrupts */
 221         ams_i2c_clear_irq(AMS_IRQ_ALL);
 222 
 223         ams_info.has_device = 1;
 224 
 225         /* Enable interrupts */
 226         ams_i2c_set_irq(AMS_IRQ_ALL, 1);
 227 
 228         printk(KERN_INFO "ams: Found I2C based motion sensor\n");
 229 
 230         return 0;
 231 }
 232 
 233 static int ams_i2c_remove(struct i2c_client *client)
 234 {
 235         if (ams_info.has_device) {
 236                 ams_sensor_detach();
 237 
 238                 /* Disable interrupts */
 239                 ams_i2c_set_irq(AMS_IRQ_ALL, 0);
 240 
 241                 /* Clear interrupts */
 242                 ams_i2c_clear_irq(AMS_IRQ_ALL);
 243 
 244                 printk(KERN_INFO "ams: Unloading\n");
 245 
 246                 ams_info.has_device = 0;
 247         }
 248 
 249         return 0;
 250 }
 251 
 252 static void ams_i2c_exit(void)
 253 {
 254         i2c_del_driver(&ams_i2c_driver);
 255 }
 256 
 257 int __init ams_i2c_init(struct device_node *np)
 258 {
 259         int result;
 260 
 261         /* Set implementation stuff */
 262         ams_info.of_node = np;
 263         ams_info.exit = ams_i2c_exit;
 264         ams_info.get_vendor = ams_i2c_get_vendor;
 265         ams_info.get_xyz = ams_i2c_get_xyz;
 266         ams_info.clear_irq = ams_i2c_clear_irq;
 267         ams_info.bustype = BUS_I2C;
 268 
 269         result = i2c_add_driver(&ams_i2c_driver);
 270 
 271         return result;
 272 }

/* [<][>][^][v][top][bottom][index][help] */