cregit-Linux how code gets into the kernel

Release 4.7 drivers/macintosh/ams/ams-i2c.c

/*
 * Apple Motion Sensor driver (I2C variant)
 *
 * Copyright (C) 2005 Stelian Pop (stelian@popies.net)
 * Copyright (C) 2006 Michael Hanselmann (linux-kernel@hansmi.ch)
 *
 * Clean room implementation based on the reverse engineered Mac OS X driver by
 * Johannes Berg <johannes@sipsolutions.net>, documentation available at
 * http://johannes.sipsolutions.net/PowerBook/Apple_Motion_Sensor_Specification
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 */

#include <linux/module.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/delay.h>

#include "ams.h"

/* AMS registers */

#define AMS_COMMAND	0x00	
/* command register */

#define AMS_STATUS	0x01	
/* status register */

#define AMS_CTRL1	0x02	
/* read control 1 (number of values) */

#define AMS_CTRL2	0x03	
/* read control 2 (offset?) */

#define AMS_CTRL3	0x04	
/* read control 3 (size of each value?) */

#define AMS_DATA1	0x05	
/* read data 1 */

#define AMS_DATA2	0x06	
/* read data 2 */

#define AMS_DATA3	0x07	
/* read data 3 */

#define AMS_DATA4	0x08	
/* read data 4 */

#define AMS_DATAX	0x20	
/* data X */

#define AMS_DATAY	0x21	
/* data Y */

#define AMS_DATAZ	0x22	
/* data Z */

#define AMS_FREEFALL	0x24	
/* freefall int control */

#define AMS_SHOCK	0x25	
/* shock int control */

#define AMS_SENSLOW	0x26	
/* sensitivity low limit */

#define AMS_SENSHIGH	0x27	
/* sensitivity high limit */

#define AMS_CTRLX	0x28	
/* control X */

#define AMS_CTRLY	0x29	
/* control Y */

#define AMS_CTRLZ	0x2A	
/* control Z */

#define AMS_UNKNOWN1	0x2B	
/* unknown 1 */

#define AMS_UNKNOWN2	0x2C	
/* unknown 2 */

#define AMS_UNKNOWN3	0x2D	
/* unknown 3 */

#define AMS_VENDOR	0x2E	
/* vendor */

/* AMS commands - use with the AMS_COMMAND register */

enum ams_i2c_cmd {
	
AMS_CMD_NOOP = 0,
	
AMS_CMD_VERSION,
	
AMS_CMD_READMEM,
	
AMS_CMD_WRITEMEM,
	
AMS_CMD_ERASEMEM,
	
AMS_CMD_READEE,
	
AMS_CMD_WRITEEE,
	
AMS_CMD_RESET,
	
AMS_CMD_START,
};

static int ams_i2c_probe(struct i2c_client *client,
			 const struct i2c_device_id *id);
static int ams_i2c_remove(struct i2c_client *client);


static const struct i2c_device_id ams_id[] = {
	{ "MAC,accelerometer_1", 0 },
	{ }
};
MODULE_DEVICE_TABLE(i2c, ams_id);


static struct i2c_driver ams_i2c_driver = {
	.driver = {
		.name   = "ams",
		.owner  = THIS_MODULE,
        },
	.probe          = ams_i2c_probe,
	.remove         = ams_i2c_remove,
	.id_table       = ams_id,
};


static s32 ams_i2c_read(u8 reg) { return i2c_smbus_read_byte_data(ams_info.i2c_client, reg); }

Contributors

PersonTokensPropCommitsCommitProp
stelian popstelian pop19100.00%1100.00%
Total19100.00%1100.00%


static int ams_i2c_write(u8 reg, u8 value) { return i2c_smbus_write_byte_data(ams_info.i2c_client, reg, value); }

Contributors

PersonTokensPropCommitsCommitProp
stelian popstelian pop24100.00%1100.00%
Total24100.00%1100.00%


static int ams_i2c_cmd(enum ams_i2c_cmd cmd) { s32 result; int count = 3; ams_i2c_write(AMS_COMMAND, cmd); msleep(5); while (count--) { result = ams_i2c_read(AMS_COMMAND); if (result == 0 || result & 0x80) return 0; schedule_timeout_uninterruptible(HZ / 20); } return -1; }

Contributors

PersonTokensPropCommitsCommitProp
stelian popstelian pop68100.00%2100.00%
Total68100.00%2100.00%


static void ams_i2c_set_irq(enum ams_irq reg, char enable) { if (reg & AMS_IRQ_FREEFALL) { u8 val = ams_i2c_read(AMS_CTRLX); if (enable) val |= 0x80; else val &= ~0x80; ams_i2c_write(AMS_CTRLX, val); } if (reg & AMS_IRQ_SHOCK) { u8 val = ams_i2c_read(AMS_CTRLY); if (enable) val |= 0x80; else val &= ~0x80; ams_i2c_write(AMS_CTRLY, val); } if (reg & AMS_IRQ_GLOBAL) { u8 val = ams_i2c_read(AMS_CTRLZ); if (enable) val |= 0x80; else val &= ~0x80; ams_i2c_write(AMS_CTRLZ, val); } }

Contributors

PersonTokensPropCommitsCommitProp
stelian popstelian pop124100.00%1100.00%
Total124100.00%1100.00%


static void ams_i2c_clear_irq(enum ams_irq reg) { if (reg & AMS_IRQ_FREEFALL) ams_i2c_write(AMS_FREEFALL, 0); if (reg & AMS_IRQ_SHOCK) ams_i2c_write(AMS_SHOCK, 0); }

Contributors

PersonTokensPropCommitsCommitProp
stelian popstelian pop36100.00%1100.00%
Total36100.00%1100.00%


static u8 ams_i2c_get_vendor(void) { return ams_i2c_read(AMS_VENDOR); }

Contributors

PersonTokensPropCommitsCommitProp
stelian popstelian pop14100.00%1100.00%
Total14100.00%1100.00%


static void ams_i2c_get_xyz(s8 *x, s8 *y, s8 *z) { *x = ams_i2c_read(AMS_DATAX); *y = ams_i2c_read(AMS_DATAY); *z = ams_i2c_read(AMS_DATAZ); }

Contributors

PersonTokensPropCommitsCommitProp
stelian popstelian pop42100.00%1100.00%
Total42100.00%1100.00%


static int ams_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { int vmaj, vmin; int result; /* There can be only one */ if (unlikely(ams_info.has_device)) return -ENODEV; ams_info.i2c_client = client; if (ams_i2c_cmd(AMS_CMD_RESET)) { printk(KERN_INFO "ams: Failed to reset the device\n"); return -ENODEV; } if (ams_i2c_cmd(AMS_CMD_START)) { printk(KERN_INFO "ams: Failed to start the device\n"); return -ENODEV; } /* get version/vendor information */ ams_i2c_write(AMS_CTRL1, 0x02); ams_i2c_write(AMS_CTRL2, 0x85); ams_i2c_write(AMS_CTRL3, 0x01); ams_i2c_cmd(AMS_CMD_READMEM); vmaj = ams_i2c_read(AMS_DATA1); vmin = ams_i2c_read(AMS_DATA2); if (vmaj != 1 || vmin != 52) { printk(KERN_INFO "ams: Incorrect device version (%d.%d)\n", vmaj, vmin); return -ENODEV; } ams_i2c_cmd(AMS_CMD_VERSION); vmaj = ams_i2c_read(AMS_DATA1); vmin = ams_i2c_read(AMS_DATA2); if (vmaj != 0 || vmin != 1) { printk(KERN_INFO "ams: Incorrect firmware version (%d.%d)\n", vmaj, vmin); return -ENODEV; } /* Disable interrupts */ ams_i2c_set_irq(AMS_IRQ_ALL, 0); result = ams_sensor_attach(); if (result < 0) return result; /* Set default values */ ams_i2c_write(AMS_SENSLOW, 0x15); ams_i2c_write(AMS_SENSHIGH, 0x60); ams_i2c_write(AMS_CTRLX, 0x08); ams_i2c_write(AMS_CTRLY, 0x0F); ams_i2c_write(AMS_CTRLZ, 0x4F); ams_i2c_write(AMS_UNKNOWN1, 0x14); /* Clear interrupts */ ams_i2c_clear_irq(AMS_IRQ_ALL); ams_info.has_device = 1; /* Enable interrupts */ ams_i2c_set_irq(AMS_IRQ_ALL, 1); printk(KERN_INFO "ams: Found I2C based motion sensor\n"); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
stelian popstelian pop27996.54%150.00%
jean delvarejean delvare103.46%150.00%
Total289100.00%2100.00%


static int ams_i2c_remove(struct i2c_client *client) { if (ams_info.has_device) { ams_sensor_detach(); /* Disable interrupts */ ams_i2c_set_irq(AMS_IRQ_ALL, 0); /* Clear interrupts */ ams_i2c_clear_irq(AMS_IRQ_ALL); printk(KERN_INFO "ams: Unloading\n"); ams_info.has_device = 0; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
stelian popstelian pop4588.24%133.33%
jean delvarejean delvare611.76%266.67%
Total51100.00%3100.00%


static void ams_i2c_exit(void) { i2c_del_driver(&ams_i2c_driver); }

Contributors

PersonTokensPropCommitsCommitProp
stelian popstelian pop14100.00%1100.00%
Total14100.00%1100.00%


int __init ams_i2c_init(struct device_node *np) { int result; /* Set implementation stuff */ ams_info.of_node = np; ams_info.exit = ams_i2c_exit; ams_info.get_vendor = ams_i2c_get_vendor; ams_info.get_xyz = ams_i2c_get_xyz; ams_info.clear_irq = ams_i2c_clear_irq; ams_info.bustype = BUS_I2C; result = i2c_add_driver(&ams_i2c_driver); return result; }

Contributors

PersonTokensPropCommitsCommitProp
stelian popstelian pop62100.00%1100.00%
Total62100.00%1100.00%


Overall Contributors

PersonTokensPropCommitsCommitProp
stelian popstelian pop92993.84%240.00%
jean delvarejean delvare606.06%240.00%
benjamin herrenschmidtbenjamin herrenschmidt10.10%120.00%
Total990100.00%5100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}