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