Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions MAINTAINERS
Original file line number Diff line number Diff line change
Expand Up @@ -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 <giovanni.cabiddu@intel.com>
Expand Down
3 changes: 3 additions & 0 deletions arch/arm64/configs/deepin_arm64_desktop_defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions drivers/acpi/ec.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions drivers/platform/Kconfig
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
2 changes: 2 additions & 0 deletions drivers/platform/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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/
Expand Down
36 changes: 36 additions & 0 deletions drivers/platform/arm64/Kconfig
Original file line number Diff line number Diff line change
@@ -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
3 changes: 3 additions & 0 deletions drivers/platform/arm64/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# SPDX-License-Identifier: GPL-2.0

obj-$(CONFIG_ARM64_PLATFORM_DEVICES) += phytium/
7 changes: 7 additions & 0 deletions drivers/platform/arm64/phytium/Makefile
Original file line number Diff line number Diff line change
@@ -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
280 changes: 280 additions & 0 deletions drivers/platform/arm64/phytium/ec_it8528.c
Original file line number Diff line number Diff line change
@@ -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 <linux/module.h>
#include <linux/spinlock.h>
#include <linux/delay.h>
#include <linux/io.h>
#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);
Loading