From: Hauke Mehrtens Date: Sat, 28 Apr 2018 15:26:54 +0000 (+0200) Subject: Revert "sunxi: Add OPP table, cpu voltage control, cpu thermal sensor for Xunlong... X-Git-Url: http://git.lede-project.org./?a=commitdiff_plain;h=4861ccf9a2790cc8a79b6d74640b9b03fe1f5dcb;p=openwrt%2Fstaging%2Fhauke.git Revert "sunxi: Add OPP table, cpu voltage control, cpu thermal sensor for Xunlong Orange Pi PC" This reverts commit 139d0a6cb37bca7ccc5584468e884e452181e431. --- diff --git a/target/linux/sunxi/cortexa7/config-default b/target/linux/sunxi/cortexa7/config-default index e49607ec6e..cf41ddb4e6 100644 --- a/target/linux/sunxi/cortexa7/config-default +++ b/target/linux/sunxi/cortexa7/config-default @@ -8,5 +8,3 @@ CONFIG_DWMAC_SUN8I=y CONFIG_MDIO_BUS_MUX=y # CONFIG_PINCTRL_SUN5I is not set # CONFIG_SUN4I_A10_CCU is not set -CONFIG_REGULATOR_SY8106A=y -CONFIG_SUN8I_THS=y diff --git a/target/linux/sunxi/patches-4.14/050-sun8i-H3-Orange-Pi-PC-cpufreq-thermal.patch b/target/linux/sunxi/patches-4.14/050-sun8i-H3-Orange-Pi-PC-cpufreq-thermal.patch deleted file mode 100644 index d5dcf223b0..0000000000 --- a/target/linux/sunxi/patches-4.14/050-sun8i-H3-Orange-Pi-PC-cpufreq-thermal.patch +++ /dev/null @@ -1,876 +0,0 @@ ---- a/arch/arm/boot/dts/sunxi-h3-h5.dtsi -+++ b/arch/arm/boot/dts/sunxi-h3-h5.dtsi -@@ -383,6 +383,16 @@ - }; - }; - -+ ths: thermal-sensor@1c25000 { -+ reg = <0x01c25000 0x400>, <0x01c14234 0x4>; -+ reg-names = "ths", "calibration"; -+ interrupts = ; -+ clocks = <&ccu CLK_BUS_THS>, <&ccu CLK_THS>; -+ clock-names = "ahb", "ths"; -+ resets = <&ccu RST_BUS_THS>; -+ reset-names = "ahb"; -+ }; -+ - timer@01c20c00 { - compatible = "allwinner,sun4i-a10-timer"; - reg = <0x01c20c00 0xa0>; -@@ -632,6 +642,20 @@ - #reset-cells = <1>; - }; - -+ r_i2c: i2c@1f02400 { -+ compatible = "allwinner,sun6i-a31-i2c"; -+ reg = <0x01f02400 0x400>; -+ interrupts = ; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&r_i2c_pins>; -+ clocks = <&r_ccu CLK_APB0_I2C>; -+ clock-frequency = <100000>; -+ resets = <&r_ccu RST_APB0_I2C>; -+ status = "disabled"; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ }; -+ - codec_analog: codec-analog@01f015c0 { - compatible = "allwinner,sun8i-h3-codec-analog"; - reg = <0x01f015c0 0x4>; -@@ -662,6 +686,11 @@ - pins = "PL11"; - function = "s_cir_rx"; - }; -+ -+ r_i2c_pins: r-i2c { -+ pins = "PL0", "PL1"; -+ function = "s_i2c"; -+ }; - }; - }; - }; - ---- a/arch/arm/boot/dts/sun8i-h3.dtsi -+++ b/arch/arm/boot/dts/sun8i-h3.dtsi -@@ -43,34 +43,95 @@ - #include "sunxi-h3-h5.dtsi" - - / { -+ cpu0_opp_table: opp_table0 { -+ compatible = "operating-points-v2"; -+ opp-shared; -+ -+ opp@480000000 { -+ opp-hz = /bits/ 64 <480000000>; -+ opp-microvolt = <1040000 1040000 1100000>; -+ clock-latency-ns = <244144>; /* 8 32k periods */ -+ opp-suspend; -+ }; -+ -+ opp@648000000 { -+ opp-hz = /bits/ 64 <648000000>; -+ opp-microvolt = <1040000 1040000 1300000>; -+ clock-latency-ns = <244144>; /* 8 32k periods */ -+ }; -+ -+ opp@816000000 { -+ opp-hz = /bits/ 64 <816000000>; -+ opp-microvolt = <1100000 1100000 1300000>; -+ clock-latency-ns = <244144>; /* 8 32k periods */ -+ }; -+ -+ opp@1008000000 { -+ opp-hz = /bits/ 64 <1008000000>; -+ opp-microvolt = <1140000 1140000 1300000>; -+ clock-latency-ns = <244144>; /* 8 32k periods */ -+ }; -+ -+ opp@1200000000 { -+ opp-hz = /bits/ 64 <1200000000>; -+ opp-microvolt = <1200000 1200000 1300000>; -+ clock-latency-ns = <244144>; /* 8 32k periods */ -+ }; -+ }; -+ - cpus { - #address-cells = <1>; - #size-cells = <0>; - -- cpu@0 { -+ cpu0: cpu@0 { - compatible = "arm,cortex-a7"; - device_type = "cpu"; - reg = <0>; -+ clocks = <&ccu CLK_CPUX>; -+ clock-names = "cpu"; -+ operating-points-v2 = <&cpu0_opp_table>; -+ clock-frequency = <1200000000>; -+ #cooling-cells = <0x2>; - }; - - cpu@1 { - compatible = "arm,cortex-a7"; - device_type = "cpu"; - reg = <1>; -+ operating-points-v2 = <&cpu0_opp_table>; -+ clock-frequency = <1200000000>; - }; - - cpu@2 { - compatible = "arm,cortex-a7"; - device_type = "cpu"; - reg = <2>; -+ operating-points-v2 = <&cpu0_opp_table>; -+ clock-frequency = <1200000000>; - }; - - cpu@3 { - compatible = "arm,cortex-a7"; - device_type = "cpu"; - reg = <3>; -+ operating-points-v2 = <&cpu0_opp_table>; -+ clock-frequency = <1200000000>; - }; - }; -+ -+ iio-hwmon { -+ compatible = "iio-hwmon"; -+ io-channels = <&ths>; -+ }; -+ -+ thermal-zones { -+ cpu_thermal: cpu-thermal { -+ /* milliseconds */ -+ polling-delay-passive = <250>; -+ polling-delay = <1000>; -+ thermal-sensors = <&ths 0>; -+ }; -+ }; - - timer { - compatible = "arm,armv7-timer"; -@@ -121,6 +177,12 @@ - "sample"; - }; - -+&ths { -+ compatible = "allwinner,sun8i-h3-ths"; -+ #thermal-sensor-cells = <0>; -+ #io-channel-cells = <0>; -+}; -+ - &pio { - compatible = "allwinner,sun8i-h3-pinctrl"; - }; - ---- a/arch/arm/boot/dts/sun8i-h3-orangepi-pc.dts -+++ b/arch/arm/boot/dts/sun8i-h3-orangepi-pc.dts -@@ -42,10 +42,10 @@ - - /dts-v1/; - #include "sun8i-h3.dtsi" --#include "sunxi-common-regulators.dtsi" - - #include - #include -+#include - - / { - model = "Xunlong Orange Pi PC"; -@@ -88,6 +88,109 @@ - gpios = <&r_pio 0 3 GPIO_ACTIVE_LOW>; - }; - }; -+ -+ reg_vcc3v3: vcc3v3 { -+ compatible = "regulator-fixed"; -+ regulator-name = "vcc3v3"; -+ regulator-min-microvolt = <3300000>; -+ regulator-max-microvolt = <3300000>; -+ }; -+ -+ reg_usb0_vbus: usb0-vbus { -+ compatible = "regulator-fixed"; -+ regulator-name = "usb0-vbus"; -+ regulator-min-microvolt = <5000000>; -+ regulator-max-microvolt = <5000000>; -+ enable-active-high; -+ gpio = <&r_pio 0 2 GPIO_ACTIVE_HIGH>; /* PL2 */ -+ status = "okay"; -+ }; -+}; -+ -+&cpu0_opp_table { -+ 1368000000 { -+ opp-hz = /bits/ 64 <1368000000>; -+ opp-microvolt = <1340000 1340000 1400000>; -+ clock-latency-ns = <244144>; /* 8 32k periods */ -+ }; -+ -+ 1440000000 { -+ opp-hz = /bits/ 64 <1440000000>; -+ opp-microvolt = <1400000 1400000 1400000>; -+ clock-latency-ns = <244144>; /* 8 32k periods */ -+ }; -+ -+ 1512000000 { -+ opp-hz = /bits/ 64 <1512000000>; -+ opp-microvolt = <1400000 1400000 1400000>; -+ clock-latency-ns = <244144>; /* 8 32k periods */ -+ }; -+}; -+ -+&cpu0 { -+ cooling-min-level = <0>; -+ cooling-max-level = <15>; -+ cpu-supply = <®_sy8106a>; -+}; -+ -+&cpu_thermal { -+ trips { -+ cpu_warm: cpu_warm { -+ temperature = <65000>; -+ hysteresis = <2000>; -+ type = "passive"; -+ }; -+ -+ cpu_hot: cpu_hot { -+ temperature = <75000>; -+ hysteresis = <2000>; -+ type = "passive"; -+ }; -+ -+ cpu_very_hot: cpu_very_hot { -+ temperature = <90000>; -+ hysteresis = <2000>; -+ type = "passive"; -+ }; -+ -+ cpu_crit: cpu_crit { -+ temperature = <105000>; -+ hysteresis = <2000>; -+ type = "critical"; -+ }; -+ }; -+ -+ cooling-maps { -+ cpu_warm_limit_cpu { -+ trip = <&cpu_warm>; -+ cooling-device = <&cpu0 THERMAL_NO_LIMIT 10>; -+ }; -+ -+ cpu_hot_limit_cpu { -+ trip = <&cpu_hot>; -+ cooling-device = <&cpu0 12 12>; -+ }; -+ -+ cpu_very_hot_limit_cpu { -+ trip = <&cpu_very_hot>; -+ cooling-device = <&cpu0 14 THERMAL_NO_LIMIT>; -+ }; -+ }; -+}; -+ -+&r_i2c { -+ status = "okay"; -+ -+ reg_sy8106a: regulator@65 { -+ compatible = "silergy,sy8106a"; -+ reg = <0x65>; -+ regulator-name = "vdd-cpux"; -+ regulator-min-microvolt = <1000000>; -+ regulator-max-microvolt = <1400000>; -+ regulator-ramp-delay = <200>; -+ regulator-boot-on; -+ regulator-always-on; -+ }; - }; - - &codec { - ---- a/arch/arm/configs/sunxi_defconfig -+++ b/arch/arm/configs/sunxi_defconfig -@@ -40,6 +40,7 @@ - CONFIG_AHCI_SUNXI=y - CONFIG_NETDEVICES=y - CONFIG_SUN4I_EMAC=y -+CONFIG_SUN8I_THS=y - # CONFIG_NET_VENDOR_ARC is not set - # CONFIG_NET_CADENCE is not set - # CONFIG_NET_VENDOR_BROADCOM is not set -@@ -93,6 +94,7 @@ - CONFIG_REGULATOR_FIXED_VOLTAGE=y - CONFIG_REGULATOR_AXP20X=y - CONFIG_REGULATOR_GPIO=y -+CONFIG_REGULATOR_SY8106A=y - CONFIG_MEDIA_SUPPORT=y - CONFIG_RC_CORE=y - CONFIG_RC_DEVICES=y - ---- a/drivers/regulator/Kconfig -+++ b/drivers/regulator/Kconfig -@@ -785,6 +785,13 @@ - This driver supports the internal VMMC regulator in the STw481x - PMIC chips. - -+config REGULATOR_SY8106A -+ tristate "Silergy SY8106A regulator" -+ depends on I2C && (OF || COMPILE_TEST) -+ select REGMAP_I2C -+ help -+ This driver supports SY8106A single output regulator. -+ - config REGULATOR_TPS51632 - tristate "TI TPS51632 Power Regulator" - depends on I2C - ---- a/drivers/regulator/Makefile -+++ b/drivers/regulator/Makefile -@@ -98,6 +98,7 @@ - obj-$(CONFIG_REGULATOR_SKY81452) += sky81452-regulator.o - obj-$(CONFIG_REGULATOR_STM32_VREFBUF) += stm32-vrefbuf.o - obj-$(CONFIG_REGULATOR_STW481X_VMMC) += stw481x-vmmc.o -+obj-$(CONFIG_REGULATOR_SY8106A) += sy8106a-regulator.o - obj-$(CONFIG_REGULATOR_TI_ABB) += ti-abb-regulator.o - obj-$(CONFIG_REGULATOR_TPS6105X) += tps6105x-regulator.o - obj-$(CONFIG_REGULATOR_TPS62360) += tps62360-regulator.o - ---- a/drivers/thermal/Kconfig -+++ b/drivers/thermal/Kconfig -@@ -407,6 +407,16 @@ - Enable this option if you want to have support for thermal management - controller present in Mediatek SoCs - -+config SUN8I_THS -+ tristate "Thermal sensor driver for Allwinner H3" -+ depends on MACH_SUN8I || COMPILE_TEST -+ depends on HAS_IOMEM -+ depends on NVMEM_SUNXI_SID -+ depends on OF -+ depends on RESET_CONTROLLER -+ help -+ Enable this options for support thermal reporting on some Allwinner SoCs. -+ - menu "Broadcom thermal drivers" - depends on ARCH_BCM || COMPILE_TEST - source "drivers/thermal/broadcom/Kconfig" - ---- a/drivers/thermal/Makefile -+++ b/drivers/thermal/Makefile -@@ -58,6 +58,7 @@ - obj-$(CONFIG_TEGRA_SOCTHERM) += tegra/ - obj-$(CONFIG_HISI_THERMAL) += hisi_thermal.o - obj-$(CONFIG_MTK_THERMAL) += mtk_thermal.o -+obj-$(CONFIG_SUN8I_THS) += sun8i_ths.o - obj-$(CONFIG_GENERIC_ADC_THERMAL) += thermal-generic-adc.o - obj-$(CONFIG_ZX2967_THERMAL) += zx2967_thermal.o - obj-$(CONFIG_UNIPHIER_THERMAL) += uniphier_thermal.o - ---- /dev/null -+++ b/drivers/regulator/sy8106a-regulator.c -@@ -0,0 +1,168 @@ -+/* -+ * sy8106a-regulator.c - Regulator device driver for SY8106A -+ * -+ * Copyright (C) 2016 Ondřej Jirman -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU Library General Public -+ * License as published by the Free Software Foundation; either -+ * version 2 of the License, or (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Library General Public License for more details. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define SY8106A_REG_VOUT1_SEL 0x01 -+#define SY8106A_REG_VOUT_COM 0x02 -+#define SY8106A_REG_VOUT1_SEL_MASK 0x7f -+#define SY8106A_DISABLE_REG BIT(0) -+/* -+ * The I2C controlled voltage will only work when this bit is set; otherwise -+ * it will behave like a fixed regulator. -+ */ -+#define SY8106A_GO_BIT BIT(7) -+ -+struct sy8106a { -+ struct regulator_dev *rdev; -+ struct regmap *regmap; -+}; -+ -+static const struct regmap_config sy8106a_regmap_config = { -+ .reg_bits = 8, -+ .val_bits = 8, -+}; -+ -+static int sy8106a_set_voltage_sel(struct regulator_dev *rdev, unsigned int sel) -+{ -+ /* We use our set_voltage_sel in order to avoid unnecessary I2C -+ * chatter, because the regulator_get_voltage_sel_regmap using -+ * apply_bit would perform 4 unnecessary transfers instead of one, -+ * increasing the chance of error. -+ */ -+ return regmap_write(rdev->regmap, rdev->desc->vsel_reg, -+ sel | SY8106A_GO_BIT); -+} -+ -+static const struct regulator_ops sy8106a_ops = { -+ .set_voltage_sel = sy8106a_set_voltage_sel, -+ .set_voltage_time_sel = regulator_set_voltage_time_sel, -+ .get_voltage_sel = regulator_get_voltage_sel_regmap, -+ .list_voltage = regulator_list_voltage_linear, -+ /* Enabling/disabling the regulator is not yet implemented */ -+}; -+ -+/* Default limits measured in millivolts and milliamps */ -+#define SY8106A_MIN_MV 680 -+#define SY8106A_MAX_MV 1950 -+#define SY8106A_STEP_MV 10 -+ -+static const struct regulator_desc sy8106a_reg = { -+ .name = "SY8106A", -+ .id = 0, -+ .ops = &sy8106a_ops, -+ .type = REGULATOR_VOLTAGE, -+ .n_voltages = ((SY8106A_MAX_MV - SY8106A_MIN_MV) / SY8106A_STEP_MV) + 1, -+ .min_uV = (SY8106A_MIN_MV * 1000), -+ .uV_step = (SY8106A_STEP_MV * 1000), -+ .vsel_reg = SY8106A_REG_VOUT1_SEL, -+ .vsel_mask = SY8106A_REG_VOUT1_SEL_MASK, -+ /* -+ * This ramp_delay is a conservative default value which works on -+ * H3/H5 boards VDD-CPUX situations. -+ */ -+ .ramp_delay = 200, -+ .owner = THIS_MODULE, -+}; -+ -+/* -+ * I2C driver interface functions -+ */ -+static int sy8106a_i2c_probe(struct i2c_client *i2c, -+ const struct i2c_device_id *id) -+{ -+ struct sy8106a *chip; -+ struct device *dev = &i2c->dev; -+ struct regulator_dev *rdev = NULL; -+ struct regulator_config config = { }; -+ unsigned int selector; -+ int error; -+ -+ chip = devm_kzalloc(&i2c->dev, sizeof(struct sy8106a), GFP_KERNEL); -+ if (!chip) -+ return -ENOMEM; -+ -+ chip->regmap = devm_regmap_init_i2c(i2c, &sy8106a_regmap_config); -+ if (IS_ERR(chip->regmap)) { -+ error = PTR_ERR(chip->regmap); -+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n", -+ error); -+ return error; -+ } -+ -+ config.dev = &i2c->dev; -+ config.regmap = chip->regmap; -+ config.driver_data = chip; -+ -+ config.of_node = dev->of_node; -+ config.init_data = of_get_regulator_init_data(dev, dev->of_node, -+ &sy8106a_reg); -+ -+ if (!config.init_data) -+ return -ENOMEM; -+ -+ /* Probe regulator */ -+ error = regmap_read(chip->regmap, SY8106A_REG_VOUT1_SEL, &selector); -+ if (error) { -+ dev_err(&i2c->dev, "Failed to read voltage at probe time: %d\n", error); -+ return error; -+ } -+ -+ rdev = devm_regulator_register(&i2c->dev, &sy8106a_reg, &config); -+ if (IS_ERR(rdev)) { -+ error = PTR_ERR(rdev); -+ dev_err(&i2c->dev, "Failed to register SY8106A regulator: %d\n", error); -+ return error; -+ } -+ -+ chip->rdev = rdev; -+ -+ i2c_set_clientdata(i2c, chip); -+ -+ return 0; -+} -+ -+static const struct of_device_id sy8106a_i2c_of_match[] = { -+ { .compatible = "silergy,sy8106a" }, -+ { }, -+}; -+MODULE_DEVICE_TABLE(of, sy8106a_i2c_of_match); -+ -+static const struct i2c_device_id sy8106a_i2c_id[] = { -+ { "sy8106a", 0 }, -+ { }, -+}; -+MODULE_DEVICE_TABLE(i2c, sy8106a_i2c_id); -+ -+static struct i2c_driver sy8106a_regulator_driver = { -+ .driver = { -+ .name = "sy8106a", -+ .of_match_table = of_match_ptr(sy8106a_i2c_of_match), -+ }, -+ .probe = sy8106a_i2c_probe, -+ .id_table = sy8106a_i2c_id, -+}; -+ -+module_i2c_driver(sy8106a_regulator_driver); -+ -+MODULE_AUTHOR("Ondřej Jirman "); -+MODULE_DESCRIPTION("Regulator device driver for Silergy SY8106A"); -+MODULE_LICENSE("GPL"); - ---- /dev/null -+++ b/drivers/thermal/sun8i_ths.c -@@ -0,0 +1,332 @@ -+/* -+ * Thermal sensor driver for Allwinner new SoCs -+ * -+ * Copyright (C) 2016 Ondřej Jirman -+ * Based on the work of Josef Gajdusek -+ * -+ * This software is licensed under the terms of the GNU General Public -+ * License version 2, as published by the Free Software Foundation, and -+ * may be copied, distributed, and modified under those terms. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define THS_H3_MAX_SENSOR_NUM 4 -+ -+#define THS_H3_CTRL0 0x00 -+#define THS_H3_CTRL2 0x40 -+#define THS_H3_INT_CTRL 0x44 -+#define THS_H3_STAT 0x48 -+#define THS_H3_FILTER 0x70 -+#define THS_H3_CDATA0 0x74 -+#define THS_H3_CDATA1 0x74 -+#define THS_H3_DATA(n) (0x80 + 4 * (n)) -+ -+#define THS_H3_CTRL0_SENSOR_ACQ0(x) (x) -+#define THS_H3_CTRL2_SENSE_EN(n) BIT(0 + (n)) -+#define THS_H3_CTRL2_SENSOR_ACQ1(x) ((x) << 16) -+#define THS_H3_INT_CTRL_DATA_IRQ_EN(n) BIT(8 + (n)) -+#define THS_H3_INT_CTRL_THERMAL_PER(x) ((x) << 12) -+#define THS_H3_STAT_DATA_IRQ_STS(n) BIT(8 + (n)) -+#define THS_H3_FILTER_TYPE(x) ((x) << 0) -+#define THS_H3_FILTER_EN BIT(2) -+ -+#define THS_H3_CLK_IN 40000000 /* Hz */ -+#define THS_H3_DATA_PERIOD 330 /* ms */ -+ -+#define THS_H3_FILTER_TYPE_VALUE 2 /* average over 2^(n+1) samples */ -+#define THS_H3_FILTER_DIV (1 << (THS_H3_FILTER_TYPE_VALUE + 1)) -+#define THS_H3_INT_CTRL_THERMAL_PER_VALUE \ -+ (THS_H3_DATA_PERIOD * (THS_H3_CLK_IN / 1000) / THS_H3_FILTER_DIV / 4096 - 1) -+#define THS_H3_CTRL0_SENSOR_ACQ0_VALUE 0x3f /* 16us */ -+#define THS_H3_CTRL2_SENSOR_ACQ1_VALUE 0x3f -+ -+struct sun8i_ths_data; -+ -+struct sun8i_ths_sensor { -+ struct sun8i_ths_data *data; -+ int id; -+ struct thermal_zone_device *tzd; -+ u32 val; -+}; -+ -+struct sun8i_ths_cfg { -+ int sensor_num; -+ int (*calc_temp)(u32 val); -+}; -+ -+struct sun8i_ths_data { -+ struct reset_control *reset; -+ struct clk *clk; -+ struct clk *busclk; -+ void __iomem *regs; -+ struct nvmem_cell *calcell; -+ const struct sun8i_ths_cfg *cfg; -+ struct sun8i_ths_sensor sensors[THS_H3_MAX_SENSOR_NUM]; -+}; -+ -+static int sun8i_ths_calc_temp_h3(u32 val) -+{ -+ return (217000 - (int)((val * 1000000) / 8253)); -+} -+ -+static int sun8i_ths_get_temp(void *_data, int *out) -+{ -+ struct sun8i_ths_sensor *sensor = _data; -+ -+ if (sensor->val == 0) -+ return -EBUSY; -+ -+ /* Formula and parameters from the Allwinner 3.4 kernel */ -+ *out = sensor->data->cfg->calc_temp(sensor->val); -+ return 0; -+} -+ -+static irqreturn_t sun8i_ths_irq_thread(int irq, void *_data) -+{ -+ struct sun8i_ths_data *data = _data; -+ int i; -+ -+ for (i = 0; i < data->cfg->sensor_num; i++) { -+ if (!(readl(data->regs + THS_H3_STAT) & -+ THS_H3_STAT_DATA_IRQ_STS(i))) -+ continue; -+ -+ writel(THS_H3_STAT_DATA_IRQ_STS(i), data->regs + THS_H3_STAT); -+ -+ data->sensors[i].val = readl(data->regs + THS_H3_DATA(i)); -+ if (data->sensors[i].val) -+ thermal_zone_device_update(data->sensors[i].tzd, -+ THERMAL_EVENT_TEMP_SAMPLE); -+ } -+ -+ return IRQ_HANDLED; -+} -+ -+static void sun8i_ths_init(struct sun8i_ths_data *data) -+{ -+ u32 val; -+ int i; -+ -+ writel(THS_H3_CTRL0_SENSOR_ACQ0(THS_H3_CTRL0_SENSOR_ACQ0_VALUE), -+ data->regs + THS_H3_CTRL0); -+ writel(THS_H3_FILTER_EN | THS_H3_FILTER_TYPE(THS_H3_FILTER_TYPE_VALUE), -+ data->regs + THS_H3_FILTER); -+ -+ val = THS_H3_CTRL2_SENSOR_ACQ1(THS_H3_CTRL2_SENSOR_ACQ1_VALUE); -+ for (i = 0; i < data->cfg->sensor_num; i++) -+ val |= THS_H3_CTRL2_SENSE_EN(i); -+ writel(val, data->regs + THS_H3_CTRL2); -+ -+ val = THS_H3_INT_CTRL_THERMAL_PER(THS_H3_INT_CTRL_THERMAL_PER_VALUE); -+ for (i = 0; i < data->cfg->sensor_num; i++) -+ val |= THS_H3_INT_CTRL_DATA_IRQ_EN(i); -+ writel(val, data->regs + THS_H3_INT_CTRL); -+} -+ -+static int sun8i_ths_calibrate(struct sun8i_ths_data *data) -+{ -+ u32 *caldata; -+ size_t callen; -+ -+ caldata = nvmem_cell_read(data->calcell, &callen); -+ if (IS_ERR(caldata)) -+ return PTR_ERR(caldata); -+ -+ writel(be32_to_cpu(caldata[0]), data->regs + THS_H3_CDATA0); -+ if (callen > 4) -+ writel(be32_to_cpu(caldata[1]), data->regs + THS_H3_CDATA1); -+ -+ kfree(caldata); -+ return 0; -+} -+ -+static const struct thermal_zone_of_device_ops sun8i_ths_thermal_ops = { -+ .get_temp = sun8i_ths_get_temp, -+}; -+ -+static int sun8i_ths_probe(struct platform_device *pdev) -+{ -+ struct sun8i_ths_data *data; -+ struct resource *res; -+ int ret, irq, i; -+ -+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); -+ if (!data) -+ return -ENOMEM; -+ -+ data->cfg = of_device_get_match_data(&pdev->dev); -+ if (!data->cfg) -+ return -EINVAL; -+ -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ if (!res) { -+ dev_err(&pdev->dev, "no memory resources defined\n"); -+ return -EINVAL; -+ } -+ -+ data->regs = devm_ioremap_resource(&pdev->dev, res); -+ if (IS_ERR(data->regs)) { -+ ret = PTR_ERR(data->regs); -+ dev_err(&pdev->dev, "failed to ioremap THS registers: %d\n", ret); -+ return ret; -+ } -+ -+ irq = platform_get_irq(pdev, 0); -+ if (irq < 0) { -+ dev_err(&pdev->dev, "failed to get IRQ: %d\n", irq); -+ return irq; -+ } -+ -+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, -+ sun8i_ths_irq_thread, IRQF_ONESHOT, -+ dev_name(&pdev->dev), data); -+ if (ret) -+ return ret; -+ -+ data->busclk = devm_clk_get(&pdev->dev, "ahb"); -+ if (IS_ERR(data->busclk)) { -+ ret = PTR_ERR(data->busclk); -+ dev_err(&pdev->dev, "failed to get ahb clk: %d\n", ret); -+ return ret; -+ } -+ -+ data->clk = devm_clk_get(&pdev->dev, "ths"); -+ if (IS_ERR(data->clk)) { -+ ret = PTR_ERR(data->clk); -+ dev_err(&pdev->dev, "failed to get ths clk: %d\n", ret); -+ return ret; -+ } -+ -+ data->reset = devm_reset_control_get(&pdev->dev, "ahb"); -+ if (IS_ERR(data->reset)) { -+ ret = PTR_ERR(data->reset); -+ dev_err(&pdev->dev, "failed to get reset: %d\n", ret); -+ return ret; -+ } -+ -+ ret = reset_control_deassert(data->reset); -+ if (ret) { -+ dev_err(&pdev->dev, "reset deassert failed: %d\n", ret); -+ return ret; -+ } -+ -+ ret = clk_prepare_enable(data->busclk); -+ if (ret) { -+ dev_err(&pdev->dev, "failed to enable bus clk: %d\n", ret); -+ goto err_assert_reset; -+ } -+ -+ ret = clk_prepare_enable(data->clk); -+ if (ret) { -+ dev_err(&pdev->dev, "failed to enable ths clk: %d\n", ret); -+ goto err_disable_bus; -+ } -+ -+ ret = clk_set_rate(data->clk, THS_H3_CLK_IN); -+ if (ret) -+ goto err_disable_ths; -+ -+ data->calcell = devm_nvmem_cell_get(&pdev->dev, "cal"); -+ if (IS_ERR(data->calcell)) { -+ if (PTR_ERR(data->calcell) == -EPROBE_DEFER) { -+ ret = PTR_ERR(data->calcell); -+ goto err_disable_ths; -+ } -+ /* -+ * Even if the external calibration data stored in eFUSE is -+ * not accessible, the THS hardware can still work, although -+ * the data won't be so accurate. -+ * The default value of calibration register is 0x800 for -+ * every sensor, and the calibration value is usually 0x7xx -+ * or 0x8xx, so they won't be away from the default value -+ * for a lot. -+ * So here we do not return if the calibartion data is not -+ * available, except the probe needs deferring. -+ */ -+ } else { -+ ret = sun8i_ths_calibrate(data); -+ if (ret) -+ goto err_disable_ths; -+ } -+ -+ for (i = 0; i < data->cfg->sensor_num; i++) { -+ data->sensors[i].data = data; -+ data->sensors[i].id = i; -+ data->sensors[i].tzd = -+ devm_thermal_zone_of_sensor_register(&pdev->dev, -+ i, &data->sensors[i], &sun8i_ths_thermal_ops); -+ if (IS_ERR(data->sensors[i].tzd)) { -+ ret = PTR_ERR(data->sensors[i].tzd); -+ dev_err(&pdev->dev, -+ "failed to register thermal zone %d: %d\n", -+ i, ret); -+ goto err_disable_ths; -+ } -+ } -+ -+ sun8i_ths_init(data); -+ -+ platform_set_drvdata(pdev, data); -+ return 0; -+ -+err_disable_ths: -+ clk_disable_unprepare(data->clk); -+err_disable_bus: -+ clk_disable_unprepare(data->busclk); -+err_assert_reset: -+ reset_control_assert(data->reset); -+ return ret; -+} -+ -+static int sun8i_ths_remove(struct platform_device *pdev) -+{ -+ struct sun8i_ths_data *data = platform_get_drvdata(pdev); -+ -+ reset_control_assert(data->reset); -+ clk_disable_unprepare(data->clk); -+ clk_disable_unprepare(data->busclk); -+ return 0; -+} -+ -+static const struct sun8i_ths_cfg sun8i_h3_ths_cfg = { -+ .sensor_num = 1, -+ .calc_temp = sun8i_ths_calc_temp_h3, -+}; -+ -+static const struct of_device_id sun8i_ths_id_table[] = { -+ { .compatible = "allwinner,sun8i-h3-ths", .data = &sun8i_h3_ths_cfg }, -+ { /* sentinel */ }, -+}; -+MODULE_DEVICE_TABLE(of, sun8i_ths_id_table); -+ -+static struct platform_driver sun8i_ths_driver = { -+ .probe = sun8i_ths_probe, -+ .remove = sun8i_ths_remove, -+ .driver = { -+ .name = "sun8i_ths", -+ .of_match_table = sun8i_ths_id_table, -+ }, -+}; -+ -+module_platform_driver(sun8i_ths_driver); -+ -+MODULE_AUTHOR("Ondřej Jirman "); -+MODULE_DESCRIPTION("Thermal sensor driver for new Allwinner SoCs"); -+MODULE_LICENSE("GPL v2");