ams-core.c 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * Apple Motion Sensor driver
  4. *
  5. * Copyright (C) 2005 Stelian Pop (stelian@popies.net)
  6. * Copyright (C) 2006 Michael Hanselmann (linux-kernel@hansmi.ch)
  7. */
  8. #include <linux/module.h>
  9. #include <linux/types.h>
  10. #include <linux/errno.h>
  11. #include <linux/init.h>
  12. #include <linux/of_platform.h>
  13. #include <asm/pmac_pfunc.h>
  14. #include "ams.h"
  15. /* There is only one motion sensor per machine */
  16. struct ams ams_info;
  17. static bool verbose;
  18. module_param(verbose, bool, 0644);
  19. MODULE_PARM_DESC(verbose, "Show free falls and shocks in kernel output");
  20. /* Call with ams_info.lock held! */
  21. void ams_sensors(s8 *x, s8 *y, s8 *z)
  22. {
  23. u32 orient = ams_info.vflag? ams_info.orient1 : ams_info.orient2;
  24. if (orient & 0x80)
  25. /* X and Y swapped */
  26. ams_info.get_xyz(y, x, z);
  27. else
  28. ams_info.get_xyz(x, y, z);
  29. if (orient & 0x04)
  30. *z = ~(*z);
  31. if (orient & 0x02)
  32. *y = ~(*y);
  33. if (orient & 0x01)
  34. *x = ~(*x);
  35. }
  36. static ssize_t ams_show_current(struct device *dev,
  37. struct device_attribute *attr, char *buf)
  38. {
  39. s8 x, y, z;
  40. mutex_lock(&ams_info.lock);
  41. ams_sensors(&x, &y, &z);
  42. mutex_unlock(&ams_info.lock);
  43. return sysfs_emit(buf, "%d %d %d\n", x, y, z);
  44. }
  45. static DEVICE_ATTR(current, S_IRUGO, ams_show_current, NULL);
  46. static void ams_handle_irq(void *data)
  47. {
  48. enum ams_irq irq = *((enum ams_irq *)data);
  49. spin_lock(&ams_info.irq_lock);
  50. ams_info.worker_irqs |= irq;
  51. schedule_work(&ams_info.worker);
  52. spin_unlock(&ams_info.irq_lock);
  53. }
  54. static enum ams_irq ams_freefall_irq_data = AMS_IRQ_FREEFALL;
  55. static struct pmf_irq_client ams_freefall_client = {
  56. .owner = THIS_MODULE,
  57. .handler = ams_handle_irq,
  58. .data = &ams_freefall_irq_data,
  59. };
  60. static enum ams_irq ams_shock_irq_data = AMS_IRQ_SHOCK;
  61. static struct pmf_irq_client ams_shock_client = {
  62. .owner = THIS_MODULE,
  63. .handler = ams_handle_irq,
  64. .data = &ams_shock_irq_data,
  65. };
  66. /* Once hard disk parking is implemented in the kernel, this function can
  67. * trigger it.
  68. */
  69. static void ams_worker(struct work_struct *work)
  70. {
  71. unsigned long flags;
  72. u8 irqs_to_clear;
  73. mutex_lock(&ams_info.lock);
  74. spin_lock_irqsave(&ams_info.irq_lock, flags);
  75. irqs_to_clear = ams_info.worker_irqs;
  76. if (ams_info.worker_irqs & AMS_IRQ_FREEFALL) {
  77. if (verbose)
  78. printk(KERN_INFO "ams: freefall detected!\n");
  79. ams_info.worker_irqs &= ~AMS_IRQ_FREEFALL;
  80. }
  81. if (ams_info.worker_irqs & AMS_IRQ_SHOCK) {
  82. if (verbose)
  83. printk(KERN_INFO "ams: shock detected!\n");
  84. ams_info.worker_irqs &= ~AMS_IRQ_SHOCK;
  85. }
  86. spin_unlock_irqrestore(&ams_info.irq_lock, flags);
  87. ams_info.clear_irq(irqs_to_clear);
  88. mutex_unlock(&ams_info.lock);
  89. }
  90. /* Call with ams_info.lock held! */
  91. int ams_sensor_attach(void)
  92. {
  93. int result;
  94. const u32 *prop;
  95. /* Get orientation */
  96. prop = of_get_property(ams_info.of_node, "orientation", NULL);
  97. if (!prop)
  98. return -ENODEV;
  99. ams_info.orient1 = *prop;
  100. ams_info.orient2 = *(prop + 1);
  101. /* Register freefall interrupt handler */
  102. result = pmf_register_irq_client(ams_info.of_node,
  103. "accel-int-1",
  104. &ams_freefall_client);
  105. if (result < 0)
  106. return -ENODEV;
  107. /* Reset saved irqs */
  108. ams_info.worker_irqs = 0;
  109. /* Register shock interrupt handler */
  110. result = pmf_register_irq_client(ams_info.of_node,
  111. "accel-int-2",
  112. &ams_shock_client);
  113. if (result < 0)
  114. goto release_freefall;
  115. /* Create device */
  116. ams_info.of_dev = of_platform_device_create(ams_info.of_node, "ams", NULL);
  117. if (!ams_info.of_dev) {
  118. result = -ENODEV;
  119. goto release_shock;
  120. }
  121. /* Create attributes */
  122. result = device_create_file(&ams_info.of_dev->dev, &dev_attr_current);
  123. if (result)
  124. goto release_of;
  125. ams_info.vflag = !!(ams_info.get_vendor() & 0x10);
  126. /* Init input device */
  127. result = ams_input_init();
  128. if (result)
  129. goto release_device_file;
  130. return result;
  131. release_device_file:
  132. device_remove_file(&ams_info.of_dev->dev, &dev_attr_current);
  133. release_of:
  134. of_device_unregister(ams_info.of_dev);
  135. release_shock:
  136. pmf_unregister_irq_client(&ams_shock_client);
  137. release_freefall:
  138. pmf_unregister_irq_client(&ams_freefall_client);
  139. return result;
  140. }
  141. static int __init ams_init(void)
  142. {
  143. struct device_node *np;
  144. spin_lock_init(&ams_info.irq_lock);
  145. mutex_init(&ams_info.lock);
  146. INIT_WORK(&ams_info.worker, ams_worker);
  147. #ifdef CONFIG_SENSORS_AMS_I2C
  148. np = of_find_node_by_name(NULL, "accelerometer");
  149. if (np && of_device_is_compatible(np, "AAPL,accelerometer_1"))
  150. /* Found I2C motion sensor */
  151. return ams_i2c_init(np);
  152. #endif
  153. #ifdef CONFIG_SENSORS_AMS_PMU
  154. np = of_find_node_by_name(NULL, "sms");
  155. if (np && of_device_is_compatible(np, "sms"))
  156. /* Found PMU motion sensor */
  157. return ams_pmu_init(np);
  158. #endif
  159. return -ENODEV;
  160. }
  161. void ams_sensor_detach(void)
  162. {
  163. /* Remove input device */
  164. ams_input_exit();
  165. /* Remove attributes */
  166. device_remove_file(&ams_info.of_dev->dev, &dev_attr_current);
  167. /* Flush interrupt worker
  168. *
  169. * We do this after ams_info.exit(), because an interrupt might
  170. * have arrived before disabling them.
  171. */
  172. flush_work(&ams_info.worker);
  173. /* Remove device */
  174. of_device_unregister(ams_info.of_dev);
  175. /* Remove handler */
  176. pmf_unregister_irq_client(&ams_shock_client);
  177. pmf_unregister_irq_client(&ams_freefall_client);
  178. }
  179. static void __exit ams_exit(void)
  180. {
  181. /* Shut down implementation */
  182. ams_info.exit();
  183. }
  184. MODULE_AUTHOR("Stelian Pop, Michael Hanselmann");
  185. MODULE_DESCRIPTION("Apple Motion Sensor driver");
  186. MODULE_LICENSE("GPL");
  187. module_init(ams_init);
  188. module_exit(ams_exit);