diff --git a/MAINTAINERS b/MAINTAINERS index 7b2018963ae3..2c32b4232773 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17570,6 +17570,12 @@ F: drivers/usb/phytium/phytium_usb_v2* F: drivers/usb/typec/role-switch-phytium.c F: arch/arm64/boot/dts/phytium/* F: drivers/gpio/gpio-phytium* +F: drivers/platform/arm64/ +F: drivers/platform/arm64/phytium/* +F: include/linux/classtypes.h +F: include/linux/cputypes.h +F: lib/classtypes.c +F: lib/cputypes.c QAT DRIVER M: Giovanni Cabiddu diff --git a/arch/arm64/configs/deepin_arm64_desktop_defconfig b/arch/arm64/configs/deepin_arm64_desktop_defconfig index c599bd320849..f249c15a5c53 100644 --- a/arch/arm64/configs/deepin_arm64_desktop_defconfig +++ b/arch/arm64/configs/deepin_arm64_desktop_defconfig @@ -4022,6 +4022,9 @@ CONFIG_QLGE=m CONFIG_PHYTIUM_NPU=m CONFIG_NPU_PLATFORM=m CONFIG_PHYTIUM_NPU_PCI=m +CONFIG_ARM64_PLATFORM_DEVICES=y +CONFIG_PHYTIUM_EC=y +CONFIG_PHYTIUM_LAPTOP=y CONFIG_GOLDFISH=y CONFIG_CHROME_PLATFORMS=y CONFIG_MELLANOX_PLATFORM=y diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index f94cddbbb55a..10a30e9e7c1f 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -1653,7 +1653,12 @@ static int acpi_ec_setup(struct acpi_ec *ec, struct acpi_device *device, bool ca ret = ec_install_handlers(ec, device, call_reg); if (ret) { +#ifdef CONFIG_ARCH_PHYTIUM + if (read_cpuid_implementor() != ARM_CPU_IMP_PHYTIUM) + ec_remove_handlers(ec); +#else ec_remove_handlers(ec); +#endif if (ec == first_ec) first_ec = NULL; diff --git a/drivers/platform/Kconfig b/drivers/platform/Kconfig index 868b20361769..db244cbd362f 100644 --- a/drivers/platform/Kconfig +++ b/drivers/platform/Kconfig @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only +source "drivers/platform/arm64/Kconfig" + source "drivers/platform/mips/Kconfig" source "drivers/platform/loongarch/Kconfig" diff --git a/drivers/platform/Makefile b/drivers/platform/Makefile index 8296d4c41eb7..4c2bf61715f4 100644 --- a/drivers/platform/Makefile +++ b/drivers/platform/Makefile @@ -3,6 +3,8 @@ # Makefile for linux/drivers/platform # +obj-$(CONFIG_ARM64) += arm64/ + obj-$(CONFIG_X86) += x86/ obj-$(CONFIG_LOONGARCH) += loongarch/ obj-$(CONFIG_SW64) += sw64/ diff --git a/drivers/platform/arm64/Kconfig b/drivers/platform/arm64/Kconfig new file mode 100644 index 000000000000..b94fdbefaf24 --- /dev/null +++ b/drivers/platform/arm64/Kconfig @@ -0,0 +1,36 @@ +# SPDX-License-Identifier: GPL-2.0-only + +menuconfig ARM64_PLATFORM_DEVICES + bool "ARM64 platform devices" + depends on ARM64 + help + Say Y here to enable platform-specific drivers for ARM64 systems. + These drivers provide support for vendor-specific hardware that is + not covered by standard subsystem drivers, such as embedded + controllers, laptop hotkeys, backlight and power supply controls. + +if ARM64_PLATFORM_DEVICES + +config PHYTIUM_EC + bool "Phytium IT8528 Embedded Controller support" + depends on PHYTIUM_PIO + help + Enable support for the IT8528 Embedded Controller found on some + Phytium laptop platforms. This driver communicates with the EC + over the Phytium PIO (Programmed I/O) path and is required by the + Phytium laptop driver for battery, lid and hotkey support. + +config PHYTIUM_LAPTOP + bool "Phytium laptop platform support" + depends on PHYTIUM_EC + select POWER_SUPPLY + select BACKLIGHT_CLASS_DEVICE + select INPUT_SPARSEKMAP + select HWMON + help + Enable laptop-specific support for Phytium platforms. This + provides power-supply reporting (AC and battery), backlight + control, lid status and Fn hotkey handling through the IT8528 + embedded controller. + +endif diff --git a/drivers/platform/arm64/Makefile b/drivers/platform/arm64/Makefile new file mode 100644 index 000000000000..6f23139ddf7b --- /dev/null +++ b/drivers/platform/arm64/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_ARM64_PLATFORM_DEVICES) += phytium/ diff --git a/drivers/platform/arm64/phytium/Makefile b/drivers/platform/arm64/phytium/Makefile new file mode 100644 index 000000000000..75a5271c8a7b --- /dev/null +++ b/drivers/platform/arm64/phytium/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for Phytium platform drivers. +# + +obj-$(CONFIG_PHYTIUM_EC) += ec_it8528.o +obj-$(CONFIG_PHYTIUM_LAPTOP) += phyt_laptop.o ec_sensor.o diff --git a/drivers/platform/arm64/phytium/ec_it8528.c b/drivers/platform/arm64/phytium/ec_it8528.c new file mode 100644 index 000000000000..57bebd53f7b8 --- /dev/null +++ b/drivers/platform/arm64/phytium/ec_it8528.c @@ -0,0 +1,280 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * EC (Embedded Controller) for phytium platform + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include "ec_it8528.h" + +/* This spinlock is dedicated for 62&66 ports and super io port access. */ +DEFINE_SPINLOCK(index_access_lock); + +static int ec_wait_ibf_clear(void) +{ + unsigned int timeout = EC_SEND_TIMEOUT; + + while ((inb(EC_STS_PORT) & EC_IBF) && --timeout) + udelay(1); + if (!timeout) { + pr_err("Timeout waiting for EC IBF clear (status 0x%x)\n", + inb(EC_STS_PORT)); + return -ETIMEDOUT; + } + return 0; +} + +static int ec_wait_obf_set(u8 *data) +{ + unsigned int timeout = EC_RECV_TIMEOUT; + + while (!(inb(EC_STS_PORT) & EC_OBF) && --timeout) + udelay(1); + if (!timeout) { + pr_err("Timeout waiting for EC OBF set (status 0x%x)\n", + inb(EC_STS_PORT)); + return -ETIMEDOUT; + } + *data = inb(EC_DAT_PORT); + return 0; +} + +static int ec_send_byte(u8 byte, u16 port) +{ + int ret = ec_wait_ibf_clear(); + + if (ret) + return ret; + outb(byte, port); + return 0; +} + +/* + * One EC transaction: command + optional write data + optional read data. + * The whole sequence is protected by index_access_lock. + */ +static int it8528_transaction(u8 command, const u8 *wdata, int wlen, + u8 *rdata, int rlen) +{ + unsigned long flags; + int i, ret; + u8 val; + + spin_lock_irqsave(&index_access_lock, flags); + + ret = ec_send_byte(command, EC_CMD_PORT); + if (ret) + goto out; + + for (i = 0; i < wlen; i++) { + ret = ec_send_byte(wdata[i], EC_DAT_PORT); + if (ret) + goto out; + } + + for (i = 0; i < rlen; i++) { + ret = ec_wait_obf_set(&val); + if (ret) + goto out; + rdata[i] = val; + } + +out: + spin_unlock_irqrestore(&index_access_lock, flags); + return ret; +} + +unsigned char it8528_read(unsigned char index) +{ + u8 value = 0; + + it8528_transaction(CMD_READ_EC, &index, 1, &value, 1); + return value; +} +EXPORT_SYMBOL(it8528_read); + +unsigned char it8528_read_all(unsigned char command, unsigned char index) +{ + u8 value = 0; + + it8528_transaction(command, &index, 1, &value, 1); + return value; +} +EXPORT_SYMBOL(it8528_read_all); + +unsigned char it8528_read_noindex(unsigned char command) +{ + u8 value = 0; + + it8528_transaction(command, NULL, 0, &value, 1); + return value; +} +EXPORT_SYMBOL(it8528_read_noindex); + +int it8528_write(unsigned char index, unsigned char data) +{ + u8 wdata[2] = { index, data }; + + return it8528_transaction(CMD_WRITE_EC, wdata, 2, NULL, 0); +} +EXPORT_SYMBOL(it8528_write); + +int it8528_write_all(unsigned char command, unsigned char index, + unsigned char data) +{ + u8 wdata[2] = { index, data }; + + return it8528_transaction(command, wdata, 2, NULL, 0); +} +EXPORT_SYMBOL(it8528_write_all); + +int it8528_write_noindex(unsigned char command, unsigned char data) +{ + return it8528_transaction(command, &data, 1, NULL, 0); +} +EXPORT_SYMBOL(it8528_write_noindex); + +bool it8528_get_ec_ibf_flags(void) +{ + return !!(inb(EC_STS_PORT) & EC_IBF); +} +EXPORT_SYMBOL(it8528_get_ec_ibf_flags); + +bool it8528_get_ec_obf_flags(void) +{ + return !!(inb(EC_STS_PORT) & EC_OBF); +} +EXPORT_SYMBOL(it8528_get_ec_obf_flags); + +bool it8528_get_ec_evt_flags(void) +{ + return !!(inb(EC_STS_PORT) & EC_SCI_EVT); +} +EXPORT_SYMBOL(it8528_get_ec_evt_flags); + +int it8528_query_get_event_num(void) +{ + unsigned long flags; + unsigned int timeout; + u8 value = 0; + int ret; + + spin_lock_irqsave(&index_access_lock, flags); + + ret = ec_send_byte(CMD_GET_EVENT_NUM, EC_CMD_PORT); + if (ret) + goto out; + + /* Give the EC a moment to consume the command. */ + timeout = EC_CMD_TIMEOUT; + while ((inb(EC_STS_PORT) & EC_IBF) && timeout--) + udelay(1); + if (!timeout) { + pr_err("EC QUERY SEQ: timeout\n"); + ret = -ETIMEDOUT; + goto out; + } + + ret = ec_wait_obf_set(&value); +out: + spin_unlock_irqrestore(&index_access_lock, flags); + return ret ? ret : value; +} +EXPORT_SYMBOL(it8528_query_get_event_num); + +int it8528_query_clean_event(void) +{ + int ret; + + while (inb(EC_STS_PORT) & EC_SCI_EVT) { + ret = it8528_query_get_event_num(); + if (ret <= 0) { + pr_info("Clean sci event done!\n"); + return ret; + } + } + + return 0; +} +EXPORT_SYMBOL(it8528_query_clean_event); + +void it8528_ec_event_int_enable(void) +{ + unsigned long flags; + unsigned int timeout; + int ret; + + spin_lock_irqsave(&index_access_lock, flags); + + ret = ec_send_byte(CMD_ENABLE_EVENT_EC, EC_CMD_PORT); + if (ret) + goto out; + + timeout = EC_CMD_TIMEOUT; + while ((inb(EC_STS_PORT) & EC_IBF) && timeout--) + udelay(1); + if (!timeout) + pr_err("EC ENABLE EVENT INTERRUPT: timeout\n"); + +out: + spin_unlock_irqrestore(&index_access_lock, flags); +} +EXPORT_SYMBOL(it8528_ec_event_int_enable); + +void it8528_ec_event_int_disable(void) +{ + unsigned long flags; + unsigned int timeout; + int ret; + + spin_lock_irqsave(&index_access_lock, flags); + + ret = ec_send_byte(CMD_DISABLE_EVENT_EC, EC_CMD_PORT); + if (ret) + goto out; + + timeout = EC_CMD_TIMEOUT; + while ((inb(EC_STS_PORT) & EC_IBF) && timeout--) + udelay(1); + if (!timeout) + pr_err("EC DISABLE EVENT INTERRUPT: timeout\n"); + +out: + spin_unlock_irqrestore(&index_access_lock, flags); +} +EXPORT_SYMBOL(it8528_ec_event_int_disable); + +int it8528_init(void) +{ + if (unlikely(!check_cpu_type())) { + pr_err("Stop init on non-Phytium CPU!\n"); + return -ENODEV; + } + return 0; +} +EXPORT_SYMBOL(it8528_init); + +int lpc_ec_interrupt_occurs(void) +{ + int data; + + data = phytium_pio_get_int_status(); + + if (data & EC_EVENT_BIT) + return 1; + return 0; +} +EXPORT_SYMBOL(lpc_ec_interrupt_occurs); + +void lpc_interrupt_clear_all(void) +{ + phytium_pio_clear_interrupt(0); +} +EXPORT_SYMBOL(lpc_interrupt_clear_all); diff --git a/drivers/platform/arm64/phytium/ec_it8528.h b/drivers/platform/arm64/phytium/ec_it8528.h new file mode 100644 index 000000000000..bb2dd53a7d9e --- /dev/null +++ b/drivers/platform/arm64/phytium/ec_it8528.h @@ -0,0 +1,367 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * EC (Embedded Controller) IT8528 device driver header for phytium czc B20 + * + * EC relative header file. All the EC registers should be defined here. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at you option) and later version. + */ + +#ifndef __EC_IT8528_H__ +#define __EC_IT8528_H__ +#include +#include "../../../bus/phytium_pio.h" + +#define EC_VERSION "1.0" + + +#define EC_EVENT_BIT (1 << 11) +#define I8042_KEY_BIT (1 << 1) +#define I8042_TOUCH_BIT (1 << 12) + +extern u32 phytium_pio_get_int_status(void); + +/* This spinlock is dedicated for 62&66 ports and super io port access. */ +extern spinlock_t i8042_lock; +extern spinlock_t index_access_lock; + +/* + * The following registers are determined by the EC index configureation. + * 1. fill the PORT_INDEX as EC register. + * 2. fill the PORT_DATA as EC register write data or get the data from it. + */ +/* base address for io access for Notebook platform */ +#define SIO_INDEX_PORT 0x4E +#define SIO_DATA_PORT 0x4F + +/* + * EC delay time 500us for register and status access + * Unit : us + */ +#define EC_REG_DELAY 30000 +#define EC_CMD_TIMEOUT 0x1000 +#define EC_SEND_TIMEOUT 0xffff +#define EC_RECV_TIMEOUT 0xffff + +/* + * EC access port for with Host communication. + */ +#define EC_CMD_PORT 0x66 +#define EC_STS_PORT 0x66 +#define EC_DAT_PORT 0x62 + +/* + * ACPI legacy commands. + */ +#define CMD_READ_EC 0x80 /* Read EC command. */ +#define CMD_WRITE_EC 0x81 /* Write EC command. */ +#define CMD_GET_EVENT_NUM 0x84 /* Query EC command, for get ec event number. */ +#define CMD_ENABLE_EVENT_EC 0x86 /* Enable EC event interrupt. */ +#define CMD_DISABLE_EVENT_EC 0x87 /* Disable EC event interrupt. */ + +/* + * ACPI OEM commands. + */ + /* Reset and poweroff the machine auto-clear: rd/wr */ +#define CMD_RESET 0x4E +enum { + RESET_OFF = 0, + RESET_ON, + PWROFF_ON, + STR_ON, + STANDBY_ON +}; + +#define CMD_EC_VERSION 0x4F /* EC Version OEM command: 36 Bytes */ + +/* + * Used ACPI legacy command 80h to do active. + */ +/* >>> Read/Write temperature & fan index for ACPI 80h/81h command. */ + /* Current CPU temperature value, Read and Write(81h command). */ +#define INDEX_CPU_TEMP_VALUE 0x98 + /* Current GPU temperature value, Read and Write(81h command). */ +#define INDEX_GPU_TEMP_VALUE 0x9A + + /* Fan speed maxinum levels supported. Defaut is 6. */ +#define INDEX_FAN_MAXSPEED_LEVEL 0x5B +#define INDEX_FAN_SPEED_LEVEL 0x5C /* FAn speed level. [0,5] or [0x06, 0x38]*/ + /* Fan control mode, 0 = by EC, 1 = by Host.*/ +#define INDEX_FAN_CTRLMOD 0x5D +enum { + FAN_CTRL_BYEC = 0, + FAN_CTRL_BYHOST +}; +#define INDEX_FAN_STSCTRL 0x5E /* Fan status/control, 0 = stop, 1 = run. */ +enum { + FAN_STSCTRL_OFF = 0, + FAN_STSCTRL_ON +}; + /* Fan error status, 0 = no error, 1 = has error. */ +#define INDEX_FAN_ERRSTS 0x5F +enum { + FAN_ERRSTS_NO = 0, + FAN_ERRSTS_HAS +}; + +#define INDEX_CPU_FAN_SPEED_LOW 0xF2 /* Fan speed low byte.*/ +#define INDEX_CPU_FAN_SPEED_HIGH 0xF3 /* Fan speed high byte. */ +#define INDEX_GPU_FAN_SPEED_LOW 0xF4 /* Fan speed low byte.*/ +#define INDEX_GPU_FAN_SPEED_HIGH 0xF5 /* Fan speed high byte. */ +/* <<< End Temp & Fan */ + +/* >>> Read/Write LCD backlight information/control index for ACPI 80h/81h command. */ + /* LCD backlight control mode: 0 = by EC, 1 = by HOST */ +#define INDEX_BACKLIGHT_CTRLMODE 0x57 +enum { + BACKLIGHT_CTRL_BYEC = 0, + BACKLIGHT_CTRL_BYHOST +}; + /* LCD backlight status or control: 0 = turn off, 1 = turn on */ +#define INDEX_BACKLIGHT_STSCTRL 0x58 +enum { + BACKLIGHT_OFF = 0, + BACKLIGHT_ON +}; +#define INDEX_DISPLAY_MAXBRIGHTNESS_LEVEL 0x59 /* LCD backlight brightness max level */ + /* 10 stages (0~9) LCD backlight brightness adjust */ +#define INDEX_DISPLAY_BRIGHTNESS_SET 0x0E + /* 10 stages (0~9) LCD backlight brightness adjust */ +#define INDEX_DISPLAY_BRIGHTNESS_GET 0x0F + +enum { + FLAG_DISPLAY_BRIGHTNESS_LEVEL_0 = 0, /* This level is backlight turn off. */ + FLAG_DISPLAY_BRIGHTNESS_LEVEL_1, + FLAG_DISPLAY_BRIGHTNESS_LEVEL_2, + FLAG_DISPLAY_BRIGHTNESS_LEVEL_3, + FLAG_DISPLAY_BRIGHTNESS_LEVEL_4, + FLAG_DISPLAY_BRIGHTNESS_LEVEL_5, + FLAG_DISPLAY_BRIGHTNESS_LEVEL_6, + FLAG_DISPLAY_BRIGHTNESS_LEVEL_7, + FLAG_DISPLAY_BRIGHTNESS_LEVEL_8, + FLAG_DISPLAY_BRIGHTNESS_LEVEL_9, + FLAG_DISPLAY_BRIGHTNESS_LEVEL_10 +}; +/* <<< End Backlight */ + +/* >>> Read battery index command */ +/* + * The reported battery die temperature. + * The temperature is expressed in units of 0.25 seconds and is updated every 2.56 seconds. + * The equation to calculate reported pack temperature is: + * Temperature = 0.1 * (256 * TEMPH + TEMPL) Kelvin + * Temperature -= 273 Degrees Celsius + * The host system has read-only access to this register pair. + */ +#define INDEX_BATTERY_VOL_LOW 0x2A /* Battery Voltage Low byte. */ +#define INDEX_BATTERY_VOL_HIGH 0x2B /* Battery Voltage High byte. */ +#define INDEX_BATTERY_CURRENT_LOW 0x2C /* Battery Current Low byte. */ +#define INDEX_BATTERY_CURRENT_HIGH 0x2D /* Battery Current High byte. */ +#define INDEX_BATTERY_AC_LOW 0x2E /* Battery AverageCurrent Low byte. */ +#define INDEX_BATTERY_AC_HIGH 0x2F /* Battery AverageCurrent High byte. */ +#define INDEX_BATTERY_CAPACITY 0x21 /* Battery RemainingCapacity percent. */ +#define INDEX_BATTERY_STATUS_LOW 0x3C /* Battery Status low byte. */ + +enum { + BIT_BATTERY_STATUS_FD = 4, /* Battery Fully Discharged Notify. 1 = Fully Discharged */ + BIT_BATTERY_STATUS_FC, /* Battery Fully Charged Notify. 1 = Fully Charged. */ + /* + * Battery Discharging mode. 0 = in charging mode, 1 = in discharging mode, + * relaxation mode, or valid charge termination has occurred. + */ + BIT_BATTERY_STATUS_DSG, + BIT_BATTERY_STATUS_INIT /* Battery Initialization. 1 = Initialization */ +}; +#define INDEX_BATTERY_STATUS 0x3C /* Battery Status byte. */ +enum { + BIT_POWER_BATEMPTY = 4, /* Battery in empty status. */ + BIT_POWER_BATFCHG = 5, /* Battery in fully charging status. */ + BIT_POWER_BATCHG = 6, /* Battery in charging status. */ +}; + +#define INDEX_BATTERY_CAP_LOW 0x24 /* Battery Capacity Low byte. */ +#define INDEX_BATTERY_CAP_HIGH 0x25 /* Battery Capacity High byte. */ +#define INDEX_BATTERY_RC_LOW 0x26 /* Battery RemainingCapacity Low byte. */ +#define INDEX_BATTERY_RC_HIGH 0x27 /* Battery RemainingCapacity High byte. */ +#define INDEX_BATTERY_ATTE_LOW 0x9C /* Battery AverageTimeToEmpty Low byte. */ +#define INDEX_BATTERY_ATTE_HIGH 0x9D /* Battery AverageTimeToEmpty High byte. */ +#define INDEX_BATTERY_ATTF_LOW 0xA4 /* Battery AverageTimeToFull Low byte. */ +#define INDEX_BATTERY_ATTF_HIGH 0xA5 /* Battery AverageTimeToFull High byte. */ +#define INDEX_BATTERY_FCC_LOW 0x24 /* Battery FullChargeCapacity Low byte. */ +#define INDEX_BATTERY_FCC_HIGH 0x25 /* Battery FullChargeCapacity High byte. */ +#define INDEX_BATTERY_CC_LOW 0xAC /* Battery ChargingCurrent Low byte. */ +#define INDEX_BATTERY_CC_HIGH 0xAD /* Battery ChargingCurrent High byte. */ +#define INDEX_BATTERY_CV_LOW 0xAA /* Battery ChargingVoltage Low byte. */ +#define INDEX_BATTERY_CV_HIGH 0xAB /* Battery ChargingVoltage High byte. */ +#define INDEX_BATTERY_TEMP_LOW 0x28 /* Battery temperature low byte. */ +#define INDEX_BATTERY_TEMP_HIGH 0x29 /* Battery temperature high byte. */ +#define INDEX_BATTERY_CYCLECNT_LOW 0xA8 /* Battery CycleCount Low byte. */ +#define INDEX_BATTERY_CYCLECNT_HIGH 0xA9 /* Battery CycleCount High byte. */ + +/* Battery static information. */ +#define INDEX_BATTERY_DC_LOW 0x38 /* Battery DesignCapacity Low byte. */ +#define INDEX_BATTERY_DC_HIGH 0x39 /* Battery DesignCapacity High byte. */ +#define INDEX_BATTERY_DV_LOW 0x3A /* Battery DesignVoltage Low byte. */ +#define INDEX_BATTERY_DV_HIGH 0x3B /* Battery DesignVoltage High byte. */ +#define INDEX_BATTERY_SN_LOW 0x3E /* Battery SerialNumber Low byte. */ +#define INDEX_BATTERY_SN_HIGH 0x3F /* Battery SerialNumber High byte. */ +/* <<< End Battery */ + +/* Phytium S3 timeout information */ +#define INDEX_POWERON_TYPE 0x0B /* Phytium EC Poweron type. */ +#define INDEX_S3_TIMEOUT_LO 0x0C /* Phytium S3 timeout Low byte. */ +#define INDEX_S3_TIMEOUT_HI 0x0D /* Phytium S3 timeout High byte. */ + +#define PHYTIUM_S3_TIMEOUT_DEFAULT 10 /* bxc timeout default 10 min */ +#define PHYTIUM_S3_TIMEOUT_FOREVER 0x0 + +#define PHYTIUM_S3_TIMEOUT_TYPE 0xAA +#define PHYTIUM_S3_NORMAL_WAKEUP_TYPE 0x55 +/* <<< End Phytium S3 */ + +#define MASK(x) (1 << x) + +#define INDEX_POWER_STATUS 0xB0 /* Read current power status. */ +enum { + BIT_POWER_ACPRES, /* AC present. */ + BIT_POWER_BATPRES, /* ZW000B Master Battery present. */ +}; + +#define INDEX_DEVICE_STATUS 0x81 /* Read Current Device Status */ +enum { + BIT_DEVICE_TP = 0, /* TouchPad status: 0 = close, 1 = open */ + BIT_DEVICE_WLAN, /* WLAN status: 0 = close, 1 = open */ + BIT_DEVICE_3G, /* 3G status: 0 = close, 1 = open */ + BIT_DEVICE_CAM, /* Camera status: 0 = close, 1 = open */ + BIT_DEVICE_MUTE, /* Mute status: 0 = close, 1 = open */ + BIT_DEVICE_LID, /* LID status: 0 = close, 1 = open */ + BIT_DEVICE_BKLIGHT, /* BackLight status: 0 = close, 1 = open */ + BIT_DEVICE_SIM /* SIM Card status: 0 = pull out, 1 = insert */ +}; + +#define INDEX_SHUTDOWN_ID 0x82 /* Read Shutdown ID */ +enum { + BIT_SHUTDNID_S45 = 0, /* in S4 or S5 */ + BIT_SHUTDNID_BATDEAD, /* Battery Dead */ + BIT_SHUTDNID_OVERHEAT, /* Over Heat */ + BIT_SHUTDNID_SYSCMD, /* System command */ + BIT_SHUTDNID_LPRESSPWN, /* Long press power button */ + BIT_SHUTDNID_PWRUNDER9V, /* Battery voltage low under 9V */ + BIT_SHUTDNID_S3, /* Entry S3 state */ + BIT_SHUTDNID_S1 /* Entry S1 state */ +}; + +#define INDEX_SYSTEM_CFG 0x82 /* Read System config */ +#define BIT_SYSCFG_TPSWITCH (1 << 0) /* TouchPad switch */ +#define BIT_SYSCFG_WLANPRES (1 << 1) /* WLAN present */ +#define BIT_SYSCFG_NB3GPRES (1 << 2) /* 3G present */ +#define BIT_SYSCFG_CAMERAPRES (1 << 3) /* Camera Present */ +#define BIT_SYSCFG_VOLCTRLEC (1 << 4) /* Volume control by EC */ +#define BIT_SYSCFG_BLCTRLEC (1 << 5) /* Backlight control by EC */ +#define BIT_SYSCFG_AUTOBRIGHT (1 << 7) /* Auto brightness */ + +#define INDEX_VOLUME_LEVEL 0xA6 /* Read Volume Level command */ +#define INDEX_VOLUME_MAXLEVEL 0xA7 /* Volume MaxLevel */ +#define VOLUME_MAX_LEVEL 0x0A /* Volume level max is 11 */ +enum { + FLAG_VOLUME_LEVEL_0 = 0, + FLAG_VOLUME_LEVEL_1, + FLAG_VOLUME_LEVEL_2, + FLAG_VOLUME_LEVEL_3, + FLAG_VOLUME_LEVEL_4, + FLAG_VOLUME_LEVEL_5, + FLAG_VOLUME_LEVEL_6, + FLAG_VOLUME_LEVEL_7, + FLAG_VOLUME_LEVEL_8, + FLAG_VOLUME_LEVEL_9, + FLAG_VOLUME_LEVEL_10 +}; + +/* Camera control */ +#define INDEX_CAM_STSCTRL 0xAA +enum { + CAM_STSCTRL_OFF = 0, + CAM_STSCTRL_ON +}; + +#define INDEX_CPU_TEMP 0x1E /* Read CPU temperature */ +#define INDEX_GPU_TEMP 0x10 /* Read GPU temperature */ + +/* EC_SC input */ +/* EC Status query, by direct read 66h port. */ +#define EC_SMI_EVT (1 << 6) /* 1 = SMI event padding */ +#define EC_SCI_EVT (1 << 5) /* 1 = SCI event padding */ +#define EC_BURST (1 << 4) /* 1 = Controller is in burst mode */ +#define EC_CMD (1 << 3) /* 1 = Byte in data register is command */ + +#define EC_IBF (1 << 1) /* 1 = Input buffer full (data ready for ec) */ +#define EC_OBF (1 << 0) /* 1 = Output buffer full (data ready for host) */ + +/* LPC Event Number from EC */ +enum { + EC_EVENT_NUM_BAT = 0xB3, /* BAT in/out */ + EC_EVENT_NUM_AC = 0xB4, /* AC in/out */ + EC_EVENT_NUM_POWERBTN = 0xB5, /* power button */ + EC_EVENT_NUM_LID = 0xD0, /* press the lid or not */ + EC_EVENT_NUM_RES1 = 0xD1, /* reserve event1 */ + EC_EVENT_NUM_RES2 = 0xD2, /* reserve event2 */ + EC_EVENT_NUM_SLEEP = 0x40, /* 0x40, Fn+F4 for entering sleep mode */ + EC_EVENT_NUM_WLAN = 0x35, /* 0x41, Fn+F6, Wlan is on or off */ + EC_EVENT_NUM_TP, /* 0x43, Fn+F5, TouchPad is on */ + EC_EVENT_NUM_SCREENLOCK, /* 0x43, Fn+F5, ScreenLock */ + /* 0x44, Fn+F3, LCD backlight brightness on or off */ + EC_EVENT_NUM_BRIGHTNESS_OFF, + /* 0x45, Fn+F1, LCD backlight brightness down adjust */ + EC_EVENT_NUM_BRIGHTNESS_DN, + /* 0x46, Fn+F2, LCD backlight brightness up adjust */ + EC_EVENT_NUM_BRIGHTNESS_UP, + EC_EVENT_NUM_DISPLAY_TOGGLE, /* 0x47, Fn+F7, Video switch(LCD/HDMI...)*/ + EC_EVENT_NUM_MICMUTE, /* 0xc2, microphone mute */ + EC_EVENT_NUM_CPU_TEMP, /* 0x3b, read out cpu temperature*/ + EC_EVENT_NUM_RFKILL /* 0xbd, airplane mode switch */ +}; + +#define EC_EVENT_NUM_MIN EC_EVENT_NUM_BAT +#define EC_EVENT_NUM_MAX EC_EVENT_NUM_RES2 +#define EC_EVENT_NUM_GSENSOR EC_EVENT_NUM_RES2 + +#define INDEX_LID_STATUS 0x46 /* Lid status register */ +#define LID_STATUS_BIT (1 << 0) /* Lid status BIT0 1: Closed, 0: Open */ + +typedef int (*ec_handler)(int status); + +extern bool it8528_get_ec_ibf_flags(void); +extern bool it8528_get_ec_obf_flags(void); +extern bool it8528_get_ec_evt_flags(void); + +/* The general ec index-io port read action */ +extern void clean_it8528_event_status(void); +extern unsigned char it8528_read(unsigned char index); +extern unsigned char it8528_read_all(unsigned char command, unsigned char index); +extern unsigned char it8528_read_noindex(unsigned char command); + +/* The general ec index-io port write action */ +extern int it8528_write(unsigned char index, unsigned char data); +extern int it8528_write_all(unsigned char command, unsigned char index, unsigned char data); +extern int it8528_write_noindex(unsigned char command, unsigned char data); + +/* Query sequence of 62/66 port access routine. */ +extern int it8528_query_seq(unsigned char command); +extern int it8528_get_event_num(void); +extern int it8528_query_get_event_num(void); +extern void it8528_ec_event_int_enable(void); +extern void it8528_ec_event_int_disable(void); +extern int it8528_query_clean_event(void); + +/* The ec interrupt functions */ +extern int it8528_init(void); +extern int lpc_ec_interrupt_occurs(void); +extern void lpc_interrupt_clear_all(void); + +/* EC sensor/hwmon registration */ +extern void ec_sensors_register(struct platform_device *pdev); + +#endif /* __EC_IT8528_H__ */ diff --git a/drivers/platform/arm64/phytium/ec_sensor.c b/drivers/platform/arm64/phytium/ec_sensor.c new file mode 100644 index 000000000000..c2eb2f632b9a --- /dev/null +++ b/drivers/platform/arm64/phytium/ec_sensor.c @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include "ec_it8528.h" + +/*----------------------------------------------------------------------*/ + +#if IS_REACHABLE(CONFIG_HWMON) + +/* + * Temperature and Fan support form ec devices. + */ +static ssize_t ec_bat_temp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int temp = 0; + + temp = (it8528_read(INDEX_BATTERY_TEMP_HIGH) << 8) | it8528_read(INDEX_BATTERY_TEMP_LOW); + + temp = temp - 2730; + + return sprintf(buf, "%d.%d\n", temp/10, temp%10); +} + +static ssize_t ec_gpu_temp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char temp = 0; + + temp = it8528_read(INDEX_GPU_TEMP_VALUE); + + return sprintf(buf, "%d\n", temp); +} + +static ssize_t ec_cpu_fan_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int speed = 0; + + speed = (it8528_read(INDEX_CPU_FAN_SPEED_HIGH) << 8) | it8528_read(INDEX_CPU_FAN_SPEED_LOW); + + return sprintf(buf, "%d\n", speed); +} + +static ssize_t ec_gpu_fan_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int speed = 0; + + speed = (it8528_read(INDEX_GPU_FAN_SPEED_HIGH) << 8) | it8528_read(INDEX_GPU_FAN_SPEED_LOW); + + return sprintf(buf, "%d\n", speed); +} + +static SENSOR_DEVICE_ATTR(ec_bat_temp, 0444, ec_bat_temp_show, + NULL, 0); +static SENSOR_DEVICE_ATTR(ec_gpu_temp, 0444, ec_gpu_temp_show, + NULL, 0); +static SENSOR_DEVICE_ATTR(ec_cpu_fan, 0444, ec_cpu_fan_show, + NULL, 0); +static SENSOR_DEVICE_ATTR(ec_gpu_fan, 0444, ec_gpu_fan_show, + NULL, 0); + +static struct attribute *ec_bat_temp_hwmon_attrs[] = { + &sensor_dev_attr_ec_bat_temp.dev_attr.attr, + NULL, +}; +static struct attribute *ec_gpu_temp_hwmon_attrs[] = { + &sensor_dev_attr_ec_gpu_temp.dev_attr.attr, + NULL, +}; +static struct attribute *ec_cpu_fan_hwmon_attrs[] = { + &sensor_dev_attr_ec_cpu_fan.dev_attr.attr, + NULL, +}; +static struct attribute *ec_gpu_fan_hwmon_attrs[] = { + &sensor_dev_attr_ec_gpu_fan.dev_attr.attr, + NULL, +}; + +ATTRIBUTE_GROUPS(ec_bat_temp_hwmon); +ATTRIBUTE_GROUPS(ec_gpu_temp_hwmon); +ATTRIBUTE_GROUPS(ec_cpu_fan_hwmon); +ATTRIBUTE_GROUPS(ec_gpu_fan_hwmon); + +void ec_sensors_register(struct platform_device *pdev) +{ + struct device *hwmon_dev; + + char *bat_temp_data, *gpu_temp_data, *cpu_fan_data, *gpu_fan_data; + + /* register bat temp hwmon device */ + bat_temp_data = kzalloc(0x10, GFP_KERNEL); + + hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev, "EC_BAT_TEMP", + bat_temp_data, + ec_bat_temp_hwmon_groups); + if (IS_ERR(hwmon_dev)) { + dev_err(&pdev->dev, "unable to register hwmon device %ld\n", + PTR_ERR(hwmon_dev)); + } + + /* register gpu temp hwmon device */ + gpu_temp_data = kzalloc(0x10, GFP_KERNEL); + + hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev, "EC_GPU_TEMP", + gpu_temp_data, + ec_gpu_temp_hwmon_groups); + if (IS_ERR(hwmon_dev)) { + dev_err(&pdev->dev, "unable to register hwmon device %ld\n", + PTR_ERR(hwmon_dev)); + } + + /* register cpu fan hwmon device */ + cpu_fan_data = kzalloc(0x10, GFP_KERNEL); + + hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev, "EC_CPU_FAN", + cpu_fan_data, + ec_cpu_fan_hwmon_groups); + if (IS_ERR(hwmon_dev)) { + dev_err(&pdev->dev, "unable to register hwmon device %ld\n", + PTR_ERR(hwmon_dev)); + } + + /* register cpu fan hwmon device */ + gpu_fan_data = kzalloc(0x10, GFP_KERNEL); + + hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev, "EC_GPU_FAN", + gpu_fan_data, + ec_gpu_fan_hwmon_groups); + if (IS_ERR(hwmon_dev)) { + dev_err(&pdev->dev, "unable to register hwmon device %ld\n", + PTR_ERR(hwmon_dev)); + } + +} + +#else + +void ec_sensors_register(struct platform_device *pdev) +{ +} + +#endif /* IS_REACHABLE(CONFIG_HWMON) */ diff --git a/drivers/platform/arm64/phytium/phyt_laptop.c b/drivers/platform/arm64/phytium/phyt_laptop.c new file mode 100644 index 000000000000..de8f395e0c2f --- /dev/null +++ b/drivers/platform/arm64/phytium/phyt_laptop.c @@ -0,0 +1,1367 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ec_it8528.h" +#include +#include +#include +#include +#include +#include +#include + +static inline u16 ec_read16(u8 high, u8 low) +{ + return (it8528_read(high) << 8) | it8528_read(low); +} + + +static bool ft_display_state = true; +static unsigned int ft_display_level; +static int ft_set_display_on(bool on); +static int ec_gsonsor_init(struct platform_device *pdev); + +#define EC_LPC_DEV "ec" + + +/* Power supply */ +enum { + APM_AC_OFFLINE = 0, + APM_AC_ONLINE, + APM_AC_BACKUP, + APM_AC_UNKNOWN = 0xff +}; + +enum { + APM_BAT_STATUS_HIGH = 0, + APM_BAT_STATUS_LOW, + APM_BAT_STATUS_CRITICAL, + APM_BAT_STATUS_CHARGING, + APM_BAT_STATUS_NOT_PRESENT, + APM_BAT_STATUS_UNKNOWN = 0xff +}; + +enum /* bat_reg_flag */ +{ + BAT_REG_TEMP_FLAG = 1, + BAT_REG_VOLTAGE_FLAG, + BAT_REG_CURRENT_FLAG, + BAT_REG_AC_FLAG, + BAT_REG_RC_FLAG, + BAT_REG_FCC_FLAG, + BAT_REG_ATTE_FLAG, + BAT_REG_ATTF_FLAG, + BAT_REG_RSOC_FLAG, + BAT_REG_CYCLCNT_FLAG +}; + +/* Power info cached timeout */ +#define POWER_INFO_CACHED_TIMEOUT 100 /* jiffies */ + +/* Backlight */ +#define MAX_BRIGHTNESS 100 + +/* Screen rotation */ +#define EC_GSENSOR_X_HIGH 0X63 +#define EC_GSENSOR_X_LOW 0X64 +#define EC_GSENSOR_Y_HIGH 0X65 +#define EC_GSENSOR_Y_LOW 0X66 +#define EC_GSENSOR_Z_HIGH 0X67 +#define EC_GSENSOR_Z_LOW 0X68 + +/* Power information structure */ +struct ft_power_info { + /* AC insert or not */ + unsigned int ac_in; + /* Battery insert or not */ + unsigned int bat_in; + unsigned int health; + + /* Battery designed capacity */ + unsigned int design_capacity; + /* Battery designed voltage */ + unsigned int design_voltage; + /* Battery capacity after full charged */ + unsigned int full_charged_capacity; + /* Battery Manufacture Date */ + unsigned char manufacture_date[11]; + /* Battery Serial number */ + unsigned char serial_number[8]; + /* Battery Manufacturer Name, max 11 + 1(length) bytes */ + unsigned char manufacturer_name[12]; + /* Battery Device Name, max 7 + 1(length) bytes */ + unsigned char device_name[8]; + /* Battery Technology */ + unsigned int technology; + /* Battery cell count */ + unsigned char cell_count; + + /* Battery dynamic charge/discharge voltage */ + unsigned int voltage_now; + /* Battery dynamic charge/discharge average current */ + int current_now; + int current_sign; + int current_average; + /* Battery current remaining capacity */ + unsigned int remain_capacity; + /* Battery current remaining capacity percent */ + unsigned int remain_capacity_percent; + /* Battery current temperature */ + unsigned int temperature; + /* Battery current remaining time (AverageTimeToEmpty) */ + unsigned int remain_time; + /* Battery current full charging time (averageTimeToFull) */ + unsigned int fullchg_time; + /* Battery Status */ + unsigned int charge_status; + /* Battery current cycle count (CycleCount) */ + unsigned int cycle_count; +}; + +/* LPC device structure */ +struct lpc_device { + /* The lpc number get from ec */ + unsigned char number; + /* count */ + unsigned char parameter; + /* Irq relative */ + unsigned char irq; + unsigned char irq_data; + /* Device name */ + unsigned char name[10]; +}; + + +/* LPC device object */ +static struct lpc_device *ft_lpc_device; +static struct lpc_device *ft_gpio_device; +static int ft_lpc_event_probe(struct platform_device *); + +static int ft_get_brightness(struct backlight_device *); +static int ft_set_brightness(struct backlight_device *); + +/* >>>Power management operation */ +/* Update battery information handle function. */ +static void ft_power_battery_info_update(unsigned char bat_reg_flag); +/* Clear battery static information. */ +static void ft_power_info_battery_static_clear(void); +/* Get battery static information. */ +static void ft_power_info_battery_static_update(void); +/* Update power_status value */ +static void ft_power_info_power_status_update(void); +static void ft_bat_get_string(unsigned char index, unsigned char *bat_string); +/* Power supply Battery get property handler */ +static int ft_bat_get_property(struct power_supply *pws, + enum power_supply_property psp, union power_supply_propval *val); +/* Power supply AC get property handler */ +static int ft_ac_get_property(struct power_supply *pws, + enum power_supply_property psp, union power_supply_propval *val); +/* LPC device AC event handler */ +static void ft_ac_handler(int status); +/* LPC device Battery event handler */ +static void ft_bat_handler(int status); + +/* <<code); + input_report_key(dev, ke->keycode, value); + input_sync(dev); + if (value && autorelease) { + input_report_key(dev, ke->keycode, 0); + input_sync(dev); + } +} + +void ft_ec_bl_dn_event(void) +{ + const struct key_entry *key = &ft_keymap[4]; + + sparse_keymap_report_entry(ft_hotkey_dev, key, 1, true); +} +EXPORT_SYMBOL(ft_ec_bl_dn_event); + +void ft_ec_bl_up_event(void) +{ + const struct key_entry *key = &ft_keymap[5]; + + sparse_keymap_report_entry(ft_hotkey_dev, key, 1, true); +} +EXPORT_SYMBOL(ft_ec_bl_up_event); + +void ft_ec_lid_event(void) +{ + const struct key_entry *key = &ft_keymap[0]; + + __clear_bit(SW_LID, ft_hotkey_dev->sw); + input_report_switch(ft_hotkey_dev, key->sw.code, 1); + input_sync(ft_hotkey_dev); + +} +EXPORT_SYMBOL(ft_ec_lid_event); + +void ft_ec_display_off(void) +{ + const struct key_entry *key = &ft_keymap[3]; + + sparse_keymap_report_entry(ft_hotkey_dev, key, 1, true); + ft_set_display_on(!ft_display_state); +} +EXPORT_SYMBOL(ft_ec_display_off); + +void ft_ec_display_event(void) +{ + const struct key_entry *key = &ft_keymap[6]; + + sparse_keymap_report_entry(ft_hotkey_dev, key, 1, true); +} +EXPORT_SYMBOL(ft_ec_display_event); + +void ft_ec_wifi_event(void) +{ + const struct key_entry *key = &ft_keymap[7]; + + sparse_keymap_report_entry(ft_hotkey_dev, key, 1, true); +} +EXPORT_SYMBOL(ft_ec_wifi_event); + +void ft_ec_micmute_event(void) +{ + const struct key_entry *key = &ft_keymap[10]; + + sparse_keymap_report_entry(ft_hotkey_dev, key, 1, true); +} +EXPORT_SYMBOL(ft_ec_micmute_event); + +void ft_ec_airplane_event(void) +{ + const struct key_entry *key = &ft_keymap[11]; + + sparse_keymap_report_entry(ft_hotkey_dev, key, 1, true); +} + +void ft_ec_screen_lock_event(void) +{ + const struct key_entry *key = &ft_keymap[9]; + + sparse_keymap_report_entry(ft_hotkey_dev, key, 1, true); +} +EXPORT_SYMBOL(ft_ec_screen_lock_event); + +void ft_ec_tp_event(void) +{ + const struct key_entry *key = &ft_keymap[2]; + + sparse_keymap_report_entry(ft_hotkey_dev, key, 1, true); +} +EXPORT_SYMBOL(ft_ec_tp_event); + +void ft_ec_screenlock_event(void) +{ + const struct key_entry *key = &ft_keymap[9]; + + sparse_keymap_report_entry(ft_hotkey_dev, key, 1, true); +} +EXPORT_SYMBOL(ft_ec_screenlock_event); + +void ft_sleep_event(void) +{ + const struct key_entry *key = &ft_keymap[1]; + + sparse_keymap_report_entry(ft_hotkey_dev, key, 1, true); +} +EXPORT_SYMBOL(ft_sleep_event); + +void ft_ec_powerbtn_event(void) +{ + const struct key_entry *key = &ft_keymap[8]; + + sparse_keymap_report_entry(ft_hotkey_dev, key, 1, true); +} +EXPORT_SYMBOL(ft_ec_powerbtn_event); + +void ft_ec_screen_rotation_event(void) +{ + int x_val = 0, y_val = 0, z_val = 0; + + x_val = (it8528_read(EC_GSENSOR_X_HIGH) << 8) | it8528_read(EC_GSENSOR_X_LOW); + y_val = (it8528_read(EC_GSENSOR_Y_HIGH) << 8) | it8528_read(EC_GSENSOR_Y_LOW); + z_val = (it8528_read(EC_GSENSOR_Z_HIGH) << 8) | it8528_read(EC_GSENSOR_Z_LOW); + + input_event(ec_gsensor_dev, EV_ABS, ABS_X, x_val); + input_event(ec_gsensor_dev, EV_ABS, ABS_Y, y_val); + input_event(ec_gsensor_dev, EV_ABS, ABS_Z, z_val); + input_sync(ec_gsensor_dev); +} + +static void ft_ec_powerbtn_event_handler(int event) +{ + ft_ec_powerbtn_event(); +} + +static void ft_ec_screen_rotation_event_handler(int event) +{ + ft_ec_screen_rotation_event(); +} + +/* LPC device event handler */ +static const struct { + int event; + void (*handler)(int event); +} ec_event_table[] = { + { EC_EVENT_NUM_LID, ft_lid_handler }, + { EC_EVENT_NUM_AC, ft_ac_handler }, + { EC_EVENT_NUM_BAT, ft_bat_handler }, + { EC_EVENT_NUM_POWERBTN, ft_ec_powerbtn_event_handler }, + { EC_EVENT_NUM_GSENSOR, ft_ec_screen_rotation_event_handler }, +}; + +void ft_ec_event_handler(int event) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ec_event_table); i++) { + if (ec_event_table[i].event == event) { + ec_event_table[i].handler(event); + return; + } + } +} + + +static irqreturn_t ft_ec_int_routine(int irq, void *dev_id) +{ + int event; + unsigned long flags; + + spin_lock_irqsave(&i8042_lock, flags); + + /* if not ec irq */ + if (!lpc_ec_interrupt_occurs()) { + spin_unlock_irqrestore(&i8042_lock, flags); + return IRQ_NONE; + } + + /* Clean lpc irq */ + lpc_interrupt_clear_all(); + + event = it8528_query_get_event_num(); + + + if ((event < EC_EVENT_NUM_MIN) || (event > EC_EVENT_NUM_MAX)) + goto exit_event_action; + + ft_ec_event_handler(event); + spin_unlock_irqrestore(&i8042_lock, flags); + return IRQ_HANDLED; + +exit_event_action: + spin_unlock_irqrestore(&i8042_lock, flags); + return IRQ_HANDLED; +} + +/* LPC driver init */ +static int lpc_int_init(void) +{ + int ret = -EIO; + + pr_debug("phytium: lpc init.\n"); + + ft_lpc_device = kmalloc(sizeof(struct lpc_device), GFP_KERNEL); + if (!ft_lpc_device) + return -ENOMEM; + + ft_lpc_device->irq = 0x25; + ft_lpc_device->irq_data = 0x00; + ft_lpc_device->number = 0x00; + ft_lpc_device->parameter = 0x00; + strscpy(ft_lpc_device->name, EC_LPC_DEV, sizeof(ft_lpc_device->name)); + + /* get lpc irq */ + ft_lpc_device->irq = phytium_pio_get_irq(); + if (ft_lpc_device->irq < 0) + goto out_pdev; + + + /* clear all interrupt status */ + lpc_interrupt_clear_all(); + + /* Regist pci irq */ + ret = request_irq(ft_lpc_device->irq, ft_ec_int_routine, + IRQF_SHARED, ft_lpc_device->name, ft_lpc_device); + if (ret) { + pr_err("phytium ec: Request irq fail.\n"); + ret = -EFAULT; + goto out_irq; + } + + ret = 0; + pr_debug("phytium: LPC init successful.\n"); + + return ret; + +out_irq: +out_pdev: + kfree(ft_lpc_device); + return ret; +} + +/* ec driver interrupt init handler */ +static int lpc_driver_init(void) +{ + int ret; + + ret = lpc_int_init(); + if (ret) { + pr_err("phytium: Register lpc driver fail.\n"); + return ret; + } + pr_debug("phytium: register lpc driver done.\n"); + return ret; +} + +int ec_event_int_error; + +static void ec_int_routine_func(struct work_struct *work) +{ + int event; + unsigned long flags; + + spin_lock_irqsave(&i8042_lock, flags); + event = it8528_query_get_event_num(); + if ((event < EC_EVENT_NUM_MIN) || (event > EC_EVENT_NUM_MAX)) { + pr_err("ec_int_gpio_routine handle event = 0x%x.\r\n", event); + ec_event_int_error = true; + spin_unlock_irqrestore(&i8042_lock, flags); + return; + } + ft_ec_event_handler(event); + spin_unlock_irqrestore(&i8042_lock, flags); +} +DECLARE_WORK(ec_int_routine_wq, ec_int_routine_func); + +static irqreturn_t ft_ec_int_gpio_routine(int irq, void *dev_id) +{ + unsigned long flags; + + spin_lock_irqsave(&i8042_lock, flags); + disable_irq_nosync(irq); + if (ec_event_int_error && !it8528_get_ec_evt_flags()) { /*sci_evt flags*/ + goto event_error; + } + + schedule_work(&ec_int_routine_wq); +event_error: + enable_irq(irq); + spin_unlock_irqrestore(&i8042_lock, flags); + return IRQ_HANDLED; +} + +static int set_func_to_gpio(void) +{ + int reg; + int pin_mul_contrl_base = 0x28180000; + void __iomem *gpio_func_ioremap = NULL; + + gpio_func_ioremap = ioremap(pin_mul_contrl_base, 0x1000); + if (!gpio_func_ioremap) { + pr_err("Could not ioreamp pin_mul_contrl_base!\n"); + return -ENOMEM; + } + + reg = readl(gpio_func_ioremap + 0x204); + + reg |= BIT(31); + reg &= ~BIT(30); + + reg &= ~BIT(29); + reg |= BIT(28); + + writel(reg, gpio_func_ioremap + 0x204); + + iounmap(gpio_func_ioremap); + return 0; +} + +static int gpio_driver_init(int irq) +{ + int ret; + + ft_gpio_device = kmalloc(sizeof(struct lpc_device), GFP_KERNEL); + if (!ft_gpio_device) + return -ENOMEM; + + /* Get GPIO7 soft irq*/ + ft_gpio_device->irq = irq; + strscpy(ft_gpio_device->name, "ec_gpio", sizeof(ft_gpio_device->name)); + + /* Configure GPIO pin function */ + + ret = set_func_to_gpio(); + if (ret) { + pr_err("set gpio func failed.\n"); + return -ENOMEM; + } + + /* Set GPIO related configuration */ + irq_set_irq_type(ft_gpio_device->irq, IRQ_TYPE_EDGE_RISING); + ret = request_irq(ft_gpio_device->irq, ft_ec_int_gpio_routine, + IRQF_TRIGGER_RISING, ft_gpio_device->name, ft_gpio_device); + if (ret) { + pr_err("phytium: Request irq fail.\n"); + return -EFAULT; + } + return ret; +} + +static int ft_set_display_on(bool on) +{ + if (!chassis_types_is_laptop()) + return 0; + + if (on) { + ft_display_state = true; + it8528_write(INDEX_DISPLAY_BRIGHTNESS_SET, ft_display_level); + } else { + ft_display_state = false; + ft_display_level = it8528_read(INDEX_DISPLAY_BRIGHTNESS_GET); + it8528_write(INDEX_DISPLAY_BRIGHTNESS_SET, 0); + } + return 0; +} + +/* Backlight device set brightness handler */ +static int ft_set_brightness(struct backlight_device *pdev) +{ + unsigned int level = 0; + + if (!chassis_types_is_laptop() && !chassis_types_is_allinone()) + return 0; + + if (!ft_display_state) + return 0; + + level = ((pdev->props.fb_blank == FB_BLANK_UNBLANK) && + (pdev->props.power == FB_BLANK_UNBLANK)) ? + pdev->props.brightness : 0; + + if (level > MAX_BRIGHTNESS) + level = MAX_BRIGHTNESS; + else if (level <= 0) + level = 1; + + it8528_write(INDEX_DISPLAY_BRIGHTNESS_SET, level); + + return 0; +} + +/* Backlight device get brightness handler */ +static int ft_get_brightness(struct backlight_device *pdev) +{ + if (!chassis_types_is_laptop() && !chassis_types_is_allinone()) + return 0; + + /* Read level from ec */ + return it8528_read(INDEX_DISPLAY_BRIGHTNESS_GET); +} + +static void ft_bat_update_temp(void) +{ + int bat_info_value; + + ft_power_info_power_status_update(); + bat_info_value = ec_read16(INDEX_BATTERY_TEMP_HIGH, INDEX_BATTERY_TEMP_LOW); + power_info->temperature = power_info->bat_in ? (bat_info_value - 2730) : 0; +} + +static void ft_bat_update_voltage(void) +{ + int bat_info_value = ec_read16(INDEX_BATTERY_VOL_HIGH, INDEX_BATTERY_VOL_LOW); + + power_info->voltage_now = power_info->bat_in ? bat_info_value : 0; +} + +static void ft_bat_update_current(void) +{ + int bat_info_value = + ec_read16(INDEX_BATTERY_CURRENT_HIGH, INDEX_BATTERY_CURRENT_LOW); + + power_info->current_now = power_info->bat_in ? bat_info_value : 0; +} + +static void ft_bat_update_ac(void) +{ + int bat_info_value = ec_read16(INDEX_BATTERY_AC_HIGH, INDEX_BATTERY_AC_LOW); + + ft_power_info_power_status_update(); + power_info->current_average = power_info->bat_in ? bat_info_value : 0; +} + +static void ft_bat_update_rc(void) +{ + power_info->remain_capacity = + ec_read16(INDEX_BATTERY_RC_HIGH, INDEX_BATTERY_RC_LOW); +} + +static void ft_bat_update_fcc(void) +{ + power_info->full_charged_capacity = + ec_read16(INDEX_BATTERY_FCC_HIGH, INDEX_BATTERY_FCC_LOW); +} + +static void ft_bat_update_atte(void) +{ + int bat_info_value = ec_read16(INDEX_BATTERY_AC_HIGH, INDEX_BATTERY_AC_LOW); + + bat_info_value = bat_info_value > 0 ? bat_info_value : -bat_info_value; + if (bat_info_value) + power_info->remain_time = 60 * + ec_read16(INDEX_BATTERY_RC_HIGH, INDEX_BATTERY_RC_LOW) / + bat_info_value; +} + +static void ft_bat_update_attf(void) +{ + int bat_info_value = + ec_read16(INDEX_BATTERY_CAP_HIGH, INDEX_BATTERY_CAP_LOW) - + ec_read16(INDEX_BATTERY_RC_HIGH, INDEX_BATTERY_RC_LOW); + int bat_info_value_ac = + ec_read16(INDEX_BATTERY_AC_HIGH, INDEX_BATTERY_AC_LOW); + + if (bat_info_value_ac > 0) + power_info->fullchg_time = 60 * bat_info_value / bat_info_value_ac; +} + +static void ft_bat_update_rsoc(void) +{ + power_info->remain_capacity_percent = it8528_read(INDEX_BATTERY_CAPACITY); +} + +static void ft_bat_update_cyclcnt(void) +{ + power_info->cycle_count = + ec_read16(INDEX_BATTERY_CYCLECNT_HIGH, INDEX_BATTERY_CYCLECNT_LOW); +} + +static void (* const bat_update_table[])(void) = { + ft_bat_update_temp, + ft_bat_update_voltage, + ft_bat_update_current, + ft_bat_update_ac, + ft_bat_update_rc, + ft_bat_update_fcc, + ft_bat_update_atte, + ft_bat_update_attf, + ft_bat_update_rsoc, + ft_bat_update_cyclcnt, +}; + +/* Update battery information handle function. */ +static void ft_power_battery_info_update(unsigned char bat_reg_flag) +{ + if (bat_reg_flag < BAT_REG_TEMP_FLAG || bat_reg_flag > BAT_REG_CYCLCNT_FLAG) + return; + bat_update_table[bat_reg_flag - 1](); +} + +/* Update battery information handle function. */ +static void ft_power_battery_info_init(void) +{ + int flag; + + pr_debug("%s: start ...\r\n", __func__); + + + for (flag = BAT_REG_TEMP_FLAG; flag <= BAT_REG_CYCLCNT_FLAG; flag++) + ft_power_battery_info_update(flag); + + pr_debug("%s: end ...\r\n", __func__); +} + +/* Clear battery static information. */ +static void ft_power_info_battery_static_clear(void) +{ + strscpy(power_info->manufacturer_name, "Unknown", sizeof(power_info->manufacturer_name)); + strscpy(power_info->device_name, "Unknown", sizeof(power_info->device_name)); + power_info->technology = POWER_SUPPLY_TECHNOLOGY_UNKNOWN; + strscpy(power_info->serial_number, "Unknown", sizeof(power_info->serial_number)); + strscpy(power_info->manufacture_date, "Unknown", sizeof(power_info->manufacture_date)); + power_info->cell_count = 0; + power_info->design_capacity = 0; + power_info->design_voltage = 0; +} + +/* Get battery static information. */ +static void ft_power_info_battery_static_update(void) +{ + unsigned int bat_serial_number; + + power_info->technology = POWER_SUPPLY_TECHNOLOGY_LION; + + bat_serial_number = ec_read16(INDEX_BATTERY_SN_HIGH, INDEX_BATTERY_SN_LOW); + snprintf(power_info->serial_number, 8, "%x", bat_serial_number); + + power_info->cell_count = ec_read16(INDEX_BATTERY_CV_HIGH, INDEX_BATTERY_CV_LOW) / 4200; + + power_info->design_capacity = ec_read16(INDEX_BATTERY_DC_HIGH, INDEX_BATTERY_DC_LOW); + power_info->design_voltage = ec_read16(INDEX_BATTERY_DV_HIGH, INDEX_BATTERY_DV_LOW); + power_info->full_charged_capacity = + ec_read16(INDEX_BATTERY_FCC_HIGH, INDEX_BATTERY_FCC_LOW); + pr_debug("DesignCapacity: %dmAh, DesignVoltage: %dmV, FullChargeCapacity: %dmAh\n", + power_info->design_capacity, + power_info->design_voltage, + power_info->full_charged_capacity); +} + +/* Update power_status value */ +static void ft_power_info_power_status_update(void) +{ + unsigned int power_status = 0; + + power_status = it8528_read(INDEX_POWER_STATUS); + + power_info->ac_in = (power_status & MASK(BIT_POWER_ACPRES)) ? + APM_AC_ONLINE : APM_AC_OFFLINE; + + power_info->bat_in = (power_status & MASK(BIT_POWER_BATPRES)) ? 1 : 0; + if (power_info->bat_in && + ec_read16(INDEX_BATTERY_DC_HIGH, INDEX_BATTERY_DC_LOW) == 0) + power_info->bat_in = 0; + + power_info->health = power_info->bat_in ? + POWER_SUPPLY_HEALTH_GOOD : + POWER_SUPPLY_HEALTH_UNKNOWN; + if (!power_info->bat_in) { + power_info->charge_status = POWER_SUPPLY_STATUS_UNKNOWN; + } else { + power_status = it8528_read(INDEX_BATTERY_STATUS); + + if (!(power_status & MASK(BIT_POWER_BATCHG))) + power_info->charge_status = POWER_SUPPLY_STATUS_CHARGING; + else if ((power_status & MASK(BIT_POWER_BATFCHG)) && + power_info->ac_in == APM_AC_ONLINE) + power_info->charge_status = POWER_SUPPLY_STATUS_FULL; + else + power_info->charge_status = POWER_SUPPLY_STATUS_DISCHARGING; + } +} + +/* Get battery static information string */ +static void __maybe_unused ft_bat_get_string(unsigned char index, unsigned char *bat_string) +{ + unsigned char length, i; + + length = it8528_read(index); + for (i = 0; i < length; i++) + *bat_string++ = it8528_read(++index); + *bat_string = '\0'; +} + +/* Power supply Battery get property handler */ +static int ft_bat_get_property(struct power_supply *pws, + enum power_supply_property psp, union power_supply_propval *val) +{ + unsigned long flags; + + spin_lock_irqsave(&i8042_lock, flags); + switch (psp) { + /* Get battery static information. */ + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + val->intval = power_info->design_voltage * 1000; /* mV -> uV */ + break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + val->intval = power_info->design_capacity * 1000; /* mAh -> uAh */ + break; + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = power_info->device_name; + break; + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = power_info->manufacturer_name; + break; + case POWER_SUPPLY_PROP_SERIAL_NUMBER: + val->strval = power_info->serial_number; + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = power_info->technology; + break; + /* Get battery dynamic information. */ + case POWER_SUPPLY_PROP_STATUS: + ft_power_info_power_status_update(); + val->intval = power_info->charge_status; + break; + case POWER_SUPPLY_PROP_PRESENT: + ft_power_info_power_status_update(); + val->intval = power_info->bat_in; + break; + case POWER_SUPPLY_PROP_HEALTH: + ft_power_info_power_status_update(); + val->intval = power_info->health; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + ft_power_battery_info_update(BAT_REG_CURRENT_FLAG); + val->intval = power_info->current_now * 1000; /* mA -> uA */ + break; + case POWER_SUPPLY_PROP_CURRENT_AVG: + ft_power_battery_info_update(BAT_REG_AC_FLAG); + val->intval = power_info->current_average * 1000; /* mA -> uA */ + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ft_power_battery_info_update(BAT_REG_VOLTAGE_FLAG); + val->intval = power_info->voltage_now * 1000; /* mV -> uV */ + break; + case POWER_SUPPLY_PROP_CHARGE_NOW: + ft_power_battery_info_update(BAT_REG_RC_FLAG); + val->intval = power_info->remain_capacity * 1000; /* mAh -> uAh */ + break; + case POWER_SUPPLY_PROP_CAPACITY: + ft_power_battery_info_update(BAT_REG_RSOC_FLAG); + val->intval = power_info->remain_capacity_percent; /* Percentage */ + break; + case POWER_SUPPLY_PROP_TEMP: + ft_power_battery_info_update(BAT_REG_TEMP_FLAG); + val->intval = power_info->temperature; /* Celcius */ + break; + case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: + ft_power_battery_info_update(BAT_REG_ATTE_FLAG); + if (power_info->remain_time == 0xFFFF) + power_info->remain_time = 0; + val->intval = power_info->remain_time * 60; /* seconds */ + break; + case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: + ft_power_battery_info_update(BAT_REG_ATTF_FLAG); + if (power_info->fullchg_time == 0xFFFF) + power_info->fullchg_time = 0; + val->intval = power_info->fullchg_time * 60; /* seconds */ + break; + case POWER_SUPPLY_PROP_CHARGE_FULL: + ft_power_battery_info_update(BAT_REG_FCC_FLAG); + val->intval = power_info->full_charged_capacity * 1000;/* mAh -> uAh */ + break; + case POWER_SUPPLY_PROP_CYCLE_COUNT: + ft_power_battery_info_update(BAT_REG_CYCLCNT_FLAG); + val->intval = power_info->cycle_count; + break; + default: + spin_unlock_irqrestore(&i8042_lock, flags); + return -EINVAL; + } + spin_unlock_irqrestore(&i8042_lock, flags); + return 0; +} + +/* Power supply AC get property handler */ +static int ft_ac_get_property(struct power_supply *pws, + enum power_supply_property psp, union power_supply_propval *val) +{ + unsigned long flags; + + spin_lock_irqsave(&i8042_lock, flags); + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + ft_power_info_power_status_update(); + val->intval = power_info->ac_in; + break; + default: + spin_unlock_irqrestore(&i8042_lock, flags); + return -EINVAL; + } + spin_unlock_irqrestore(&i8042_lock, flags); + return 0; +} + +/* EC device AC event handler */ +static void ft_ac_handler(int status) +{ + /* Report status changed */ + power_supply_changed(ec_ac); +} + +/* EC device AC event handler */ +int ft_ac_handler_test(void) +{ + /* Report status changed */ + power_supply_changed(ec_ac); + + return 0; +} +EXPORT_SYMBOL(ft_ac_handler_test); + +/* LPC device Battery event handler */ +static void ft_bat_handler(int status) +{ + /* Battery insert/pull-out to handle battery static information. */ + if (status & MASK(BIT_POWER_BATPRES)) { + /* If battery is insert, get battery static information. */ + ft_power_info_battery_static_update(); + } else { + /* Else if battery is pull-out, clear battery static information. */ + ft_power_info_battery_static_clear(); + } + /* Report status changed */ + power_supply_changed(ec_bat); +} + +/* LPC device LID event handler */ +static void ft_lid_handler(int status) +{ + ft_ec_lid_event(); +} + +/* Hotkey device init */ +int ft_hotkey_init(void) +{ + int ret; + + if (ft_hotkey_dev) + return 0; + + ft_hotkey_dev = input_allocate_device(); + if (!ft_hotkey_dev) + return -ENOMEM; + + ft_hotkey_dev->name = "phytium Laptop Hotkeys"; + ft_hotkey_dev->phys = "button/input0"; + ft_hotkey_dev->id.bustype = BUS_HOST; + ft_hotkey_dev->dev.parent = NULL; + set_bit(EV_KEY, ft_hotkey_dev->evbit); + set_bit(EV_SW, ft_hotkey_dev->evbit); + set_bit(KEY_POWER, ft_hotkey_dev->keybit); + set_bit(SW_LID, ft_hotkey_dev->swbit); + + + ret = sparse_keymap_setup(ft_hotkey_dev, ft_keymap, NULL); + if (ret) { + pr_err("phytium Laptop Platform Driver: Fail to setup input device keymap\n"); + input_free_device(ft_hotkey_dev); + + return ret; + } + + ret = input_register_device(ft_hotkey_dev); + if (ret) { + input_free_device(ft_hotkey_dev); + + return ret; + } + return 0; +} +EXPORT_SYMBOL(ft_hotkey_init); + +/* Hotkey device exit */ +static void ft_hotkey_exit(void) +{ + if (ft_hotkey_dev) { + input_unregister_device(ft_hotkey_dev); + ft_hotkey_dev = NULL; + } +} + +static ssize_t gsensor_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int x_val = 0; + + x_val = (it8528_read(EC_GSENSOR_X_HIGH) << 8) | it8528_read(EC_GSENSOR_X_LOW); + return sprintf(buf, "%d\n", x_val); +} + +static struct kobj_attribute gsensor_attr = + __ATTR(value_x, 0644, gsensor_show, NULL); + +static struct attribute *gsensor_attrs[] = { + &gsensor_attr.attr, + NULL, +}; + +static struct attribute_group gsensor_attr_group = { + .attrs = gsensor_attrs, +}; + +static struct kobject *gsensor_kobj; + +/* Gsensor device init */ +int ec_gsonsor_init(struct platform_device *pdev) +{ + int ret; + + if (ec_gsensor_dev) + return 0; + + ec_gsensor_dev = input_allocate_device(); + if (!ec_gsensor_dev) + return -ENOMEM; + + ec_gsensor_dev->name = "EC_Gsensor"; + ec_gsensor_dev->uniq = "EC_Gsensor"; + ec_gsensor_dev->id.bustype = BUS_HOST; + ec_gsensor_dev->dev.parent = &pdev->dev; + + set_bit(EV_SYN, ec_gsensor_dev->evbit); + set_bit(EV_ABS, ec_gsensor_dev->evbit); + set_bit(ABS_X, ec_gsensor_dev->absbit); + set_bit(ABS_Y, ec_gsensor_dev->absbit); + set_bit(ABS_Z, ec_gsensor_dev->absbit); + + input_set_capability(ec_gsensor_dev, EV_ABS, ABS_X); + input_set_capability(ec_gsensor_dev, EV_ABS, ABS_Y); + input_set_capability(ec_gsensor_dev, EV_ABS, ABS_Z); + input_set_abs_params(ec_gsensor_dev, ABS_X, 0, 0xFFFF, 0, 0); + input_set_abs_params(ec_gsensor_dev, ABS_Y, 0, 0xFFFF, 0, 0); + input_set_abs_params(ec_gsensor_dev, ABS_Z, 0, 0xFFFF, 0, 0); + + ret = input_register_device(ec_gsensor_dev); + if (ret) { + input_free_device(ec_gsensor_dev); + return ret; + } + + gsensor_kobj = kobject_create_and_add("gsensor", &pdev->dev.kobj); + if (!gsensor_kobj) + return -ENOMEM; + ret = sysfs_create_group(gsensor_kobj, &gsensor_attr_group); + if (ret) { + kobject_put(gsensor_kobj); + return ret; + } + + return 0; +} + +/* Gsensor device exit */ +static void ec_gsensor_exit(void) +{ + if (ec_gsensor_dev) { + input_unregister_device(ec_gsensor_dev); + ec_gsensor_dev = NULL; + } +} + +#ifdef CONFIG_PM + +static int ft_laptop_suspend(struct device *dev) +{ + if (ft_gpio_device) + disable_irq_nosync(ft_gpio_device->irq); + it8528_ec_event_int_disable(); + return 0; +} + +static int ft_laptop_resume(struct device *dev) +{ + int ret; + + if (ft_gpio_device) { + /* Configure GPIO pin function */ + ret = set_func_to_gpio(); + if (ret) { + pr_err("set gpio func failed.\n"); + return -ENOMEM; + } + /*Set GPIO related configuration*/ + irq_set_irq_type(ft_gpio_device->irq, IRQ_TYPE_EDGE_RISING); + it8528_ec_event_int_enable(); + /* clean ec evnet queue*/ + ret = it8528_query_clean_event(); + if (ret) { + pr_err("EC clean event fail.\n"); + return -EINVAL; + } + } + + if (ft_gpio_device) + enable_irq(ft_gpio_device->irq); + /* enable ec event interrupt */ + it8528_ec_event_int_enable(); + + if (chassis_types_is_laptop()) { /* chassis is laptop */ + ft_power_info_power_status_update(); + if (power_info->bat_in) { + /* Get battery static information. */ + ft_power_info_battery_static_update(); + + /* Report status changed */ + power_supply_changed(ec_ac); + } + + input_report_switch(ft_hotkey_dev, SW_LID, 0); + input_sync(ft_hotkey_dev); + } + + return 0; +} +#endif + + +static int ft_lpc_event_probe(struct platform_device *dev) +{ + int ret = 0; + int gpio_irq; + int brightness_default; + + pr_debug("phytium ec: in probe!\n"); + + if (read_cpuid_implementor() != ARM_CPU_IMP_PHYTIUM) + return ret; + + ret = it8528_init(); + if (ret) { + pr_info("laptop_init: ec chip not found!\n"); + return ret; + } + + /* Regist backlight */ + if (chassis_types_is_laptop() || chassis_types_is_allinone()) { + ft_backlight_dev = devm_backlight_device_register(&dev->dev, "phytium", + &dev->dev, NULL, &ft_backlight_ops, NULL); + if (IS_ERR(ft_backlight_dev)) { + ret = PTR_ERR(ft_backlight_dev); + return ret; + } + ft_backlight_dev->props.max_brightness = 100; + + brightness_default = it8528_read(INDEX_DISPLAY_BRIGHTNESS_GET); + if (brightness_default == 0) { + ft_backlight_dev->props.brightness = ft_backlight_dev->props.max_brightness; + it8528_write(INDEX_DISPLAY_BRIGHTNESS_SET, + ft_backlight_dev->props.max_brightness); + } else { + ft_backlight_dev->props.brightness = brightness_default; + } + + backlight_update_status(ft_backlight_dev); + } + + ret = ft_hotkey_init(); + if (ret) { + pr_err("phytium Platform Driver: Hotkey init fail.\n"); + return ret; + } + + ret = ec_gsonsor_init(dev); + if (ret) { + pr_err("phytium Platform Driver: Gsensor init fail.\n"); + ft_hotkey_exit(); + return ret; + } + + /* Register power supply START */ + power_info = devm_kzalloc(&dev->dev, sizeof(struct ft_power_info), GFP_KERNEL); + if (!power_info) { + ret = -ENOMEM; + goto fail_after_gsensor; + } + + ft_power_battery_info_init(); + + ft_power_info_power_status_update(); + if (power_info->bat_in) { + /* Get battery static information. */ + ft_power_info_battery_static_update(); + } else { + pr_info("phytium Platform Driver: The battery does not exist!!\n"); + } + + if (chassis_types_is_laptop() && power_info->bat_in) { + ec_bat = devm_power_supply_register(&dev->dev, &ft_bat, NULL); + if (IS_ERR(ec_bat)) { + ret = -ENOMEM; + goto fail_after_gsensor; + } + + ec_ac = devm_power_supply_register(&dev->dev, &ft_ac, NULL); + if (IS_ERR(ec_ac)) { + ret = -ENOMEM; + goto fail_after_gsensor; + } + } + /* Register power supply END */ + + /* Register Temp and Fan hwmon */ + ec_sensors_register(dev); + + gpio_irq = acpi_dev_gpio_irq_get(ACPI_COMPANION(&(dev->dev)), 0); + if (gpio_irq > 0) { + /* GPIO Driver Init Start */ + ret = gpio_driver_init(gpio_irq); + if (ret) { + pr_err("phytium ec: gpio driver init fail.\n"); + goto fail_after_gsensor; + } + } else { + /* LPC Driver Init Start */ + ret = lpc_driver_init(); + if (ret) { + pr_err("phytium: LPC driver init fail.\n"); + goto fail_after_gsensor; + } + } + + /* clean ec evnet queue*/ + ret = it8528_query_clean_event(); + if (ret) { + pr_err("EC clean event fail.\n"); + goto fail_after_gsensor; + } + + /* enable ec event interrupt */ + it8528_ec_event_int_enable(); + + pr_debug("phytium: ec event driver init done...\n"); + + return 0; + +fail_after_gsensor: + ec_gsensor_exit(); + ft_hotkey_exit(); + return ret; +} + +module_platform_driver(ft_ec_pdriver); + +MODULE_DESCRIPTION("phytium Laptop Driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/classtypes.h b/include/linux/classtypes.h index 0e5eef81f2df..dd74fb4bcb66 100644 --- a/include/linux/classtypes.h +++ b/include/linux/classtypes.h @@ -2,6 +2,11 @@ #ifndef _LIB_CLASS_TYPE_H #define _LIB_CLASS_TYPE_H +#include + char *get_cpu_name(void); +unsigned long get_chassis_types(void); +bool chassis_types_is_laptop(void); +bool chassis_types_is_allinone(void); #endif diff --git a/lib/classtypes.c b/lib/classtypes.c index f209fdf2b2fa..5932f35e677a 100644 --- a/lib/classtypes.c +++ b/lib/classtypes.c @@ -3,6 +3,7 @@ #include #include #include +#include static char cpu_name[256]; static bool get_cpu_flag; @@ -60,3 +61,54 @@ char *get_cpu_name(void) return cpu_name; } EXPORT_SYMBOL(get_cpu_name); + +static unsigned long chassis_type; + +unsigned long get_chassis_types(void) +{ + const char *chassis_type_str = NULL; + + if (chassis_type) + return chassis_type; + + chassis_type_str = dmi_get_system_info(DMI_CHASSIS_TYPE); + if (!chassis_type_str) + return 0; + + if (kstrtoul(chassis_type_str, 10, &chassis_type) != 0) + return 0; + + return chassis_type; +} +EXPORT_SYMBOL(get_chassis_types); + +bool chassis_types_is_laptop(void) +{ + unsigned long type; + + type = get_chassis_types(); + + switch (type) { + case 0x09: /* Laptop */ + case 0x0A: /* Notebook */ + return true; + } + + return false; +} +EXPORT_SYMBOL(chassis_types_is_laptop); + +bool chassis_types_is_allinone(void) +{ + unsigned long type; + + type = get_chassis_types(); + + switch (type) { + case 0x0D: /* All in One */ + return true; + } + + return false; +} +EXPORT_SYMBOL(chassis_types_is_allinone);