From 9d62f1b8abb852a12a437b94ddc67c3294dd0953 Mon Sep 17 00:00:00 2001 From: Nick Xie Date: Mon, 23 Dec 2019 22:51:19 +0800 Subject: [PATCH 098/101] arm64: dts: VIMs: add simple MCU driver for FAN Signed-off-by: Nick Xie --- .../amlogic/meson-gxl-s905x-khadas-vim.dts | 10 + .../dts/amlogic/meson-gxm-khadas-vim2.dts | 10 + .../boot/dts/amlogic/meson-khadas-vim3.dtsi | 10 + drivers/hwmon/scpi-hwmon.c | 18 + drivers/misc/Kconfig | 5 + drivers/misc/Makefile | 1 + drivers/misc/khadas-mcu.c | 720 ++++++++++++++++++ drivers/thermal/amlogic_thermal.c | 19 + 8 files changed, 793 insertions(+) create mode 100644 drivers/misc/khadas-mcu.c diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-khadas-vim.dts b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-khadas-vim.dts index 0786ea55f839..02b6691768b3 100644 --- a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-khadas-vim.dts +++ b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-khadas-vim.dts @@ -155,6 +155,16 @@ clock-frequency = <32768>; clock-output-names = "xin32k"; }; + + khadas-mcu@18 { + status = "okay"; + compatible = "khadas-mcu"; + reg = <0x18>; + fan,trig_temp_level0 = <50>; + fan,trig_temp_level1 = <60>; + fan,trig_temp_level2 = <70>; + hwver = "VIM1.V12"; /* Will be updated in uboot. */ + }; }; &ir { diff --git a/arch/arm64/boot/dts/amlogic/meson-gxm-khadas-vim2.dts b/arch/arm64/boot/dts/amlogic/meson-gxm-khadas-vim2.dts index 57de06faa841..f9ec3f3efbe1 100644 --- a/arch/arm64/boot/dts/amlogic/meson-gxm-khadas-vim2.dts +++ b/arch/arm64/boot/dts/amlogic/meson-gxm-khadas-vim2.dts @@ -363,6 +363,16 @@ clock-frequency = <32768>; clock-output-names = "xin32k"; }; + + khadas-mcu@18 { + status = "okay"; + compatible = "khadas-mcu"; + reg = <0x18>; + fan,trig_temp_level0 = <50>; + fan,trig_temp_level1 = <60>; + fan,trig_temp_level2 = <70>; + hwver = "VIM2.V12"; /* Will be updated in uboot. */ + }; }; &ir { diff --git a/arch/arm64/boot/dts/amlogic/meson-khadas-vim3.dtsi b/arch/arm64/boot/dts/amlogic/meson-khadas-vim3.dtsi index 6d0163f56b0d..f6b5de8328ac 100644 --- a/arch/arm64/boot/dts/amlogic/meson-khadas-vim3.dtsi +++ b/arch/arm64/boot/dts/amlogic/meson-khadas-vim3.dtsi @@ -242,6 +242,16 @@ reg = <0x51>; #clock-cells = <0>; }; + + khadas-mcu@18 { + status = "okay"; + compatible = "khadas-mcu"; + reg = <0x18>; + fan,trig_temp_level0 = <50>; + fan,trig_temp_level1 = <60>; + fan,trig_temp_level2 = <70>; + hwver = "VIM3.V11"; /* Will be updated in uboot. */ + }; }; &ir { diff --git a/drivers/hwmon/scpi-hwmon.c b/drivers/hwmon/scpi-hwmon.c index 25aac40f2764..3dd7af04c5bd 100644 --- a/drivers/hwmon/scpi-hwmon.c +++ b/drivers/hwmon/scpi-hwmon.c @@ -54,6 +54,8 @@ static const u32 scpi_scale[] = { [ENERGY] = 1000000, /* (microjoules) */ }; +static struct scpi_thermal_zone *g_scpi_thermal_zone_ptr; + static void scpi_scale_reading(u64 *value, struct sensor_data *sensor) { if (scpi_scale[sensor->info.class] != sensor->scale) { @@ -81,6 +83,20 @@ static int scpi_read_temp(void *dev, int *temp) return 0; } +int meson_gx_get_temperature(void) +{ + int temp; + int ret; + ret = scpi_read_temp(g_scpi_thermal_zone_ptr, &temp); + if (ret) { + printk("scpi_read_temp() failed!\n"); + return ret; + } + + return temp / 1000; +} +EXPORT_SYMBOL(meson_gx_get_temperature); + /* hwmon callback functions */ static ssize_t scpi_show_sensor(struct device *dev, struct device_attribute *attr, char *buf) @@ -266,6 +282,8 @@ static int scpi_hwmon_probe(struct platform_device *pdev) if (!zone) return -ENOMEM; + g_scpi_thermal_zone_ptr = zone; + zone->sensor_id = i; zone->scpi_sensors = scpi_sensors; z = devm_thermal_zone_of_sensor_register(dev, diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 7f0d48f406e3..fb0a3830fd87 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -465,6 +465,11 @@ config PVPANIC a paravirtualized device provided by QEMU; it lets a virtual machine (guest) communicate panic events to the host. +config KHADAS_MCU + tristate "Khadas boards on-board MCU" + help + This driver provides support for Khadas boards on-board MCU. + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index c1860d35dc7e..9bbf2a479405 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -57,4 +57,5 @@ obj-y += cardreader/ obj-$(CONFIG_HABANA_AI) += habanalabs/ obj-$(CONFIG_UACCE) += uacce/ obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o +obj-$(CONFIG_KHADAS_MCU) += khadas-mcu.o obj-$(CONFIG_HISI_HIKEY_USB) += hisi_hikey_usb.o diff --git a/drivers/misc/khadas-mcu.c b/drivers/misc/khadas-mcu.c new file mode 100644 index 000000000000..7c6f2903b7a5 --- /dev/null +++ b/drivers/misc/khadas-mcu.c @@ -0,0 +1,720 @@ +/* + * Khadas MCU control driver + * + * Written by: Nick + * + * Copyright (c) 2019 Shenzhen Wesion Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* Device registers */ +#define MCU_BOOT_EN_WOL_REG 0x21 +#define MCU_CMD_FAN_STATUS_CTRL_REG 0x88 +#define MCU_USB_PCIE_SWITCH_REG 0x33 /* VIM3/VIM3L only */ +#define MCU_PWR_OFF_CMD_REG 0x80 +#define MCU_SHUTDOWN_NORMAL_REG 0x2c + +#define MCU_FAN_TRIG_TEMP_LEVEL0 50 // 50 degree if not set +#define MCU_FAN_TRIG_TEMP_LEVEL1 60 // 60 degree if not set +#define MCU_FAN_TRIG_TEMP_LEVEL2 70 // 70 degree if not set +#define MCU_FAN_TRIG_MAXTEMP 80 +#define MCU_FAN_LOOP_SECS (30 * HZ) // 30 seconds +#define MCU_FAN_TEST_LOOP_SECS (5 * HZ) // 5 seconds +#define MCU_FAN_LOOP_NODELAY_SECS 0 +#define MCU_FAN_SPEED_OFF 0 +#define MCU_FAN_SPEED_LOW 1 +#define MCU_FAN_SPEED_MID 2 +#define MCU_FAN_SPEED_HIGH 3 + +enum mcu_fan_mode { + MCU_FAN_MODE_MANUAL = 0, + MCU_FAN_MODE_AUTO, +}; + +enum mcu_fan_level { + MCU_FAN_LEVEL_0 = 0, + MCU_FAN_LEVEL_1, + MCU_FAN_LEVEL_2, + MCU_FAN_LEVEL_3, +}; + +enum mcu_fan_status { + MCU_FAN_STATUS_DISABLE = 0, + MCU_FAN_STATUS_ENABLE, +}; + +enum mcu_usb_pcie_switch_mode { + MCU_USB_PCIE_SWITCH_MODE_USB3 = 0, + MCU_USB_PCIE_SWITCH_MODE_PCIE +}; + +enum khadas_board_hwver { + KHADAS_BOARD_HWVER_NONE = 0, + KHADAS_BOARD_HWVER_V11, + KHADAS_BOARD_HWVER_V12, + KHADAS_BOARD_HWVER_V13, + KHADAS_BOARD_HWVER_V14 +}; + +enum khadas_board { + KHADAS_BOARD_NONE, + KHADAS_BOARD_VIM1, + KHADAS_BOARD_VIM2, + KHADAS_BOARD_VIM3 +}; + +struct mcu_fan_data { + struct platform_device *pdev; + struct class *fan_class; + struct delayed_work work; + struct delayed_work fan_test_work; + enum mcu_fan_status enable; + enum mcu_fan_mode mode; + enum mcu_fan_level level; + int trig_temp_level0; + int trig_temp_level1; + int trig_temp_level2; +}; + +struct mcu_data { + struct i2c_client *client; + struct class *usb_pcie_switch_class; + struct class *mcu_class; + u8 usb_pcie_switch_mode; + enum khadas_board board; + enum khadas_board_hwver hwver; + struct mcu_fan_data fan_data; +}; + +struct mcu_data *g_mcu_data; + +static char * mcu_board_type_to_str(enum khadas_board board) +{ + switch (board) { + case KHADAS_BOARD_NONE: + return "Unknown"; + case KHADAS_BOARD_VIM1: + return "VIM1"; + case KHADAS_BOARD_VIM2: + return "VIM2"; + case KHADAS_BOARD_VIM3: + return "VIM3"; + default: + return "Unknown"; + } +} + +static char * mcu_board_hardware_version_str(enum khadas_board_hwver hwver) +{ + switch (hwver) { + case KHADAS_BOARD_HWVER_NONE: + return "Unknown"; + case KHADAS_BOARD_HWVER_V11: + return "V11"; + case KHADAS_BOARD_HWVER_V12: + return "V12"; + case KHADAS_BOARD_HWVER_V13: + return "V13"; + case KHADAS_BOARD_HWVER_V14: + return "V14"; + default: + return "Unknown"; + } +} + +static int i2c_master_reg8_send(const struct i2c_client *client, + const char reg, const char *buf, int count) +{ + struct i2c_adapter *adap = client->adapter; + struct i2c_msg msg; + int ret; + char *tx_buf = kzalloc(count + 1, GFP_KERNEL); + if (!tx_buf) + return -ENOMEM; + tx_buf[0] = reg; + memcpy(tx_buf+1, buf, count); + + msg.addr = client->addr; + msg.flags = client->flags; + msg.len = count + 1; + msg.buf = (char *)tx_buf; + + ret = i2c_transfer(adap, &msg, 1); + kfree(tx_buf); + return (ret == 1) ? count : ret; +} + +static int i2c_master_reg8_recv(const struct i2c_client *client, + const char reg, char *buf, int count) +{ + struct i2c_adapter *adap = client->adapter; + struct i2c_msg msgs[2]; + int ret; + char reg_buf = reg; + + msgs[0].addr = client->addr; + msgs[0].flags = client->flags; + msgs[0].len = 1; + msgs[0].buf = ®_buf; + + msgs[1].addr = client->addr; + msgs[1].flags = client->flags | I2C_M_RD; + msgs[1].len = count; + msgs[1].buf = (char *)buf; + + ret = i2c_transfer(adap, msgs, 2); + + return (ret == 2) ? count : ret; +} + +static int mcu_i2c_read_regs(struct i2c_client *client, + u8 reg, u8 buf[], unsigned len) +{ + int ret; + ret = i2c_master_reg8_recv(client, reg, buf, len); + return ret; +} + +static int mcu_i2c_write_regs(struct i2c_client *client, + u8 reg, u8 const buf[], __u16 len) +{ + int ret; + ret = i2c_master_reg8_send(client, reg, buf, (int)len); + return ret; +} + +static int is_mcu_fan_control_supported(void) +{ + // MCU FAN control is supported for: + // 1. Khadas VIM1 V13 and later + // 2. Khadas VIM2 V13 and later + // 3. Khadas VIM3 V11 and later + if (KHADAS_BOARD_VIM1 == g_mcu_data->board) { + if (g_mcu_data->hwver >= KHADAS_BOARD_HWVER_V13) + return 1; + else + return 0; + } else if (KHADAS_BOARD_VIM2 == g_mcu_data->board) { + if (g_mcu_data->hwver > KHADAS_BOARD_HWVER_V12) + return 1; + else + return 0; + } else if (KHADAS_BOARD_VIM3 == g_mcu_data->board) { + if (g_mcu_data->hwver >= KHADAS_BOARD_HWVER_V11) + return 1; + else + return 0; + } else + return 0; +} + +static bool is_mcu_usb_pcie_switch_supported(void) +{ + // MCU USB PCIe switch is supported for: + // 1. Khadas VIM3 + if (KHADAS_BOARD_VIM3 == g_mcu_data->board) + return 1; + else + return 0; +} + +static void mcu_fan_level_set(struct mcu_fan_data *fan_data, int level) +{ + if (is_mcu_fan_control_supported()) { + int ret; + u8 data[2] = {0}; + + if(0 == level) { + data[0] = MCU_FAN_SPEED_OFF; + }else if(1 == level){ + data[0] = MCU_FAN_SPEED_LOW; + }else if(2 == level){ + data[0] = MCU_FAN_SPEED_MID; + }else if(3 == level){ + data[0] = MCU_FAN_SPEED_HIGH; + } + + g_mcu_data->fan_data.level = data[0]; + + ret = mcu_i2c_write_regs(g_mcu_data->client, MCU_CMD_FAN_STATUS_CTRL_REG, data, 1); + if (ret < 0) { + printk("write fan control err\n"); + return; + } + } +} + +extern int meson_gx_get_temperature(void); +extern int meson_g12_get_temperature(void); +static void fan_work_func(struct work_struct *_work) +{ + if (is_mcu_fan_control_supported()) { + int temp = -EINVAL; + struct mcu_fan_data *fan_data = &g_mcu_data->fan_data; + + if ((KHADAS_BOARD_VIM1 == g_mcu_data->board) || + (KHADAS_BOARD_VIM2 == g_mcu_data->board)) + temp = meson_gx_get_temperature(); + else if (KHADAS_BOARD_VIM3 == g_mcu_data->board) + temp = meson_g12_get_temperature(); + + if(temp != -EINVAL){ + if(temp < fan_data->trig_temp_level0 ) { + mcu_fan_level_set(fan_data, 0); + }else if(temp < fan_data->trig_temp_level1 ) { + mcu_fan_level_set(fan_data, 1); + }else if(temp < fan_data->trig_temp_level2 ) { + mcu_fan_level_set(fan_data, 2); + }else{ + mcu_fan_level_set(fan_data, 3); + } + } + + schedule_delayed_work(&fan_data->work, MCU_FAN_LOOP_SECS); + } +} + +static void khadas_fan_set(struct mcu_fan_data *fan_data) +{ + if (is_mcu_fan_control_supported()) { + + cancel_delayed_work(&fan_data->work); + + if (fan_data->enable == MCU_FAN_STATUS_DISABLE) { + mcu_fan_level_set(fan_data, 0); + return; + } + switch (fan_data->mode) { + case MCU_FAN_MODE_MANUAL: + switch(fan_data->level) { + case MCU_FAN_LEVEL_0: + mcu_fan_level_set(fan_data, 0); + break; + case MCU_FAN_LEVEL_1: + mcu_fan_level_set(fan_data, 1); + break; + case MCU_FAN_LEVEL_2: + mcu_fan_level_set(fan_data, 2); + break; + case MCU_FAN_LEVEL_3: + mcu_fan_level_set(fan_data, 3); + break; + default: + break; + } + break; + + case MCU_FAN_MODE_AUTO: + // FIXME: achieve with a better way + schedule_delayed_work(&fan_data->work, MCU_FAN_LOOP_NODELAY_SECS); + break; + + default: + break; + } + } +} + +static ssize_t show_fan_enable(struct class *cls, + struct class_attribute *attr, char *buf) +{ + return sprintf(buf, "Fan enable: %d\n", g_mcu_data->fan_data.enable); +} + +static ssize_t store_fan_enable(struct class *cls, struct class_attribute *attr, + const char *buf, size_t count) +{ + int enable; + + if (kstrtoint(buf, 0, &enable)) + return -EINVAL; + + // 0: manual, 1: auto + if( enable >= 0 && enable < 2 ){ + g_mcu_data->fan_data.enable = enable; + khadas_fan_set(&g_mcu_data->fan_data); + } + + return count; +} + +static ssize_t show_fan_mode(struct class *cls, + struct class_attribute *attr, char *buf) +{ + return sprintf(buf, "Fan mode: %d\n", g_mcu_data->fan_data.mode); +} + +static ssize_t store_fan_mode(struct class *cls, struct class_attribute *attr, + const char *buf, size_t count) +{ + int mode; + + if (kstrtoint(buf, 0, &mode)) + return -EINVAL; + + // 0: manual, 1: auto + if( mode >= 0 && mode < 2 ){ + g_mcu_data->fan_data.mode = mode; + khadas_fan_set(&g_mcu_data->fan_data); + } + + return count; +} + +static ssize_t show_fan_level(struct class *cls, + struct class_attribute *attr, char *buf) +{ + return sprintf(buf, "Fan level: %d\n", g_mcu_data->fan_data.level); +} + +static ssize_t store_fan_level(struct class *cls, struct class_attribute *attr, + const char *buf, size_t count) +{ + int level; + + if (kstrtoint(buf, 0, &level)) + return -EINVAL; + + if( level >= 0 && level < 4){ + g_mcu_data->fan_data.level = level; + khadas_fan_set(&g_mcu_data->fan_data); + } + + return count; +} + +static ssize_t show_fan_temp(struct class *cls, + struct class_attribute *attr, char *buf) +{ + int temp = -EINVAL; + + if ((KHADAS_BOARD_VIM1 == g_mcu_data->board) || + (KHADAS_BOARD_VIM2 == g_mcu_data->board)) + temp = meson_gx_get_temperature(); + else if (KHADAS_BOARD_VIM3 == g_mcu_data->board) + temp = meson_g12_get_temperature(); + + return sprintf(buf, "cpu_temp:%d\nFan trigger temperature: level0:%d level1:%d level2:%d\n", temp, g_mcu_data->fan_data.trig_temp_level0, g_mcu_data->fan_data.trig_temp_level1, g_mcu_data->fan_data.trig_temp_level2); +} + +static ssize_t store_usb_pcie_switch_mode(struct class *cls, struct class_attribute *attr, + const char *buf, size_t count) +{ + int ret; + int mode; + + if (kstrtoint(buf, 0, &mode)) + return -EINVAL; + + if (0 != mode && 1 != mode) + return -EINVAL; + + if ((mode < MCU_USB_PCIE_SWITCH_MODE_USB3) || (mode > MCU_USB_PCIE_SWITCH_MODE_PCIE)) + return -EINVAL; + + g_mcu_data->usb_pcie_switch_mode = (u8)mode; + ret = mcu_i2c_write_regs(g_mcu_data->client, MCU_USB_PCIE_SWITCH_REG, &g_mcu_data->usb_pcie_switch_mode, 1); + if (ret < 0) { + printk("write USB PCIe switch error\n"); + + return ret; + } + + printk("Set USB PCIe Switch Mode: %s\n", g_mcu_data->usb_pcie_switch_mode ? "PCIe" : "USB3.0"); + + return count; +} + +static ssize_t show_usb_pcie_switch_mode(struct class *cls, + struct class_attribute *attr, char *buf) +{ + printk("USB PCIe Switch Mode: %s\n", g_mcu_data->usb_pcie_switch_mode ? "PCIe" : "USB3.0"); + return sprintf(buf, "%d\n", g_mcu_data->usb_pcie_switch_mode); +} + +static ssize_t store_mcu_poweroff(struct class *cls, struct class_attribute *attr, + const char *buf, size_t count) +{ + int ret; + int val; + u8 reg; + + if (kstrtoint(buf, 0, &val)) + return -EINVAL; + + if (1 != val) + return -EINVAL; + + reg = (u8)val; + ret = mcu_i2c_write_regs(g_mcu_data->client, MCU_PWR_OFF_CMD_REG, ®, 1); + if (ret < 0) { + printk("write poweroff cmd error\n"); + + return ret; + } + + return count; +} + +static ssize_t store_mcu_rst(struct class *cls, struct class_attribute *attr, + const char *buf, size_t count) +{ + u8 reg[2]; + int ret; + int rst; + + if (kstrtoint(buf, 0, &rst)) + return -EINVAL; + + reg[0] = rst; + ret = mcu_i2c_write_regs(g_mcu_data->client, MCU_SHUTDOWN_NORMAL_REG, reg, 1); + if (ret < 0) + printk("rst mcu err\n"); + + return count; +} + +static struct class_attribute fan_class_attrs[] = { + __ATTR(enable, 0644, show_fan_enable, store_fan_enable), + __ATTR(mode, 0644, show_fan_mode, store_fan_mode), + __ATTR(level, 0644, show_fan_level, store_fan_level), + __ATTR(temp, 0644, show_fan_temp, NULL), +}; + +static struct class_attribute mcu_class_attrs[] = { + __ATTR(poweroff, 0644, NULL, store_mcu_poweroff), + __ATTR(usb_pcie_switch_mode, 0644, show_usb_pcie_switch_mode, store_usb_pcie_switch_mode), + __ATTR(rst, 0644, NULL, store_mcu_rst), +}; + +static void create_mcu_attrs(void) +{ + int i; + printk("%s\n",__func__); + + g_mcu_data->mcu_class = class_create(THIS_MODULE, "mcu"); + if (IS_ERR(g_mcu_data->mcu_class)) { + pr_err("create mcu_class debug class fail\n"); + return; + } + + for (i = 0; i < ARRAY_SIZE(mcu_class_attrs); i++) { + if (strstr(mcu_class_attrs[i].attr.name, "usb_pcie_switch_mode")) { + if (!is_mcu_usb_pcie_switch_supported()) + continue; + } + if (class_create_file(g_mcu_data->mcu_class, &mcu_class_attrs[i])) + pr_err("create mcu attribute %s fail\n", mcu_class_attrs[i].attr.name); + } + + if (is_mcu_fan_control_supported()) { + g_mcu_data->fan_data.fan_class = class_create(THIS_MODULE, "fan"); + if (IS_ERR(g_mcu_data->fan_data.fan_class)) { + pr_err("create fan_class debug class fail\n"); + return; + } + + for (i=0; ifan_data.fan_class, &fan_class_attrs[i])) + pr_err("create fan attribute %s fail\n", fan_class_attrs[i].attr.name); + } + } +} + +static int mcu_parse_dt(struct device *dev) +{ + int ret; + const char *hwver = NULL; + + if (NULL == dev) return -EINVAL; + + // Get hardwere version + ret = of_property_read_string(dev->of_node, "hwver", &hwver); + if (ret < 0) { + g_mcu_data->hwver = KHADAS_BOARD_HWVER_V12; + g_mcu_data->board = KHADAS_BOARD_VIM2; + } else { + if (strstr(hwver, "VIM1")) + g_mcu_data->board = KHADAS_BOARD_VIM1; + else if (strstr(hwver, "VIM2")) + g_mcu_data->board = KHADAS_BOARD_VIM2; + else if (strstr(hwver, "VIM3")) + g_mcu_data->board = KHADAS_BOARD_VIM3; + else + g_mcu_data->board = KHADAS_BOARD_NONE; + + if (KHADAS_BOARD_VIM1 == g_mcu_data->board) { + if (0 == strcmp(hwver, "VIM1.V13")) { + g_mcu_data->hwver = KHADAS_BOARD_HWVER_V13; + } else if (0 == strcmp(hwver, "VIM1.V14")) { + g_mcu_data->hwver = KHADAS_BOARD_HWVER_V14; + } else { + g_mcu_data->hwver = KHADAS_BOARD_HWVER_NONE; + } + } else if (KHADAS_BOARD_VIM2 == g_mcu_data->board) { + if (0 == strcmp(hwver, "VIM2.V12")) { + g_mcu_data->hwver = KHADAS_BOARD_HWVER_V12; + } else if (0 == strcmp(hwver, "VIM2.V13")) { + g_mcu_data->hwver = KHADAS_BOARD_HWVER_V13; + } else if (0 == strcmp(hwver, "VIM2.V14")) { + g_mcu_data->hwver = KHADAS_BOARD_HWVER_V14; + } else { + g_mcu_data->hwver = KHADAS_BOARD_HWVER_NONE; + } + } else if (KHADAS_BOARD_VIM3 == g_mcu_data->board) { + if (0 == strcmp(hwver, "VIM3.V11")) { + g_mcu_data->hwver = KHADAS_BOARD_HWVER_V11; + } else if (0 == strcmp(hwver, "VIM3.V12")) { + g_mcu_data->hwver = KHADAS_BOARD_HWVER_V12; + } else if (0 == strcmp(hwver, "VIM3.V13")) { + g_mcu_data->hwver = KHADAS_BOARD_HWVER_V13; + } else if (0 == strcmp(hwver, "VIM3.V14")) { + g_mcu_data->hwver = KHADAS_BOARD_HWVER_V14; + } else { + g_mcu_data->hwver = KHADAS_BOARD_HWVER_NONE; + } + } + } + + ret = of_property_read_u32(dev->of_node, "fan,trig_temp_level0", &g_mcu_data->fan_data.trig_temp_level0); + if (ret < 0) + g_mcu_data->fan_data.trig_temp_level0 = MCU_FAN_TRIG_TEMP_LEVEL0; + ret = of_property_read_u32(dev->of_node, "fan,trig_temp_level1", &g_mcu_data->fan_data.trig_temp_level1); + if (ret < 0) + g_mcu_data->fan_data.trig_temp_level1 = MCU_FAN_TRIG_TEMP_LEVEL1; + ret = of_property_read_u32(dev->of_node, "fan,trig_temp_level2", &g_mcu_data->fan_data.trig_temp_level2); + if (ret < 0) + g_mcu_data->fan_data.trig_temp_level2 = MCU_FAN_TRIG_TEMP_LEVEL2; + + return ret; +} + +static int mcu_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + u8 reg[2]; + int ret; + + printk("%s\n", __func__); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + g_mcu_data = kzalloc(sizeof(struct mcu_data), GFP_KERNEL); + + if (g_mcu_data == NULL) + return -ENOMEM; + + mcu_parse_dt(&client->dev); + + printk("%s: board: %s (%d), hwver: %s (%d)\n", __func__, + mcu_board_type_to_str(g_mcu_data->board), + (int)g_mcu_data->board, + mcu_board_hardware_version_str(g_mcu_data->hwver), + (int)g_mcu_data->hwver); + + g_mcu_data->client = client; + + if (is_mcu_usb_pcie_switch_supported()) { + // Get USB PCIe Switch status + ret = mcu_i2c_read_regs(client, MCU_USB_PCIE_SWITCH_REG, reg, 1); + if (ret < 0) + goto exit; + g_mcu_data->usb_pcie_switch_mode = (u8)reg[0]; + } + + if (is_mcu_fan_control_supported()) { + g_mcu_data->fan_data.mode = MCU_FAN_MODE_AUTO; + g_mcu_data->fan_data.level = MCU_FAN_LEVEL_0; + g_mcu_data->fan_data.enable = MCU_FAN_STATUS_DISABLE; + + INIT_DELAYED_WORK(&g_mcu_data->fan_data.work, fan_work_func); + mcu_fan_level_set(&g_mcu_data->fan_data, 0); + } + create_mcu_attrs(); + + return 0; +exit: + return ret; +} + + +static int mcu_remove(struct i2c_client *client) +{ + return 0; +} + +static void khadas_fan_shutdown(struct i2c_client *client) +{ + g_mcu_data->fan_data.enable = MCU_FAN_STATUS_DISABLE; + khadas_fan_set(&g_mcu_data->fan_data); +} + +#ifdef CONFIG_PM_SLEEP +static int khadas_fan_suspend(struct device *dev) +{ + cancel_delayed_work(&g_mcu_data->fan_data.work); + mcu_fan_level_set(&g_mcu_data->fan_data, 0); + + return 0; +} + +static int khadas_fan_resume(struct device *dev) +{ + return 0; +} + +static const struct dev_pm_ops fan_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(khadas_fan_suspend, khadas_fan_resume) +}; + +#define FAN_PM_OPS (&(fan_dev_pm_ops)) + +#endif + +static const struct i2c_device_id mcu_id[] = { + { "khadas-mcu", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mcu_id); + + +static struct of_device_id mcu_dt_ids[] = { + { .compatible = "khadas-mcu" }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, mcu_dt_ids); + +struct i2c_driver mcu_driver = { + .driver = { + .name = "khadas-mcu", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(mcu_dt_ids), +#ifdef CONFIG_PM_SLEEP + .pm = FAN_PM_OPS, +#endif + }, + .probe = mcu_probe, + .remove = mcu_remove, + .shutdown = khadas_fan_shutdown, + .id_table = mcu_id, +}; +module_i2c_driver(mcu_driver); + +MODULE_AUTHOR("Nick "); +MODULE_DESCRIPTION("Khadas MCU control driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/thermal/amlogic_thermal.c b/drivers/thermal/amlogic_thermal.c index ccb1fe18e993..3947f3c1f91f 100644 --- a/drivers/thermal/amlogic_thermal.c +++ b/drivers/thermal/amlogic_thermal.c @@ -104,6 +104,8 @@ struct amlogic_thermal { u32 trim_info; }; +static struct amlogic_thermal *amlogic_thermal_data_ptr; + /* * Calculate a temperature value from a temperature code. * The unit of the temperature is degree milliCelsius. @@ -194,6 +196,21 @@ static int amlogic_thermal_get_temp(void *data, int *temp) return 0; } +int meson_g12_get_temperature(void) +{ + int temp; + int ret; + + ret = amlogic_thermal_get_temp(amlogic_thermal_data_ptr, &temp); + if (ret) { + printk("amlogic_thermal_get_temp() failed!\n"); + return ret; + } + + return temp / 1000; +} +EXPORT_SYMBOL(meson_g12_get_temperature); + static const struct thermal_zone_of_device_ops amlogic_thermal_ops = { .get_temp = amlogic_thermal_get_temp, }; @@ -248,6 +265,8 @@ static int amlogic_thermal_probe(struct platform_device *pdev) if (!pdata) return -ENOMEM; + amlogic_thermal_data_ptr = pdata; + pdata->data = of_device_get_match_data(dev); pdata->pdev = pdev; platform_set_drvdata(pdev, pdata); -- 2.17.1