18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * drivers/hwmon/applesmc.c - driver for Apple's SMC (accelerometer, temperature 48c2ecf20Sopenharmony_ci * sensors, fan control, keyboard backlight control) used in Intel-based Apple 58c2ecf20Sopenharmony_ci * computers. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 2007 Nicolas Boichat <nicolas@boichat.ch> 88c2ecf20Sopenharmony_ci * Copyright (C) 2010 Henrik Rydberg <rydberg@euromail.se> 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Based on hdaps.c driver: 118c2ecf20Sopenharmony_ci * Copyright (C) 2005 Robert Love <rml@novell.com> 128c2ecf20Sopenharmony_ci * Copyright (C) 2005 Jesper Juhl <jj@chaosbits.net> 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * Fan control based on smcFanControl: 158c2ecf20Sopenharmony_ci * Copyright (C) 2006 Hendrik Holtmann <holtmann@mac.com> 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <linux/delay.h> 218c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 228c2ecf20Sopenharmony_ci#include <linux/input.h> 238c2ecf20Sopenharmony_ci#include <linux/kernel.h> 248c2ecf20Sopenharmony_ci#include <linux/slab.h> 258c2ecf20Sopenharmony_ci#include <linux/module.h> 268c2ecf20Sopenharmony_ci#include <linux/timer.h> 278c2ecf20Sopenharmony_ci#include <linux/dmi.h> 288c2ecf20Sopenharmony_ci#include <linux/mutex.h> 298c2ecf20Sopenharmony_ci#include <linux/hwmon-sysfs.h> 308c2ecf20Sopenharmony_ci#include <linux/io.h> 318c2ecf20Sopenharmony_ci#include <linux/leds.h> 328c2ecf20Sopenharmony_ci#include <linux/hwmon.h> 338c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 348c2ecf20Sopenharmony_ci#include <linux/err.h> 358c2ecf20Sopenharmony_ci#include <linux/bits.h> 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* data port used by Apple SMC */ 388c2ecf20Sopenharmony_ci#define APPLESMC_DATA_PORT 0x300 398c2ecf20Sopenharmony_ci/* command/status port used by Apple SMC */ 408c2ecf20Sopenharmony_ci#define APPLESMC_CMD_PORT 0x304 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define APPLESMC_NR_PORTS 32 /* 0x300-0x31f */ 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define APPLESMC_MAX_DATA_LENGTH 32 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/* Apple SMC status bits */ 478c2ecf20Sopenharmony_ci#define SMC_STATUS_AWAITING_DATA BIT(0) /* SMC has data waiting to be read */ 488c2ecf20Sopenharmony_ci#define SMC_STATUS_IB_CLOSED BIT(1) /* Will ignore any input */ 498c2ecf20Sopenharmony_ci#define SMC_STATUS_BUSY BIT(2) /* Command in progress */ 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* Initial wait is 8us */ 528c2ecf20Sopenharmony_ci#define APPLESMC_MIN_WAIT 0x0008 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#define APPLESMC_READ_CMD 0x10 558c2ecf20Sopenharmony_ci#define APPLESMC_WRITE_CMD 0x11 568c2ecf20Sopenharmony_ci#define APPLESMC_GET_KEY_BY_INDEX_CMD 0x12 578c2ecf20Sopenharmony_ci#define APPLESMC_GET_KEY_TYPE_CMD 0x13 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci#define KEY_COUNT_KEY "#KEY" /* r-o ui32 */ 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci#define LIGHT_SENSOR_LEFT_KEY "ALV0" /* r-o {alv (6-10 bytes) */ 628c2ecf20Sopenharmony_ci#define LIGHT_SENSOR_RIGHT_KEY "ALV1" /* r-o {alv (6-10 bytes) */ 638c2ecf20Sopenharmony_ci#define BACKLIGHT_KEY "LKSB" /* w-o {lkb (2 bytes) */ 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci#define CLAMSHELL_KEY "MSLD" /* r-o ui8 (unused) */ 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci#define MOTION_SENSOR_X_KEY "MO_X" /* r-o sp78 (2 bytes) */ 688c2ecf20Sopenharmony_ci#define MOTION_SENSOR_Y_KEY "MO_Y" /* r-o sp78 (2 bytes) */ 698c2ecf20Sopenharmony_ci#define MOTION_SENSOR_Z_KEY "MO_Z" /* r-o sp78 (2 bytes) */ 708c2ecf20Sopenharmony_ci#define MOTION_SENSOR_KEY "MOCN" /* r/w ui16 */ 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci#define FANS_COUNT "FNum" /* r-o ui8 */ 738c2ecf20Sopenharmony_ci#define FANS_MANUAL "FS! " /* r-w ui16 */ 748c2ecf20Sopenharmony_ci#define FAN_ID_FMT "F%dID" /* r-o char[16] */ 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci#define TEMP_SENSOR_TYPE "sp78" 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* List of keys used to read/write fan speeds */ 798c2ecf20Sopenharmony_cistatic const char *const fan_speed_fmt[] = { 808c2ecf20Sopenharmony_ci "F%dAc", /* actual speed */ 818c2ecf20Sopenharmony_ci "F%dMn", /* minimum speed (rw) */ 828c2ecf20Sopenharmony_ci "F%dMx", /* maximum speed */ 838c2ecf20Sopenharmony_ci "F%dSf", /* safe speed - not all models */ 848c2ecf20Sopenharmony_ci "F%dTg", /* target speed (manual: rw) */ 858c2ecf20Sopenharmony_ci}; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci#define INIT_TIMEOUT_MSECS 5000 /* wait up to 5s for device init ... */ 888c2ecf20Sopenharmony_ci#define INIT_WAIT_MSECS 50 /* ... in 50ms increments */ 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci#define APPLESMC_POLL_INTERVAL 50 /* msecs */ 918c2ecf20Sopenharmony_ci#define APPLESMC_INPUT_FUZZ 4 /* input event threshold */ 928c2ecf20Sopenharmony_ci#define APPLESMC_INPUT_FLAT 4 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci#define to_index(attr) (to_sensor_dev_attr(attr)->index & 0xffff) 958c2ecf20Sopenharmony_ci#define to_option(attr) (to_sensor_dev_attr(attr)->index >> 16) 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci/* Dynamic device node attributes */ 988c2ecf20Sopenharmony_cistruct applesmc_dev_attr { 998c2ecf20Sopenharmony_ci struct sensor_device_attribute sda; /* hwmon attributes */ 1008c2ecf20Sopenharmony_ci char name[32]; /* room for node file name */ 1018c2ecf20Sopenharmony_ci}; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci/* Dynamic device node group */ 1048c2ecf20Sopenharmony_cistruct applesmc_node_group { 1058c2ecf20Sopenharmony_ci char *format; /* format string */ 1068c2ecf20Sopenharmony_ci void *show; /* show function */ 1078c2ecf20Sopenharmony_ci void *store; /* store function */ 1088c2ecf20Sopenharmony_ci int option; /* function argument */ 1098c2ecf20Sopenharmony_ci struct applesmc_dev_attr *nodes; /* dynamic node array */ 1108c2ecf20Sopenharmony_ci}; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci/* AppleSMC entry - cached register information */ 1138c2ecf20Sopenharmony_cistruct applesmc_entry { 1148c2ecf20Sopenharmony_ci char key[5]; /* four-letter key code */ 1158c2ecf20Sopenharmony_ci u8 valid; /* set when entry is successfully read once */ 1168c2ecf20Sopenharmony_ci u8 len; /* bounded by APPLESMC_MAX_DATA_LENGTH */ 1178c2ecf20Sopenharmony_ci char type[5]; /* four-letter type code */ 1188c2ecf20Sopenharmony_ci u8 flags; /* 0x10: func; 0x40: write; 0x80: read */ 1198c2ecf20Sopenharmony_ci}; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci/* Register lookup and registers common to all SMCs */ 1228c2ecf20Sopenharmony_cistatic struct applesmc_registers { 1238c2ecf20Sopenharmony_ci struct mutex mutex; /* register read/write mutex */ 1248c2ecf20Sopenharmony_ci unsigned int key_count; /* number of SMC registers */ 1258c2ecf20Sopenharmony_ci unsigned int fan_count; /* number of fans */ 1268c2ecf20Sopenharmony_ci unsigned int temp_count; /* number of temperature registers */ 1278c2ecf20Sopenharmony_ci unsigned int temp_begin; /* temperature lower index bound */ 1288c2ecf20Sopenharmony_ci unsigned int temp_end; /* temperature upper index bound */ 1298c2ecf20Sopenharmony_ci unsigned int index_count; /* size of temperature index array */ 1308c2ecf20Sopenharmony_ci int num_light_sensors; /* number of light sensors */ 1318c2ecf20Sopenharmony_ci bool has_accelerometer; /* has motion sensor */ 1328c2ecf20Sopenharmony_ci bool has_key_backlight; /* has keyboard backlight */ 1338c2ecf20Sopenharmony_ci bool init_complete; /* true when fully initialized */ 1348c2ecf20Sopenharmony_ci struct applesmc_entry *cache; /* cached key entries */ 1358c2ecf20Sopenharmony_ci const char **index; /* temperature key index */ 1368c2ecf20Sopenharmony_ci} smcreg = { 1378c2ecf20Sopenharmony_ci .mutex = __MUTEX_INITIALIZER(smcreg.mutex), 1388c2ecf20Sopenharmony_ci}; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic const int debug; 1418c2ecf20Sopenharmony_cistatic struct platform_device *pdev; 1428c2ecf20Sopenharmony_cistatic s16 rest_x; 1438c2ecf20Sopenharmony_cistatic s16 rest_y; 1448c2ecf20Sopenharmony_cistatic u8 backlight_state[2]; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic struct device *hwmon_dev; 1478c2ecf20Sopenharmony_cistatic struct input_dev *applesmc_idev; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci/* 1508c2ecf20Sopenharmony_ci * Last index written to key_at_index sysfs file, and value to use for all other 1518c2ecf20Sopenharmony_ci * key_at_index_* sysfs files. 1528c2ecf20Sopenharmony_ci */ 1538c2ecf20Sopenharmony_cistatic unsigned int key_at_index; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic struct workqueue_struct *applesmc_led_wq; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci/* 1588c2ecf20Sopenharmony_ci * Wait for specific status bits with a mask on the SMC. 1598c2ecf20Sopenharmony_ci * Used before all transactions. 1608c2ecf20Sopenharmony_ci * This does 10 fast loops of 8us then exponentially backs off for a 1618c2ecf20Sopenharmony_ci * minimum total wait of 262ms. Depending on usleep_range this could 1628c2ecf20Sopenharmony_ci * run out past 500ms. 1638c2ecf20Sopenharmony_ci */ 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic int wait_status(u8 val, u8 mask) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci u8 status; 1688c2ecf20Sopenharmony_ci int us; 1698c2ecf20Sopenharmony_ci int i; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci us = APPLESMC_MIN_WAIT; 1728c2ecf20Sopenharmony_ci for (i = 0; i < 24 ; i++) { 1738c2ecf20Sopenharmony_ci status = inb(APPLESMC_CMD_PORT); 1748c2ecf20Sopenharmony_ci if ((status & mask) == val) 1758c2ecf20Sopenharmony_ci return 0; 1768c2ecf20Sopenharmony_ci usleep_range(us, us * 2); 1778c2ecf20Sopenharmony_ci if (i > 9) 1788c2ecf20Sopenharmony_ci us <<= 1; 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci return -EIO; 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci/* send_byte - Write to SMC data port. Callers must hold applesmc_lock. */ 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic int send_byte(u8 cmd, u16 port) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci int status; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci status = wait_status(0, SMC_STATUS_IB_CLOSED); 1908c2ecf20Sopenharmony_ci if (status) 1918c2ecf20Sopenharmony_ci return status; 1928c2ecf20Sopenharmony_ci /* 1938c2ecf20Sopenharmony_ci * This needs to be a separate read looking for bit 0x04 1948c2ecf20Sopenharmony_ci * after bit 0x02 falls. If consolidated with the wait above 1958c2ecf20Sopenharmony_ci * this extra read may not happen if status returns both 1968c2ecf20Sopenharmony_ci * simultaneously and this would appear to be required. 1978c2ecf20Sopenharmony_ci */ 1988c2ecf20Sopenharmony_ci status = wait_status(SMC_STATUS_BUSY, SMC_STATUS_BUSY); 1998c2ecf20Sopenharmony_ci if (status) 2008c2ecf20Sopenharmony_ci return status; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci outb(cmd, port); 2038c2ecf20Sopenharmony_ci return 0; 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci/* send_command - Write a command to the SMC. Callers must hold applesmc_lock. */ 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic int send_command(u8 cmd) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci int ret; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci ret = wait_status(0, SMC_STATUS_IB_CLOSED); 2138c2ecf20Sopenharmony_ci if (ret) 2148c2ecf20Sopenharmony_ci return ret; 2158c2ecf20Sopenharmony_ci outb(cmd, APPLESMC_CMD_PORT); 2168c2ecf20Sopenharmony_ci return 0; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci/* 2208c2ecf20Sopenharmony_ci * Based on logic from the Apple driver. This is issued before any interaction 2218c2ecf20Sopenharmony_ci * If busy is stuck high, issue a read command to reset the SMC state machine. 2228c2ecf20Sopenharmony_ci * If busy is stuck high after the command then the SMC is jammed. 2238c2ecf20Sopenharmony_ci */ 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic int smc_sane(void) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci int ret; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci ret = wait_status(0, SMC_STATUS_BUSY); 2308c2ecf20Sopenharmony_ci if (!ret) 2318c2ecf20Sopenharmony_ci return ret; 2328c2ecf20Sopenharmony_ci ret = send_command(APPLESMC_READ_CMD); 2338c2ecf20Sopenharmony_ci if (ret) 2348c2ecf20Sopenharmony_ci return ret; 2358c2ecf20Sopenharmony_ci return wait_status(0, SMC_STATUS_BUSY); 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic int send_argument(const char *key) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci int i; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) 2438c2ecf20Sopenharmony_ci if (send_byte(key[i], APPLESMC_DATA_PORT)) 2448c2ecf20Sopenharmony_ci return -EIO; 2458c2ecf20Sopenharmony_ci return 0; 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic int read_smc(u8 cmd, const char *key, u8 *buffer, u8 len) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci u8 status, data = 0; 2518c2ecf20Sopenharmony_ci int i; 2528c2ecf20Sopenharmony_ci int ret; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci ret = smc_sane(); 2558c2ecf20Sopenharmony_ci if (ret) 2568c2ecf20Sopenharmony_ci return ret; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci if (send_command(cmd) || send_argument(key)) { 2598c2ecf20Sopenharmony_ci pr_warn("%.4s: read arg fail\n", key); 2608c2ecf20Sopenharmony_ci return -EIO; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci /* This has no effect on newer (2012) SMCs */ 2648c2ecf20Sopenharmony_ci if (send_byte(len, APPLESMC_DATA_PORT)) { 2658c2ecf20Sopenharmony_ci pr_warn("%.4s: read len fail\n", key); 2668c2ecf20Sopenharmony_ci return -EIO; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) { 2708c2ecf20Sopenharmony_ci if (wait_status(SMC_STATUS_AWAITING_DATA | SMC_STATUS_BUSY, 2718c2ecf20Sopenharmony_ci SMC_STATUS_AWAITING_DATA | SMC_STATUS_BUSY)) { 2728c2ecf20Sopenharmony_ci pr_warn("%.4s: read data[%d] fail\n", key, i); 2738c2ecf20Sopenharmony_ci return -EIO; 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci buffer[i] = inb(APPLESMC_DATA_PORT); 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci /* Read the data port until bit0 is cleared */ 2798c2ecf20Sopenharmony_ci for (i = 0; i < 16; i++) { 2808c2ecf20Sopenharmony_ci udelay(APPLESMC_MIN_WAIT); 2818c2ecf20Sopenharmony_ci status = inb(APPLESMC_CMD_PORT); 2828c2ecf20Sopenharmony_ci if (!(status & SMC_STATUS_AWAITING_DATA)) 2838c2ecf20Sopenharmony_ci break; 2848c2ecf20Sopenharmony_ci data = inb(APPLESMC_DATA_PORT); 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci if (i) 2878c2ecf20Sopenharmony_ci pr_warn("flushed %d bytes, last value is: %d\n", i, data); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci return wait_status(0, SMC_STATUS_BUSY); 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic int write_smc(u8 cmd, const char *key, const u8 *buffer, u8 len) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci int i; 2958c2ecf20Sopenharmony_ci int ret; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci ret = smc_sane(); 2988c2ecf20Sopenharmony_ci if (ret) 2998c2ecf20Sopenharmony_ci return ret; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci if (send_command(cmd) || send_argument(key)) { 3028c2ecf20Sopenharmony_ci pr_warn("%s: write arg fail\n", key); 3038c2ecf20Sopenharmony_ci return -EIO; 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci if (send_byte(len, APPLESMC_DATA_PORT)) { 3078c2ecf20Sopenharmony_ci pr_warn("%.4s: write len fail\n", key); 3088c2ecf20Sopenharmony_ci return -EIO; 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) { 3128c2ecf20Sopenharmony_ci if (send_byte(buffer[i], APPLESMC_DATA_PORT)) { 3138c2ecf20Sopenharmony_ci pr_warn("%s: write data fail\n", key); 3148c2ecf20Sopenharmony_ci return -EIO; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci return wait_status(0, SMC_STATUS_BUSY); 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic int read_register_count(unsigned int *count) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci __be32 be; 3248c2ecf20Sopenharmony_ci int ret; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci ret = read_smc(APPLESMC_READ_CMD, KEY_COUNT_KEY, (u8 *)&be, 4); 3278c2ecf20Sopenharmony_ci if (ret) 3288c2ecf20Sopenharmony_ci return ret; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci *count = be32_to_cpu(be); 3318c2ecf20Sopenharmony_ci return 0; 3328c2ecf20Sopenharmony_ci} 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci/* 3358c2ecf20Sopenharmony_ci * Serialized I/O 3368c2ecf20Sopenharmony_ci * 3378c2ecf20Sopenharmony_ci * Returns zero on success or a negative error on failure. 3388c2ecf20Sopenharmony_ci * All functions below are concurrency safe - callers should NOT hold lock. 3398c2ecf20Sopenharmony_ci */ 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_cistatic int applesmc_read_entry(const struct applesmc_entry *entry, 3428c2ecf20Sopenharmony_ci u8 *buf, u8 len) 3438c2ecf20Sopenharmony_ci{ 3448c2ecf20Sopenharmony_ci int ret; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci if (entry->len != len) 3478c2ecf20Sopenharmony_ci return -EINVAL; 3488c2ecf20Sopenharmony_ci mutex_lock(&smcreg.mutex); 3498c2ecf20Sopenharmony_ci ret = read_smc(APPLESMC_READ_CMD, entry->key, buf, len); 3508c2ecf20Sopenharmony_ci mutex_unlock(&smcreg.mutex); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci return ret; 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cistatic int applesmc_write_entry(const struct applesmc_entry *entry, 3568c2ecf20Sopenharmony_ci const u8 *buf, u8 len) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci int ret; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (entry->len != len) 3618c2ecf20Sopenharmony_ci return -EINVAL; 3628c2ecf20Sopenharmony_ci mutex_lock(&smcreg.mutex); 3638c2ecf20Sopenharmony_ci ret = write_smc(APPLESMC_WRITE_CMD, entry->key, buf, len); 3648c2ecf20Sopenharmony_ci mutex_unlock(&smcreg.mutex); 3658c2ecf20Sopenharmony_ci return ret; 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistatic const struct applesmc_entry *applesmc_get_entry_by_index(int index) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci struct applesmc_entry *cache = &smcreg.cache[index]; 3718c2ecf20Sopenharmony_ci u8 key[4], info[6]; 3728c2ecf20Sopenharmony_ci __be32 be; 3738c2ecf20Sopenharmony_ci int ret = 0; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci if (cache->valid) 3768c2ecf20Sopenharmony_ci return cache; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci mutex_lock(&smcreg.mutex); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci if (cache->valid) 3818c2ecf20Sopenharmony_ci goto out; 3828c2ecf20Sopenharmony_ci be = cpu_to_be32(index); 3838c2ecf20Sopenharmony_ci ret = read_smc(APPLESMC_GET_KEY_BY_INDEX_CMD, (u8 *)&be, key, 4); 3848c2ecf20Sopenharmony_ci if (ret) 3858c2ecf20Sopenharmony_ci goto out; 3868c2ecf20Sopenharmony_ci ret = read_smc(APPLESMC_GET_KEY_TYPE_CMD, key, info, 6); 3878c2ecf20Sopenharmony_ci if (ret) 3888c2ecf20Sopenharmony_ci goto out; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci memcpy(cache->key, key, 4); 3918c2ecf20Sopenharmony_ci cache->len = info[0]; 3928c2ecf20Sopenharmony_ci memcpy(cache->type, &info[1], 4); 3938c2ecf20Sopenharmony_ci cache->flags = info[5]; 3948c2ecf20Sopenharmony_ci cache->valid = 1; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ciout: 3978c2ecf20Sopenharmony_ci mutex_unlock(&smcreg.mutex); 3988c2ecf20Sopenharmony_ci if (ret) 3998c2ecf20Sopenharmony_ci return ERR_PTR(ret); 4008c2ecf20Sopenharmony_ci return cache; 4018c2ecf20Sopenharmony_ci} 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_cistatic int applesmc_get_lower_bound(unsigned int *lo, const char *key) 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci int begin = 0, end = smcreg.key_count; 4068c2ecf20Sopenharmony_ci const struct applesmc_entry *entry; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci while (begin != end) { 4098c2ecf20Sopenharmony_ci int middle = begin + (end - begin) / 2; 4108c2ecf20Sopenharmony_ci entry = applesmc_get_entry_by_index(middle); 4118c2ecf20Sopenharmony_ci if (IS_ERR(entry)) { 4128c2ecf20Sopenharmony_ci *lo = 0; 4138c2ecf20Sopenharmony_ci return PTR_ERR(entry); 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci if (strcmp(entry->key, key) < 0) 4168c2ecf20Sopenharmony_ci begin = middle + 1; 4178c2ecf20Sopenharmony_ci else 4188c2ecf20Sopenharmony_ci end = middle; 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci *lo = begin; 4228c2ecf20Sopenharmony_ci return 0; 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_cistatic int applesmc_get_upper_bound(unsigned int *hi, const char *key) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci int begin = 0, end = smcreg.key_count; 4288c2ecf20Sopenharmony_ci const struct applesmc_entry *entry; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci while (begin != end) { 4318c2ecf20Sopenharmony_ci int middle = begin + (end - begin) / 2; 4328c2ecf20Sopenharmony_ci entry = applesmc_get_entry_by_index(middle); 4338c2ecf20Sopenharmony_ci if (IS_ERR(entry)) { 4348c2ecf20Sopenharmony_ci *hi = smcreg.key_count; 4358c2ecf20Sopenharmony_ci return PTR_ERR(entry); 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci if (strcmp(key, entry->key) < 0) 4388c2ecf20Sopenharmony_ci end = middle; 4398c2ecf20Sopenharmony_ci else 4408c2ecf20Sopenharmony_ci begin = middle + 1; 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci *hi = begin; 4448c2ecf20Sopenharmony_ci return 0; 4458c2ecf20Sopenharmony_ci} 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_cistatic const struct applesmc_entry *applesmc_get_entry_by_key(const char *key) 4488c2ecf20Sopenharmony_ci{ 4498c2ecf20Sopenharmony_ci int begin, end; 4508c2ecf20Sopenharmony_ci int ret; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci ret = applesmc_get_lower_bound(&begin, key); 4538c2ecf20Sopenharmony_ci if (ret) 4548c2ecf20Sopenharmony_ci return ERR_PTR(ret); 4558c2ecf20Sopenharmony_ci ret = applesmc_get_upper_bound(&end, key); 4568c2ecf20Sopenharmony_ci if (ret) 4578c2ecf20Sopenharmony_ci return ERR_PTR(ret); 4588c2ecf20Sopenharmony_ci if (end - begin != 1) 4598c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci return applesmc_get_entry_by_index(begin); 4628c2ecf20Sopenharmony_ci} 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_cistatic int applesmc_read_key(const char *key, u8 *buffer, u8 len) 4658c2ecf20Sopenharmony_ci{ 4668c2ecf20Sopenharmony_ci const struct applesmc_entry *entry; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci entry = applesmc_get_entry_by_key(key); 4698c2ecf20Sopenharmony_ci if (IS_ERR(entry)) 4708c2ecf20Sopenharmony_ci return PTR_ERR(entry); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci return applesmc_read_entry(entry, buffer, len); 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_cistatic int applesmc_write_key(const char *key, const u8 *buffer, u8 len) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci const struct applesmc_entry *entry; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci entry = applesmc_get_entry_by_key(key); 4808c2ecf20Sopenharmony_ci if (IS_ERR(entry)) 4818c2ecf20Sopenharmony_ci return PTR_ERR(entry); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci return applesmc_write_entry(entry, buffer, len); 4848c2ecf20Sopenharmony_ci} 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_cistatic int applesmc_has_key(const char *key, bool *value) 4878c2ecf20Sopenharmony_ci{ 4888c2ecf20Sopenharmony_ci const struct applesmc_entry *entry; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci entry = applesmc_get_entry_by_key(key); 4918c2ecf20Sopenharmony_ci if (IS_ERR(entry) && PTR_ERR(entry) != -EINVAL) 4928c2ecf20Sopenharmony_ci return PTR_ERR(entry); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci *value = !IS_ERR(entry); 4958c2ecf20Sopenharmony_ci return 0; 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci/* 4998c2ecf20Sopenharmony_ci * applesmc_read_s16 - Read 16-bit signed big endian register 5008c2ecf20Sopenharmony_ci */ 5018c2ecf20Sopenharmony_cistatic int applesmc_read_s16(const char *key, s16 *value) 5028c2ecf20Sopenharmony_ci{ 5038c2ecf20Sopenharmony_ci u8 buffer[2]; 5048c2ecf20Sopenharmony_ci int ret; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci ret = applesmc_read_key(key, buffer, 2); 5078c2ecf20Sopenharmony_ci if (ret) 5088c2ecf20Sopenharmony_ci return ret; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci *value = ((s16)buffer[0] << 8) | buffer[1]; 5118c2ecf20Sopenharmony_ci return 0; 5128c2ecf20Sopenharmony_ci} 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci/* 5158c2ecf20Sopenharmony_ci * applesmc_device_init - initialize the accelerometer. Can sleep. 5168c2ecf20Sopenharmony_ci */ 5178c2ecf20Sopenharmony_cistatic void applesmc_device_init(void) 5188c2ecf20Sopenharmony_ci{ 5198c2ecf20Sopenharmony_ci int total; 5208c2ecf20Sopenharmony_ci u8 buffer[2]; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci if (!smcreg.has_accelerometer) 5238c2ecf20Sopenharmony_ci return; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci for (total = INIT_TIMEOUT_MSECS; total > 0; total -= INIT_WAIT_MSECS) { 5268c2ecf20Sopenharmony_ci if (!applesmc_read_key(MOTION_SENSOR_KEY, buffer, 2) && 5278c2ecf20Sopenharmony_ci (buffer[0] != 0x00 || buffer[1] != 0x00)) 5288c2ecf20Sopenharmony_ci return; 5298c2ecf20Sopenharmony_ci buffer[0] = 0xe0; 5308c2ecf20Sopenharmony_ci buffer[1] = 0x00; 5318c2ecf20Sopenharmony_ci applesmc_write_key(MOTION_SENSOR_KEY, buffer, 2); 5328c2ecf20Sopenharmony_ci msleep(INIT_WAIT_MSECS); 5338c2ecf20Sopenharmony_ci } 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci pr_warn("failed to init the device\n"); 5368c2ecf20Sopenharmony_ci} 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_cistatic int applesmc_init_index(struct applesmc_registers *s) 5398c2ecf20Sopenharmony_ci{ 5408c2ecf20Sopenharmony_ci const struct applesmc_entry *entry; 5418c2ecf20Sopenharmony_ci unsigned int i; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci if (s->index) 5448c2ecf20Sopenharmony_ci return 0; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci s->index = kcalloc(s->temp_count, sizeof(s->index[0]), GFP_KERNEL); 5478c2ecf20Sopenharmony_ci if (!s->index) 5488c2ecf20Sopenharmony_ci return -ENOMEM; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci for (i = s->temp_begin; i < s->temp_end; i++) { 5518c2ecf20Sopenharmony_ci entry = applesmc_get_entry_by_index(i); 5528c2ecf20Sopenharmony_ci if (IS_ERR(entry)) 5538c2ecf20Sopenharmony_ci continue; 5548c2ecf20Sopenharmony_ci if (strcmp(entry->type, TEMP_SENSOR_TYPE)) 5558c2ecf20Sopenharmony_ci continue; 5568c2ecf20Sopenharmony_ci s->index[s->index_count++] = entry->key; 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci return 0; 5608c2ecf20Sopenharmony_ci} 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci/* 5638c2ecf20Sopenharmony_ci * applesmc_init_smcreg_try - Try to initialize register cache. Idempotent. 5648c2ecf20Sopenharmony_ci */ 5658c2ecf20Sopenharmony_cistatic int applesmc_init_smcreg_try(void) 5668c2ecf20Sopenharmony_ci{ 5678c2ecf20Sopenharmony_ci struct applesmc_registers *s = &smcreg; 5688c2ecf20Sopenharmony_ci bool left_light_sensor = 0, right_light_sensor = 0; 5698c2ecf20Sopenharmony_ci unsigned int count; 5708c2ecf20Sopenharmony_ci u8 tmp[1]; 5718c2ecf20Sopenharmony_ci int ret; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci if (s->init_complete) 5748c2ecf20Sopenharmony_ci return 0; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci ret = read_register_count(&count); 5778c2ecf20Sopenharmony_ci if (ret) 5788c2ecf20Sopenharmony_ci return ret; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci if (s->cache && s->key_count != count) { 5818c2ecf20Sopenharmony_ci pr_warn("key count changed from %d to %d\n", 5828c2ecf20Sopenharmony_ci s->key_count, count); 5838c2ecf20Sopenharmony_ci kfree(s->cache); 5848c2ecf20Sopenharmony_ci s->cache = NULL; 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci s->key_count = count; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci if (!s->cache) 5898c2ecf20Sopenharmony_ci s->cache = kcalloc(s->key_count, sizeof(*s->cache), GFP_KERNEL); 5908c2ecf20Sopenharmony_ci if (!s->cache) 5918c2ecf20Sopenharmony_ci return -ENOMEM; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci ret = applesmc_read_key(FANS_COUNT, tmp, 1); 5948c2ecf20Sopenharmony_ci if (ret) 5958c2ecf20Sopenharmony_ci return ret; 5968c2ecf20Sopenharmony_ci s->fan_count = tmp[0]; 5978c2ecf20Sopenharmony_ci if (s->fan_count > 10) 5988c2ecf20Sopenharmony_ci s->fan_count = 10; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci ret = applesmc_get_lower_bound(&s->temp_begin, "T"); 6018c2ecf20Sopenharmony_ci if (ret) 6028c2ecf20Sopenharmony_ci return ret; 6038c2ecf20Sopenharmony_ci ret = applesmc_get_lower_bound(&s->temp_end, "U"); 6048c2ecf20Sopenharmony_ci if (ret) 6058c2ecf20Sopenharmony_ci return ret; 6068c2ecf20Sopenharmony_ci s->temp_count = s->temp_end - s->temp_begin; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci ret = applesmc_init_index(s); 6098c2ecf20Sopenharmony_ci if (ret) 6108c2ecf20Sopenharmony_ci return ret; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci ret = applesmc_has_key(LIGHT_SENSOR_LEFT_KEY, &left_light_sensor); 6138c2ecf20Sopenharmony_ci if (ret) 6148c2ecf20Sopenharmony_ci return ret; 6158c2ecf20Sopenharmony_ci ret = applesmc_has_key(LIGHT_SENSOR_RIGHT_KEY, &right_light_sensor); 6168c2ecf20Sopenharmony_ci if (ret) 6178c2ecf20Sopenharmony_ci return ret; 6188c2ecf20Sopenharmony_ci ret = applesmc_has_key(MOTION_SENSOR_KEY, &s->has_accelerometer); 6198c2ecf20Sopenharmony_ci if (ret) 6208c2ecf20Sopenharmony_ci return ret; 6218c2ecf20Sopenharmony_ci ret = applesmc_has_key(BACKLIGHT_KEY, &s->has_key_backlight); 6228c2ecf20Sopenharmony_ci if (ret) 6238c2ecf20Sopenharmony_ci return ret; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci s->num_light_sensors = left_light_sensor + right_light_sensor; 6268c2ecf20Sopenharmony_ci s->init_complete = true; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci pr_info("key=%d fan=%d temp=%d index=%d acc=%d lux=%d kbd=%d\n", 6298c2ecf20Sopenharmony_ci s->key_count, s->fan_count, s->temp_count, s->index_count, 6308c2ecf20Sopenharmony_ci s->has_accelerometer, 6318c2ecf20Sopenharmony_ci s->num_light_sensors, 6328c2ecf20Sopenharmony_ci s->has_key_backlight); 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci return 0; 6358c2ecf20Sopenharmony_ci} 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_cistatic void applesmc_destroy_smcreg(void) 6388c2ecf20Sopenharmony_ci{ 6398c2ecf20Sopenharmony_ci kfree(smcreg.index); 6408c2ecf20Sopenharmony_ci smcreg.index = NULL; 6418c2ecf20Sopenharmony_ci kfree(smcreg.cache); 6428c2ecf20Sopenharmony_ci smcreg.cache = NULL; 6438c2ecf20Sopenharmony_ci smcreg.init_complete = false; 6448c2ecf20Sopenharmony_ci} 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci/* 6478c2ecf20Sopenharmony_ci * applesmc_init_smcreg - Initialize register cache. 6488c2ecf20Sopenharmony_ci * 6498c2ecf20Sopenharmony_ci * Retries until initialization is successful, or the operation times out. 6508c2ecf20Sopenharmony_ci * 6518c2ecf20Sopenharmony_ci */ 6528c2ecf20Sopenharmony_cistatic int applesmc_init_smcreg(void) 6538c2ecf20Sopenharmony_ci{ 6548c2ecf20Sopenharmony_ci int ms, ret; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci for (ms = 0; ms < INIT_TIMEOUT_MSECS; ms += INIT_WAIT_MSECS) { 6578c2ecf20Sopenharmony_ci ret = applesmc_init_smcreg_try(); 6588c2ecf20Sopenharmony_ci if (!ret) { 6598c2ecf20Sopenharmony_ci if (ms) 6608c2ecf20Sopenharmony_ci pr_info("init_smcreg() took %d ms\n", ms); 6618c2ecf20Sopenharmony_ci return 0; 6628c2ecf20Sopenharmony_ci } 6638c2ecf20Sopenharmony_ci msleep(INIT_WAIT_MSECS); 6648c2ecf20Sopenharmony_ci } 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci applesmc_destroy_smcreg(); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci return ret; 6698c2ecf20Sopenharmony_ci} 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci/* Device model stuff */ 6728c2ecf20Sopenharmony_cistatic int applesmc_probe(struct platform_device *dev) 6738c2ecf20Sopenharmony_ci{ 6748c2ecf20Sopenharmony_ci int ret; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci ret = applesmc_init_smcreg(); 6778c2ecf20Sopenharmony_ci if (ret) 6788c2ecf20Sopenharmony_ci return ret; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci applesmc_device_init(); 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci return 0; 6838c2ecf20Sopenharmony_ci} 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci/* Synchronize device with memorized backlight state */ 6868c2ecf20Sopenharmony_cistatic int applesmc_pm_resume(struct device *dev) 6878c2ecf20Sopenharmony_ci{ 6888c2ecf20Sopenharmony_ci if (smcreg.has_key_backlight) 6898c2ecf20Sopenharmony_ci applesmc_write_key(BACKLIGHT_KEY, backlight_state, 2); 6908c2ecf20Sopenharmony_ci return 0; 6918c2ecf20Sopenharmony_ci} 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci/* Reinitialize device on resume from hibernation */ 6948c2ecf20Sopenharmony_cistatic int applesmc_pm_restore(struct device *dev) 6958c2ecf20Sopenharmony_ci{ 6968c2ecf20Sopenharmony_ci applesmc_device_init(); 6978c2ecf20Sopenharmony_ci return applesmc_pm_resume(dev); 6988c2ecf20Sopenharmony_ci} 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_cistatic const struct dev_pm_ops applesmc_pm_ops = { 7018c2ecf20Sopenharmony_ci .resume = applesmc_pm_resume, 7028c2ecf20Sopenharmony_ci .restore = applesmc_pm_restore, 7038c2ecf20Sopenharmony_ci}; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_cistatic struct platform_driver applesmc_driver = { 7068c2ecf20Sopenharmony_ci .probe = applesmc_probe, 7078c2ecf20Sopenharmony_ci .driver = { 7088c2ecf20Sopenharmony_ci .name = "applesmc", 7098c2ecf20Sopenharmony_ci .pm = &applesmc_pm_ops, 7108c2ecf20Sopenharmony_ci }, 7118c2ecf20Sopenharmony_ci}; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci/* 7148c2ecf20Sopenharmony_ci * applesmc_calibrate - Set our "resting" values. Callers must 7158c2ecf20Sopenharmony_ci * hold applesmc_lock. 7168c2ecf20Sopenharmony_ci */ 7178c2ecf20Sopenharmony_cistatic void applesmc_calibrate(void) 7188c2ecf20Sopenharmony_ci{ 7198c2ecf20Sopenharmony_ci applesmc_read_s16(MOTION_SENSOR_X_KEY, &rest_x); 7208c2ecf20Sopenharmony_ci applesmc_read_s16(MOTION_SENSOR_Y_KEY, &rest_y); 7218c2ecf20Sopenharmony_ci rest_x = -rest_x; 7228c2ecf20Sopenharmony_ci} 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_cistatic void applesmc_idev_poll(struct input_dev *idev) 7258c2ecf20Sopenharmony_ci{ 7268c2ecf20Sopenharmony_ci s16 x, y; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci if (applesmc_read_s16(MOTION_SENSOR_X_KEY, &x)) 7298c2ecf20Sopenharmony_ci return; 7308c2ecf20Sopenharmony_ci if (applesmc_read_s16(MOTION_SENSOR_Y_KEY, &y)) 7318c2ecf20Sopenharmony_ci return; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci x = -x; 7348c2ecf20Sopenharmony_ci input_report_abs(idev, ABS_X, x - rest_x); 7358c2ecf20Sopenharmony_ci input_report_abs(idev, ABS_Y, y - rest_y); 7368c2ecf20Sopenharmony_ci input_sync(idev); 7378c2ecf20Sopenharmony_ci} 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci/* Sysfs Files */ 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_cistatic ssize_t applesmc_name_show(struct device *dev, 7428c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 7438c2ecf20Sopenharmony_ci{ 7448c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "applesmc\n"); 7458c2ecf20Sopenharmony_ci} 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_cistatic ssize_t applesmc_position_show(struct device *dev, 7488c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 7498c2ecf20Sopenharmony_ci{ 7508c2ecf20Sopenharmony_ci int ret; 7518c2ecf20Sopenharmony_ci s16 x, y, z; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci ret = applesmc_read_s16(MOTION_SENSOR_X_KEY, &x); 7548c2ecf20Sopenharmony_ci if (ret) 7558c2ecf20Sopenharmony_ci goto out; 7568c2ecf20Sopenharmony_ci ret = applesmc_read_s16(MOTION_SENSOR_Y_KEY, &y); 7578c2ecf20Sopenharmony_ci if (ret) 7588c2ecf20Sopenharmony_ci goto out; 7598c2ecf20Sopenharmony_ci ret = applesmc_read_s16(MOTION_SENSOR_Z_KEY, &z); 7608c2ecf20Sopenharmony_ci if (ret) 7618c2ecf20Sopenharmony_ci goto out; 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ciout: 7648c2ecf20Sopenharmony_ci if (ret) 7658c2ecf20Sopenharmony_ci return ret; 7668c2ecf20Sopenharmony_ci else 7678c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "(%d,%d,%d)\n", x, y, z); 7688c2ecf20Sopenharmony_ci} 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_cistatic ssize_t applesmc_light_show(struct device *dev, 7718c2ecf20Sopenharmony_ci struct device_attribute *attr, char *sysfsbuf) 7728c2ecf20Sopenharmony_ci{ 7738c2ecf20Sopenharmony_ci const struct applesmc_entry *entry; 7748c2ecf20Sopenharmony_ci static int data_length; 7758c2ecf20Sopenharmony_ci int ret; 7768c2ecf20Sopenharmony_ci u8 left = 0, right = 0; 7778c2ecf20Sopenharmony_ci u8 buffer[10]; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci if (!data_length) { 7808c2ecf20Sopenharmony_ci entry = applesmc_get_entry_by_key(LIGHT_SENSOR_LEFT_KEY); 7818c2ecf20Sopenharmony_ci if (IS_ERR(entry)) 7828c2ecf20Sopenharmony_ci return PTR_ERR(entry); 7838c2ecf20Sopenharmony_ci if (entry->len > 10) 7848c2ecf20Sopenharmony_ci return -ENXIO; 7858c2ecf20Sopenharmony_ci data_length = entry->len; 7868c2ecf20Sopenharmony_ci pr_info("light sensor data length set to %d\n", data_length); 7878c2ecf20Sopenharmony_ci } 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci ret = applesmc_read_key(LIGHT_SENSOR_LEFT_KEY, buffer, data_length); 7908c2ecf20Sopenharmony_ci if (ret) 7918c2ecf20Sopenharmony_ci goto out; 7928c2ecf20Sopenharmony_ci /* newer macbooks report a single 10-bit bigendian value */ 7938c2ecf20Sopenharmony_ci if (data_length == 10) { 7948c2ecf20Sopenharmony_ci left = be16_to_cpu(*(__be16 *)(buffer + 6)) >> 2; 7958c2ecf20Sopenharmony_ci goto out; 7968c2ecf20Sopenharmony_ci } 7978c2ecf20Sopenharmony_ci left = buffer[2]; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci ret = applesmc_read_key(LIGHT_SENSOR_RIGHT_KEY, buffer, data_length); 8008c2ecf20Sopenharmony_ci if (ret) 8018c2ecf20Sopenharmony_ci goto out; 8028c2ecf20Sopenharmony_ci right = buffer[2]; 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ciout: 8058c2ecf20Sopenharmony_ci if (ret) 8068c2ecf20Sopenharmony_ci return ret; 8078c2ecf20Sopenharmony_ci else 8088c2ecf20Sopenharmony_ci return snprintf(sysfsbuf, PAGE_SIZE, "(%d,%d)\n", left, right); 8098c2ecf20Sopenharmony_ci} 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci/* Displays sensor key as label */ 8128c2ecf20Sopenharmony_cistatic ssize_t applesmc_show_sensor_label(struct device *dev, 8138c2ecf20Sopenharmony_ci struct device_attribute *devattr, char *sysfsbuf) 8148c2ecf20Sopenharmony_ci{ 8158c2ecf20Sopenharmony_ci const char *key = smcreg.index[to_index(devattr)]; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", key); 8188c2ecf20Sopenharmony_ci} 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci/* Displays degree Celsius * 1000 */ 8218c2ecf20Sopenharmony_cistatic ssize_t applesmc_show_temperature(struct device *dev, 8228c2ecf20Sopenharmony_ci struct device_attribute *devattr, char *sysfsbuf) 8238c2ecf20Sopenharmony_ci{ 8248c2ecf20Sopenharmony_ci const char *key = smcreg.index[to_index(devattr)]; 8258c2ecf20Sopenharmony_ci int ret; 8268c2ecf20Sopenharmony_ci s16 value; 8278c2ecf20Sopenharmony_ci int temp; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci ret = applesmc_read_s16(key, &value); 8308c2ecf20Sopenharmony_ci if (ret) 8318c2ecf20Sopenharmony_ci return ret; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci temp = 250 * (value >> 6); 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", temp); 8368c2ecf20Sopenharmony_ci} 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_cistatic ssize_t applesmc_show_fan_speed(struct device *dev, 8398c2ecf20Sopenharmony_ci struct device_attribute *attr, char *sysfsbuf) 8408c2ecf20Sopenharmony_ci{ 8418c2ecf20Sopenharmony_ci int ret; 8428c2ecf20Sopenharmony_ci unsigned int speed = 0; 8438c2ecf20Sopenharmony_ci char newkey[5]; 8448c2ecf20Sopenharmony_ci u8 buffer[2]; 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci scnprintf(newkey, sizeof(newkey), fan_speed_fmt[to_option(attr)], 8478c2ecf20Sopenharmony_ci to_index(attr)); 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci ret = applesmc_read_key(newkey, buffer, 2); 8508c2ecf20Sopenharmony_ci if (ret) 8518c2ecf20Sopenharmony_ci return ret; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci speed = ((buffer[0] << 8 | buffer[1]) >> 2); 8548c2ecf20Sopenharmony_ci return snprintf(sysfsbuf, PAGE_SIZE, "%u\n", speed); 8558c2ecf20Sopenharmony_ci} 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_cistatic ssize_t applesmc_store_fan_speed(struct device *dev, 8588c2ecf20Sopenharmony_ci struct device_attribute *attr, 8598c2ecf20Sopenharmony_ci const char *sysfsbuf, size_t count) 8608c2ecf20Sopenharmony_ci{ 8618c2ecf20Sopenharmony_ci int ret; 8628c2ecf20Sopenharmony_ci unsigned long speed; 8638c2ecf20Sopenharmony_ci char newkey[5]; 8648c2ecf20Sopenharmony_ci u8 buffer[2]; 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci if (kstrtoul(sysfsbuf, 10, &speed) < 0 || speed >= 0x4000) 8678c2ecf20Sopenharmony_ci return -EINVAL; /* Bigger than a 14-bit value */ 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci scnprintf(newkey, sizeof(newkey), fan_speed_fmt[to_option(attr)], 8708c2ecf20Sopenharmony_ci to_index(attr)); 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci buffer[0] = (speed >> 6) & 0xff; 8738c2ecf20Sopenharmony_ci buffer[1] = (speed << 2) & 0xff; 8748c2ecf20Sopenharmony_ci ret = applesmc_write_key(newkey, buffer, 2); 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci if (ret) 8778c2ecf20Sopenharmony_ci return ret; 8788c2ecf20Sopenharmony_ci else 8798c2ecf20Sopenharmony_ci return count; 8808c2ecf20Sopenharmony_ci} 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_cistatic ssize_t applesmc_show_fan_manual(struct device *dev, 8838c2ecf20Sopenharmony_ci struct device_attribute *attr, char *sysfsbuf) 8848c2ecf20Sopenharmony_ci{ 8858c2ecf20Sopenharmony_ci int ret; 8868c2ecf20Sopenharmony_ci u16 manual = 0; 8878c2ecf20Sopenharmony_ci u8 buffer[2]; 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci ret = applesmc_read_key(FANS_MANUAL, buffer, 2); 8908c2ecf20Sopenharmony_ci if (ret) 8918c2ecf20Sopenharmony_ci return ret; 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci manual = ((buffer[0] << 8 | buffer[1]) >> to_index(attr)) & 0x01; 8948c2ecf20Sopenharmony_ci return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", manual); 8958c2ecf20Sopenharmony_ci} 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_cistatic ssize_t applesmc_store_fan_manual(struct device *dev, 8988c2ecf20Sopenharmony_ci struct device_attribute *attr, 8998c2ecf20Sopenharmony_ci const char *sysfsbuf, size_t count) 9008c2ecf20Sopenharmony_ci{ 9018c2ecf20Sopenharmony_ci int ret; 9028c2ecf20Sopenharmony_ci u8 buffer[2]; 9038c2ecf20Sopenharmony_ci unsigned long input; 9048c2ecf20Sopenharmony_ci u16 val; 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci if (kstrtoul(sysfsbuf, 10, &input) < 0) 9078c2ecf20Sopenharmony_ci return -EINVAL; 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci ret = applesmc_read_key(FANS_MANUAL, buffer, 2); 9108c2ecf20Sopenharmony_ci if (ret) 9118c2ecf20Sopenharmony_ci goto out; 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci val = (buffer[0] << 8 | buffer[1]); 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci if (input) 9168c2ecf20Sopenharmony_ci val = val | (0x01 << to_index(attr)); 9178c2ecf20Sopenharmony_ci else 9188c2ecf20Sopenharmony_ci val = val & ~(0x01 << to_index(attr)); 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci buffer[0] = (val >> 8) & 0xFF; 9218c2ecf20Sopenharmony_ci buffer[1] = val & 0xFF; 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci ret = applesmc_write_key(FANS_MANUAL, buffer, 2); 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ciout: 9268c2ecf20Sopenharmony_ci if (ret) 9278c2ecf20Sopenharmony_ci return ret; 9288c2ecf20Sopenharmony_ci else 9298c2ecf20Sopenharmony_ci return count; 9308c2ecf20Sopenharmony_ci} 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_cistatic ssize_t applesmc_show_fan_position(struct device *dev, 9338c2ecf20Sopenharmony_ci struct device_attribute *attr, char *sysfsbuf) 9348c2ecf20Sopenharmony_ci{ 9358c2ecf20Sopenharmony_ci int ret; 9368c2ecf20Sopenharmony_ci char newkey[5]; 9378c2ecf20Sopenharmony_ci u8 buffer[17]; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci scnprintf(newkey, sizeof(newkey), FAN_ID_FMT, to_index(attr)); 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci ret = applesmc_read_key(newkey, buffer, 16); 9428c2ecf20Sopenharmony_ci buffer[16] = 0; 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci if (ret) 9458c2ecf20Sopenharmony_ci return ret; 9468c2ecf20Sopenharmony_ci else 9478c2ecf20Sopenharmony_ci return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", buffer+4); 9488c2ecf20Sopenharmony_ci} 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_cistatic ssize_t applesmc_calibrate_show(struct device *dev, 9518c2ecf20Sopenharmony_ci struct device_attribute *attr, char *sysfsbuf) 9528c2ecf20Sopenharmony_ci{ 9538c2ecf20Sopenharmony_ci return snprintf(sysfsbuf, PAGE_SIZE, "(%d,%d)\n", rest_x, rest_y); 9548c2ecf20Sopenharmony_ci} 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_cistatic ssize_t applesmc_calibrate_store(struct device *dev, 9578c2ecf20Sopenharmony_ci struct device_attribute *attr, const char *sysfsbuf, size_t count) 9588c2ecf20Sopenharmony_ci{ 9598c2ecf20Sopenharmony_ci applesmc_calibrate(); 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci return count; 9628c2ecf20Sopenharmony_ci} 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_cistatic void applesmc_backlight_set(struct work_struct *work) 9658c2ecf20Sopenharmony_ci{ 9668c2ecf20Sopenharmony_ci applesmc_write_key(BACKLIGHT_KEY, backlight_state, 2); 9678c2ecf20Sopenharmony_ci} 9688c2ecf20Sopenharmony_cistatic DECLARE_WORK(backlight_work, &applesmc_backlight_set); 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_cistatic void applesmc_brightness_set(struct led_classdev *led_cdev, 9718c2ecf20Sopenharmony_ci enum led_brightness value) 9728c2ecf20Sopenharmony_ci{ 9738c2ecf20Sopenharmony_ci int ret; 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci backlight_state[0] = value; 9768c2ecf20Sopenharmony_ci ret = queue_work(applesmc_led_wq, &backlight_work); 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci if (debug && (!ret)) 9798c2ecf20Sopenharmony_ci dev_dbg(led_cdev->dev, "work was already on the queue.\n"); 9808c2ecf20Sopenharmony_ci} 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_cistatic ssize_t applesmc_key_count_show(struct device *dev, 9838c2ecf20Sopenharmony_ci struct device_attribute *attr, char *sysfsbuf) 9848c2ecf20Sopenharmony_ci{ 9858c2ecf20Sopenharmony_ci int ret; 9868c2ecf20Sopenharmony_ci u8 buffer[4]; 9878c2ecf20Sopenharmony_ci u32 count; 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci ret = applesmc_read_key(KEY_COUNT_KEY, buffer, 4); 9908c2ecf20Sopenharmony_ci if (ret) 9918c2ecf20Sopenharmony_ci return ret; 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci count = ((u32)buffer[0]<<24) + ((u32)buffer[1]<<16) + 9948c2ecf20Sopenharmony_ci ((u32)buffer[2]<<8) + buffer[3]; 9958c2ecf20Sopenharmony_ci return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", count); 9968c2ecf20Sopenharmony_ci} 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_cistatic ssize_t applesmc_key_at_index_read_show(struct device *dev, 9998c2ecf20Sopenharmony_ci struct device_attribute *attr, char *sysfsbuf) 10008c2ecf20Sopenharmony_ci{ 10018c2ecf20Sopenharmony_ci const struct applesmc_entry *entry; 10028c2ecf20Sopenharmony_ci int ret; 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci entry = applesmc_get_entry_by_index(key_at_index); 10058c2ecf20Sopenharmony_ci if (IS_ERR(entry)) 10068c2ecf20Sopenharmony_ci return PTR_ERR(entry); 10078c2ecf20Sopenharmony_ci ret = applesmc_read_entry(entry, sysfsbuf, entry->len); 10088c2ecf20Sopenharmony_ci if (ret) 10098c2ecf20Sopenharmony_ci return ret; 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci return entry->len; 10128c2ecf20Sopenharmony_ci} 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_cistatic ssize_t applesmc_key_at_index_data_length_show(struct device *dev, 10158c2ecf20Sopenharmony_ci struct device_attribute *attr, char *sysfsbuf) 10168c2ecf20Sopenharmony_ci{ 10178c2ecf20Sopenharmony_ci const struct applesmc_entry *entry; 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci entry = applesmc_get_entry_by_index(key_at_index); 10208c2ecf20Sopenharmony_ci if (IS_ERR(entry)) 10218c2ecf20Sopenharmony_ci return PTR_ERR(entry); 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", entry->len); 10248c2ecf20Sopenharmony_ci} 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_cistatic ssize_t applesmc_key_at_index_type_show(struct device *dev, 10278c2ecf20Sopenharmony_ci struct device_attribute *attr, char *sysfsbuf) 10288c2ecf20Sopenharmony_ci{ 10298c2ecf20Sopenharmony_ci const struct applesmc_entry *entry; 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci entry = applesmc_get_entry_by_index(key_at_index); 10328c2ecf20Sopenharmony_ci if (IS_ERR(entry)) 10338c2ecf20Sopenharmony_ci return PTR_ERR(entry); 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", entry->type); 10368c2ecf20Sopenharmony_ci} 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_cistatic ssize_t applesmc_key_at_index_name_show(struct device *dev, 10398c2ecf20Sopenharmony_ci struct device_attribute *attr, char *sysfsbuf) 10408c2ecf20Sopenharmony_ci{ 10418c2ecf20Sopenharmony_ci const struct applesmc_entry *entry; 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci entry = applesmc_get_entry_by_index(key_at_index); 10448c2ecf20Sopenharmony_ci if (IS_ERR(entry)) 10458c2ecf20Sopenharmony_ci return PTR_ERR(entry); 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", entry->key); 10488c2ecf20Sopenharmony_ci} 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_cistatic ssize_t applesmc_key_at_index_show(struct device *dev, 10518c2ecf20Sopenharmony_ci struct device_attribute *attr, char *sysfsbuf) 10528c2ecf20Sopenharmony_ci{ 10538c2ecf20Sopenharmony_ci return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", key_at_index); 10548c2ecf20Sopenharmony_ci} 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_cistatic ssize_t applesmc_key_at_index_store(struct device *dev, 10578c2ecf20Sopenharmony_ci struct device_attribute *attr, const char *sysfsbuf, size_t count) 10588c2ecf20Sopenharmony_ci{ 10598c2ecf20Sopenharmony_ci unsigned long newkey; 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci if (kstrtoul(sysfsbuf, 10, &newkey) < 0 10628c2ecf20Sopenharmony_ci || newkey >= smcreg.key_count) 10638c2ecf20Sopenharmony_ci return -EINVAL; 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci key_at_index = newkey; 10668c2ecf20Sopenharmony_ci return count; 10678c2ecf20Sopenharmony_ci} 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_cistatic struct led_classdev applesmc_backlight = { 10708c2ecf20Sopenharmony_ci .name = "smc::kbd_backlight", 10718c2ecf20Sopenharmony_ci .default_trigger = "nand-disk", 10728c2ecf20Sopenharmony_ci .brightness_set = applesmc_brightness_set, 10738c2ecf20Sopenharmony_ci}; 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_cistatic struct applesmc_node_group info_group[] = { 10768c2ecf20Sopenharmony_ci { "name", applesmc_name_show }, 10778c2ecf20Sopenharmony_ci { "key_count", applesmc_key_count_show }, 10788c2ecf20Sopenharmony_ci { "key_at_index", applesmc_key_at_index_show, applesmc_key_at_index_store }, 10798c2ecf20Sopenharmony_ci { "key_at_index_name", applesmc_key_at_index_name_show }, 10808c2ecf20Sopenharmony_ci { "key_at_index_type", applesmc_key_at_index_type_show }, 10818c2ecf20Sopenharmony_ci { "key_at_index_data_length", applesmc_key_at_index_data_length_show }, 10828c2ecf20Sopenharmony_ci { "key_at_index_data", applesmc_key_at_index_read_show }, 10838c2ecf20Sopenharmony_ci { } 10848c2ecf20Sopenharmony_ci}; 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_cistatic struct applesmc_node_group accelerometer_group[] = { 10878c2ecf20Sopenharmony_ci { "position", applesmc_position_show }, 10888c2ecf20Sopenharmony_ci { "calibrate", applesmc_calibrate_show, applesmc_calibrate_store }, 10898c2ecf20Sopenharmony_ci { } 10908c2ecf20Sopenharmony_ci}; 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_cistatic struct applesmc_node_group light_sensor_group[] = { 10938c2ecf20Sopenharmony_ci { "light", applesmc_light_show }, 10948c2ecf20Sopenharmony_ci { } 10958c2ecf20Sopenharmony_ci}; 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_cistatic struct applesmc_node_group fan_group[] = { 10988c2ecf20Sopenharmony_ci { "fan%d_label", applesmc_show_fan_position }, 10998c2ecf20Sopenharmony_ci { "fan%d_input", applesmc_show_fan_speed, NULL, 0 }, 11008c2ecf20Sopenharmony_ci { "fan%d_min", applesmc_show_fan_speed, applesmc_store_fan_speed, 1 }, 11018c2ecf20Sopenharmony_ci { "fan%d_max", applesmc_show_fan_speed, NULL, 2 }, 11028c2ecf20Sopenharmony_ci { "fan%d_safe", applesmc_show_fan_speed, NULL, 3 }, 11038c2ecf20Sopenharmony_ci { "fan%d_output", applesmc_show_fan_speed, applesmc_store_fan_speed, 4 }, 11048c2ecf20Sopenharmony_ci { "fan%d_manual", applesmc_show_fan_manual, applesmc_store_fan_manual }, 11058c2ecf20Sopenharmony_ci { } 11068c2ecf20Sopenharmony_ci}; 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_cistatic struct applesmc_node_group temp_group[] = { 11098c2ecf20Sopenharmony_ci { "temp%d_label", applesmc_show_sensor_label }, 11108c2ecf20Sopenharmony_ci { "temp%d_input", applesmc_show_temperature }, 11118c2ecf20Sopenharmony_ci { } 11128c2ecf20Sopenharmony_ci}; 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci/* Module stuff */ 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci/* 11178c2ecf20Sopenharmony_ci * applesmc_destroy_nodes - remove files and free associated memory 11188c2ecf20Sopenharmony_ci */ 11198c2ecf20Sopenharmony_cistatic void applesmc_destroy_nodes(struct applesmc_node_group *groups) 11208c2ecf20Sopenharmony_ci{ 11218c2ecf20Sopenharmony_ci struct applesmc_node_group *grp; 11228c2ecf20Sopenharmony_ci struct applesmc_dev_attr *node; 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci for (grp = groups; grp->nodes; grp++) { 11258c2ecf20Sopenharmony_ci for (node = grp->nodes; node->sda.dev_attr.attr.name; node++) 11268c2ecf20Sopenharmony_ci sysfs_remove_file(&pdev->dev.kobj, 11278c2ecf20Sopenharmony_ci &node->sda.dev_attr.attr); 11288c2ecf20Sopenharmony_ci kfree(grp->nodes); 11298c2ecf20Sopenharmony_ci grp->nodes = NULL; 11308c2ecf20Sopenharmony_ci } 11318c2ecf20Sopenharmony_ci} 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci/* 11348c2ecf20Sopenharmony_ci * applesmc_create_nodes - create a two-dimensional group of sysfs files 11358c2ecf20Sopenharmony_ci */ 11368c2ecf20Sopenharmony_cistatic int applesmc_create_nodes(struct applesmc_node_group *groups, int num) 11378c2ecf20Sopenharmony_ci{ 11388c2ecf20Sopenharmony_ci struct applesmc_node_group *grp; 11398c2ecf20Sopenharmony_ci struct applesmc_dev_attr *node; 11408c2ecf20Sopenharmony_ci struct attribute *attr; 11418c2ecf20Sopenharmony_ci int ret, i; 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci for (grp = groups; grp->format; grp++) { 11448c2ecf20Sopenharmony_ci grp->nodes = kcalloc(num + 1, sizeof(*node), GFP_KERNEL); 11458c2ecf20Sopenharmony_ci if (!grp->nodes) { 11468c2ecf20Sopenharmony_ci ret = -ENOMEM; 11478c2ecf20Sopenharmony_ci goto out; 11488c2ecf20Sopenharmony_ci } 11498c2ecf20Sopenharmony_ci for (i = 0; i < num; i++) { 11508c2ecf20Sopenharmony_ci node = &grp->nodes[i]; 11518c2ecf20Sopenharmony_ci scnprintf(node->name, sizeof(node->name), grp->format, 11528c2ecf20Sopenharmony_ci i + 1); 11538c2ecf20Sopenharmony_ci node->sda.index = (grp->option << 16) | (i & 0xffff); 11548c2ecf20Sopenharmony_ci node->sda.dev_attr.show = grp->show; 11558c2ecf20Sopenharmony_ci node->sda.dev_attr.store = grp->store; 11568c2ecf20Sopenharmony_ci attr = &node->sda.dev_attr.attr; 11578c2ecf20Sopenharmony_ci sysfs_attr_init(attr); 11588c2ecf20Sopenharmony_ci attr->name = node->name; 11598c2ecf20Sopenharmony_ci attr->mode = 0444 | (grp->store ? 0200 : 0); 11608c2ecf20Sopenharmony_ci ret = sysfs_create_file(&pdev->dev.kobj, attr); 11618c2ecf20Sopenharmony_ci if (ret) { 11628c2ecf20Sopenharmony_ci attr->name = NULL; 11638c2ecf20Sopenharmony_ci goto out; 11648c2ecf20Sopenharmony_ci } 11658c2ecf20Sopenharmony_ci } 11668c2ecf20Sopenharmony_ci } 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci return 0; 11698c2ecf20Sopenharmony_ciout: 11708c2ecf20Sopenharmony_ci applesmc_destroy_nodes(groups); 11718c2ecf20Sopenharmony_ci return ret; 11728c2ecf20Sopenharmony_ci} 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci/* Create accelerometer resources */ 11758c2ecf20Sopenharmony_cistatic int applesmc_create_accelerometer(void) 11768c2ecf20Sopenharmony_ci{ 11778c2ecf20Sopenharmony_ci int ret; 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ci if (!smcreg.has_accelerometer) 11808c2ecf20Sopenharmony_ci return 0; 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci ret = applesmc_create_nodes(accelerometer_group, 1); 11838c2ecf20Sopenharmony_ci if (ret) 11848c2ecf20Sopenharmony_ci goto out; 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci applesmc_idev = input_allocate_device(); 11878c2ecf20Sopenharmony_ci if (!applesmc_idev) { 11888c2ecf20Sopenharmony_ci ret = -ENOMEM; 11898c2ecf20Sopenharmony_ci goto out_sysfs; 11908c2ecf20Sopenharmony_ci } 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci /* initial calibrate for the input device */ 11938c2ecf20Sopenharmony_ci applesmc_calibrate(); 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci /* initialize the input device */ 11968c2ecf20Sopenharmony_ci applesmc_idev->name = "applesmc"; 11978c2ecf20Sopenharmony_ci applesmc_idev->id.bustype = BUS_HOST; 11988c2ecf20Sopenharmony_ci applesmc_idev->dev.parent = &pdev->dev; 11998c2ecf20Sopenharmony_ci input_set_abs_params(applesmc_idev, ABS_X, 12008c2ecf20Sopenharmony_ci -256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT); 12018c2ecf20Sopenharmony_ci input_set_abs_params(applesmc_idev, ABS_Y, 12028c2ecf20Sopenharmony_ci -256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT); 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci ret = input_setup_polling(applesmc_idev, applesmc_idev_poll); 12058c2ecf20Sopenharmony_ci if (ret) 12068c2ecf20Sopenharmony_ci goto out_idev; 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci input_set_poll_interval(applesmc_idev, APPLESMC_POLL_INTERVAL); 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci ret = input_register_device(applesmc_idev); 12118c2ecf20Sopenharmony_ci if (ret) 12128c2ecf20Sopenharmony_ci goto out_idev; 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci return 0; 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ciout_idev: 12178c2ecf20Sopenharmony_ci input_free_device(applesmc_idev); 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ciout_sysfs: 12208c2ecf20Sopenharmony_ci applesmc_destroy_nodes(accelerometer_group); 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ciout: 12238c2ecf20Sopenharmony_ci pr_warn("driver init failed (ret=%d)!\n", ret); 12248c2ecf20Sopenharmony_ci return ret; 12258c2ecf20Sopenharmony_ci} 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci/* Release all resources used by the accelerometer */ 12288c2ecf20Sopenharmony_cistatic void applesmc_release_accelerometer(void) 12298c2ecf20Sopenharmony_ci{ 12308c2ecf20Sopenharmony_ci if (!smcreg.has_accelerometer) 12318c2ecf20Sopenharmony_ci return; 12328c2ecf20Sopenharmony_ci input_unregister_device(applesmc_idev); 12338c2ecf20Sopenharmony_ci applesmc_destroy_nodes(accelerometer_group); 12348c2ecf20Sopenharmony_ci} 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_cistatic int applesmc_create_light_sensor(void) 12378c2ecf20Sopenharmony_ci{ 12388c2ecf20Sopenharmony_ci if (!smcreg.num_light_sensors) 12398c2ecf20Sopenharmony_ci return 0; 12408c2ecf20Sopenharmony_ci return applesmc_create_nodes(light_sensor_group, 1); 12418c2ecf20Sopenharmony_ci} 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_cistatic void applesmc_release_light_sensor(void) 12448c2ecf20Sopenharmony_ci{ 12458c2ecf20Sopenharmony_ci if (!smcreg.num_light_sensors) 12468c2ecf20Sopenharmony_ci return; 12478c2ecf20Sopenharmony_ci applesmc_destroy_nodes(light_sensor_group); 12488c2ecf20Sopenharmony_ci} 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_cistatic int applesmc_create_key_backlight(void) 12518c2ecf20Sopenharmony_ci{ 12528c2ecf20Sopenharmony_ci if (!smcreg.has_key_backlight) 12538c2ecf20Sopenharmony_ci return 0; 12548c2ecf20Sopenharmony_ci applesmc_led_wq = create_singlethread_workqueue("applesmc-led"); 12558c2ecf20Sopenharmony_ci if (!applesmc_led_wq) 12568c2ecf20Sopenharmony_ci return -ENOMEM; 12578c2ecf20Sopenharmony_ci return led_classdev_register(&pdev->dev, &applesmc_backlight); 12588c2ecf20Sopenharmony_ci} 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_cistatic void applesmc_release_key_backlight(void) 12618c2ecf20Sopenharmony_ci{ 12628c2ecf20Sopenharmony_ci if (!smcreg.has_key_backlight) 12638c2ecf20Sopenharmony_ci return; 12648c2ecf20Sopenharmony_ci led_classdev_unregister(&applesmc_backlight); 12658c2ecf20Sopenharmony_ci destroy_workqueue(applesmc_led_wq); 12668c2ecf20Sopenharmony_ci} 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_cistatic int applesmc_dmi_match(const struct dmi_system_id *id) 12698c2ecf20Sopenharmony_ci{ 12708c2ecf20Sopenharmony_ci return 1; 12718c2ecf20Sopenharmony_ci} 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci/* 12748c2ecf20Sopenharmony_ci * Note that DMI_MATCH(...,"MacBook") will match "MacBookPro1,1". 12758c2ecf20Sopenharmony_ci * So we need to put "Apple MacBook Pro" before "Apple MacBook". 12768c2ecf20Sopenharmony_ci */ 12778c2ecf20Sopenharmony_cistatic const struct dmi_system_id applesmc_whitelist[] __initconst = { 12788c2ecf20Sopenharmony_ci { applesmc_dmi_match, "Apple MacBook Air", { 12798c2ecf20Sopenharmony_ci DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), 12808c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir") }, 12818c2ecf20Sopenharmony_ci }, 12828c2ecf20Sopenharmony_ci { applesmc_dmi_match, "Apple MacBook Pro", { 12838c2ecf20Sopenharmony_ci DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), 12848c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro") }, 12858c2ecf20Sopenharmony_ci }, 12868c2ecf20Sopenharmony_ci { applesmc_dmi_match, "Apple MacBook", { 12878c2ecf20Sopenharmony_ci DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), 12888c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "MacBook") }, 12898c2ecf20Sopenharmony_ci }, 12908c2ecf20Sopenharmony_ci { applesmc_dmi_match, "Apple Macmini", { 12918c2ecf20Sopenharmony_ci DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), 12928c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "Macmini") }, 12938c2ecf20Sopenharmony_ci }, 12948c2ecf20Sopenharmony_ci { applesmc_dmi_match, "Apple MacPro", { 12958c2ecf20Sopenharmony_ci DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), 12968c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "MacPro") }, 12978c2ecf20Sopenharmony_ci }, 12988c2ecf20Sopenharmony_ci { applesmc_dmi_match, "Apple iMac", { 12998c2ecf20Sopenharmony_ci DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), 13008c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "iMac") }, 13018c2ecf20Sopenharmony_ci }, 13028c2ecf20Sopenharmony_ci { .ident = NULL } 13038c2ecf20Sopenharmony_ci}; 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_cistatic int __init applesmc_init(void) 13068c2ecf20Sopenharmony_ci{ 13078c2ecf20Sopenharmony_ci int ret; 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci if (!dmi_check_system(applesmc_whitelist)) { 13108c2ecf20Sopenharmony_ci pr_warn("supported laptop not found!\n"); 13118c2ecf20Sopenharmony_ci ret = -ENODEV; 13128c2ecf20Sopenharmony_ci goto out; 13138c2ecf20Sopenharmony_ci } 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci if (!request_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS, 13168c2ecf20Sopenharmony_ci "applesmc")) { 13178c2ecf20Sopenharmony_ci ret = -ENXIO; 13188c2ecf20Sopenharmony_ci goto out; 13198c2ecf20Sopenharmony_ci } 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_ci ret = platform_driver_register(&applesmc_driver); 13228c2ecf20Sopenharmony_ci if (ret) 13238c2ecf20Sopenharmony_ci goto out_region; 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci pdev = platform_device_register_simple("applesmc", APPLESMC_DATA_PORT, 13268c2ecf20Sopenharmony_ci NULL, 0); 13278c2ecf20Sopenharmony_ci if (IS_ERR(pdev)) { 13288c2ecf20Sopenharmony_ci ret = PTR_ERR(pdev); 13298c2ecf20Sopenharmony_ci goto out_driver; 13308c2ecf20Sopenharmony_ci } 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci /* create register cache */ 13338c2ecf20Sopenharmony_ci ret = applesmc_init_smcreg(); 13348c2ecf20Sopenharmony_ci if (ret) 13358c2ecf20Sopenharmony_ci goto out_device; 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_ci ret = applesmc_create_nodes(info_group, 1); 13388c2ecf20Sopenharmony_ci if (ret) 13398c2ecf20Sopenharmony_ci goto out_smcreg; 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci ret = applesmc_create_nodes(fan_group, smcreg.fan_count); 13428c2ecf20Sopenharmony_ci if (ret) 13438c2ecf20Sopenharmony_ci goto out_info; 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci ret = applesmc_create_nodes(temp_group, smcreg.index_count); 13468c2ecf20Sopenharmony_ci if (ret) 13478c2ecf20Sopenharmony_ci goto out_fans; 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_ci ret = applesmc_create_accelerometer(); 13508c2ecf20Sopenharmony_ci if (ret) 13518c2ecf20Sopenharmony_ci goto out_temperature; 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci ret = applesmc_create_light_sensor(); 13548c2ecf20Sopenharmony_ci if (ret) 13558c2ecf20Sopenharmony_ci goto out_accelerometer; 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci ret = applesmc_create_key_backlight(); 13588c2ecf20Sopenharmony_ci if (ret) 13598c2ecf20Sopenharmony_ci goto out_light_sysfs; 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci hwmon_dev = hwmon_device_register(&pdev->dev); 13628c2ecf20Sopenharmony_ci if (IS_ERR(hwmon_dev)) { 13638c2ecf20Sopenharmony_ci ret = PTR_ERR(hwmon_dev); 13648c2ecf20Sopenharmony_ci goto out_light_ledclass; 13658c2ecf20Sopenharmony_ci } 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci return 0; 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ciout_light_ledclass: 13708c2ecf20Sopenharmony_ci applesmc_release_key_backlight(); 13718c2ecf20Sopenharmony_ciout_light_sysfs: 13728c2ecf20Sopenharmony_ci applesmc_release_light_sensor(); 13738c2ecf20Sopenharmony_ciout_accelerometer: 13748c2ecf20Sopenharmony_ci applesmc_release_accelerometer(); 13758c2ecf20Sopenharmony_ciout_temperature: 13768c2ecf20Sopenharmony_ci applesmc_destroy_nodes(temp_group); 13778c2ecf20Sopenharmony_ciout_fans: 13788c2ecf20Sopenharmony_ci applesmc_destroy_nodes(fan_group); 13798c2ecf20Sopenharmony_ciout_info: 13808c2ecf20Sopenharmony_ci applesmc_destroy_nodes(info_group); 13818c2ecf20Sopenharmony_ciout_smcreg: 13828c2ecf20Sopenharmony_ci applesmc_destroy_smcreg(); 13838c2ecf20Sopenharmony_ciout_device: 13848c2ecf20Sopenharmony_ci platform_device_unregister(pdev); 13858c2ecf20Sopenharmony_ciout_driver: 13868c2ecf20Sopenharmony_ci platform_driver_unregister(&applesmc_driver); 13878c2ecf20Sopenharmony_ciout_region: 13888c2ecf20Sopenharmony_ci release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS); 13898c2ecf20Sopenharmony_ciout: 13908c2ecf20Sopenharmony_ci pr_warn("driver init failed (ret=%d)!\n", ret); 13918c2ecf20Sopenharmony_ci return ret; 13928c2ecf20Sopenharmony_ci} 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_cistatic void __exit applesmc_exit(void) 13958c2ecf20Sopenharmony_ci{ 13968c2ecf20Sopenharmony_ci hwmon_device_unregister(hwmon_dev); 13978c2ecf20Sopenharmony_ci applesmc_release_key_backlight(); 13988c2ecf20Sopenharmony_ci applesmc_release_light_sensor(); 13998c2ecf20Sopenharmony_ci applesmc_release_accelerometer(); 14008c2ecf20Sopenharmony_ci applesmc_destroy_nodes(temp_group); 14018c2ecf20Sopenharmony_ci applesmc_destroy_nodes(fan_group); 14028c2ecf20Sopenharmony_ci applesmc_destroy_nodes(info_group); 14038c2ecf20Sopenharmony_ci applesmc_destroy_smcreg(); 14048c2ecf20Sopenharmony_ci platform_device_unregister(pdev); 14058c2ecf20Sopenharmony_ci platform_driver_unregister(&applesmc_driver); 14068c2ecf20Sopenharmony_ci release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS); 14078c2ecf20Sopenharmony_ci} 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_cimodule_init(applesmc_init); 14108c2ecf20Sopenharmony_cimodule_exit(applesmc_exit); 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_ciMODULE_AUTHOR("Nicolas Boichat"); 14138c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Apple SMC"); 14148c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 14158c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(dmi, applesmc_whitelist); 1416