Linux Audio

Check our new training course

Loading...
v5.9
  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 */
 47enum 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
 59static int ams_i2c_probe(struct i2c_client *client,
 60			 const struct i2c_device_id *id);
 61static int ams_i2c_remove(struct i2c_client *client);
 62
 63static const struct i2c_device_id ams_id[] = {
 64	{ "MAC,accelerometer_1", 0 },
 65	{ }
 66};
 67MODULE_DEVICE_TABLE(i2c, ams_id);
 68
 69static 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
 78static s32 ams_i2c_read(u8 reg)
 79{
 80	return i2c_smbus_read_byte_data(ams_info.i2c_client, reg);
 81}
 82
 83static int ams_i2c_write(u8 reg, u8 value)
 84{
 85	return i2c_smbus_write_byte_data(ams_info.i2c_client, reg, value);
 86}
 87
 88static 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
107static 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
137static 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
146static u8 ams_i2c_get_vendor(void)
147{
148	return ams_i2c_read(AMS_VENDOR);
149}
150
151static 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
158static 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
233static 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
252static void ams_i2c_exit(void)
253{
254	i2c_del_driver(&ams_i2c_driver);
255}
256
257int __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}
v5.14.15
  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 */
 47enum 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
 59static int ams_i2c_probe(struct i2c_client *client,
 60			 const struct i2c_device_id *id);
 61static int ams_i2c_remove(struct i2c_client *client);
 62
 63static const struct i2c_device_id ams_id[] = {
 64	{ "MAC,accelerometer_1", 0 },
 65	{ }
 66};
 67MODULE_DEVICE_TABLE(i2c, ams_id);
 68
 69static 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
 78static s32 ams_i2c_read(u8 reg)
 79{
 80	return i2c_smbus_read_byte_data(ams_info.i2c_client, reg);
 81}
 82
 83static int ams_i2c_write(u8 reg, u8 value)
 84{
 85	return i2c_smbus_write_byte_data(ams_info.i2c_client, reg, value);
 86}
 87
 88static 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
107static 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
137static 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
146static u8 ams_i2c_get_vendor(void)
147{
148	return ams_i2c_read(AMS_VENDOR);
149}
150
151static 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
158static 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
233static 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
252static void ams_i2c_exit(void)
253{
254	i2c_del_driver(&ams_i2c_driver);
255}
256
257int __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}