0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/module.h>
0010 #include <linux/types.h>
0011 #include <linux/errno.h>
0012 #include <linux/init.h>
0013 #include <linux/of_platform.h>
0014 #include <asm/pmac_pfunc.h>
0015
0016 #include "ams.h"
0017
0018
0019 struct ams ams_info;
0020
0021 static bool verbose;
0022 module_param(verbose, bool, 0644);
0023 MODULE_PARM_DESC(verbose, "Show free falls and shocks in kernel output");
0024
0025
0026 void ams_sensors(s8 *x, s8 *y, s8 *z)
0027 {
0028 u32 orient = ams_info.vflag? ams_info.orient1 : ams_info.orient2;
0029
0030 if (orient & 0x80)
0031
0032 ams_info.get_xyz(y, x, z);
0033 else
0034 ams_info.get_xyz(x, y, z);
0035
0036 if (orient & 0x04)
0037 *z = ~(*z);
0038 if (orient & 0x02)
0039 *y = ~(*y);
0040 if (orient & 0x01)
0041 *x = ~(*x);
0042 }
0043
0044 static ssize_t ams_show_current(struct device *dev,
0045 struct device_attribute *attr, char *buf)
0046 {
0047 s8 x, y, z;
0048
0049 mutex_lock(&ams_info.lock);
0050 ams_sensors(&x, &y, &z);
0051 mutex_unlock(&ams_info.lock);
0052
0053 return sysfs_emit(buf, "%d %d %d\n", x, y, z);
0054 }
0055
0056 static DEVICE_ATTR(current, S_IRUGO, ams_show_current, NULL);
0057
0058 static void ams_handle_irq(void *data)
0059 {
0060 enum ams_irq irq = *((enum ams_irq *)data);
0061
0062 spin_lock(&ams_info.irq_lock);
0063
0064 ams_info.worker_irqs |= irq;
0065 schedule_work(&ams_info.worker);
0066
0067 spin_unlock(&ams_info.irq_lock);
0068 }
0069
0070 static enum ams_irq ams_freefall_irq_data = AMS_IRQ_FREEFALL;
0071 static struct pmf_irq_client ams_freefall_client = {
0072 .owner = THIS_MODULE,
0073 .handler = ams_handle_irq,
0074 .data = &ams_freefall_irq_data,
0075 };
0076
0077 static enum ams_irq ams_shock_irq_data = AMS_IRQ_SHOCK;
0078 static struct pmf_irq_client ams_shock_client = {
0079 .owner = THIS_MODULE,
0080 .handler = ams_handle_irq,
0081 .data = &ams_shock_irq_data,
0082 };
0083
0084
0085
0086
0087 static void ams_worker(struct work_struct *work)
0088 {
0089 unsigned long flags;
0090 u8 irqs_to_clear;
0091
0092 mutex_lock(&ams_info.lock);
0093
0094 spin_lock_irqsave(&ams_info.irq_lock, flags);
0095 irqs_to_clear = ams_info.worker_irqs;
0096
0097 if (ams_info.worker_irqs & AMS_IRQ_FREEFALL) {
0098 if (verbose)
0099 printk(KERN_INFO "ams: freefall detected!\n");
0100
0101 ams_info.worker_irqs &= ~AMS_IRQ_FREEFALL;
0102 }
0103
0104 if (ams_info.worker_irqs & AMS_IRQ_SHOCK) {
0105 if (verbose)
0106 printk(KERN_INFO "ams: shock detected!\n");
0107
0108 ams_info.worker_irqs &= ~AMS_IRQ_SHOCK;
0109 }
0110
0111 spin_unlock_irqrestore(&ams_info.irq_lock, flags);
0112
0113 ams_info.clear_irq(irqs_to_clear);
0114
0115 mutex_unlock(&ams_info.lock);
0116 }
0117
0118
0119 int ams_sensor_attach(void)
0120 {
0121 int result;
0122 const u32 *prop;
0123
0124
0125 prop = of_get_property(ams_info.of_node, "orientation", NULL);
0126 if (!prop)
0127 return -ENODEV;
0128 ams_info.orient1 = *prop;
0129 ams_info.orient2 = *(prop + 1);
0130
0131
0132 result = pmf_register_irq_client(ams_info.of_node,
0133 "accel-int-1",
0134 &ams_freefall_client);
0135 if (result < 0)
0136 return -ENODEV;
0137
0138
0139 ams_info.worker_irqs = 0;
0140
0141
0142 result = pmf_register_irq_client(ams_info.of_node,
0143 "accel-int-2",
0144 &ams_shock_client);
0145 if (result < 0)
0146 goto release_freefall;
0147
0148
0149 ams_info.of_dev = of_platform_device_create(ams_info.of_node, "ams", NULL);
0150 if (!ams_info.of_dev) {
0151 result = -ENODEV;
0152 goto release_shock;
0153 }
0154
0155
0156 result = device_create_file(&ams_info.of_dev->dev, &dev_attr_current);
0157 if (result)
0158 goto release_of;
0159
0160 ams_info.vflag = !!(ams_info.get_vendor() & 0x10);
0161
0162
0163 result = ams_input_init();
0164 if (result)
0165 goto release_device_file;
0166
0167 return result;
0168 release_device_file:
0169 device_remove_file(&ams_info.of_dev->dev, &dev_attr_current);
0170 release_of:
0171 of_device_unregister(ams_info.of_dev);
0172 release_shock:
0173 pmf_unregister_irq_client(&ams_shock_client);
0174 release_freefall:
0175 pmf_unregister_irq_client(&ams_freefall_client);
0176 return result;
0177 }
0178
0179 int __init ams_init(void)
0180 {
0181 struct device_node *np;
0182
0183 spin_lock_init(&ams_info.irq_lock);
0184 mutex_init(&ams_info.lock);
0185 INIT_WORK(&ams_info.worker, ams_worker);
0186
0187 #ifdef CONFIG_SENSORS_AMS_I2C
0188 np = of_find_node_by_name(NULL, "accelerometer");
0189 if (np && of_device_is_compatible(np, "AAPL,accelerometer_1"))
0190
0191 return ams_i2c_init(np);
0192 #endif
0193
0194 #ifdef CONFIG_SENSORS_AMS_PMU
0195 np = of_find_node_by_name(NULL, "sms");
0196 if (np && of_device_is_compatible(np, "sms"))
0197
0198 return ams_pmu_init(np);
0199 #endif
0200 return -ENODEV;
0201 }
0202
0203 void ams_sensor_detach(void)
0204 {
0205
0206 ams_input_exit();
0207
0208
0209 device_remove_file(&ams_info.of_dev->dev, &dev_attr_current);
0210
0211
0212
0213
0214
0215
0216 flush_work(&ams_info.worker);
0217
0218
0219 of_device_unregister(ams_info.of_dev);
0220
0221
0222 pmf_unregister_irq_client(&ams_shock_client);
0223 pmf_unregister_irq_client(&ams_freefall_client);
0224 }
0225
0226 static void __exit ams_exit(void)
0227 {
0228
0229 ams_info.exit();
0230 }
0231
0232 MODULE_AUTHOR("Stelian Pop, Michael Hanselmann");
0233 MODULE_DESCRIPTION("Apple Motion Sensor driver");
0234 MODULE_LICENSE("GPL");
0235
0236 module_init(ams_init);
0237 module_exit(ams_exit);