Hello,

i'd like to place a RFC for a Kernel Module reading out the TP temperature of the A2x SoC.

- output via sysfs
- all THS registers are user configurable including start/stop
- module is initially turned off after loading. THS has to be enabled or module loaded with param.
- module honors /proc/ioports

This module is the (base) test layout for the A33 (which i dont have) and the A80 (which i hopefully get within the next days).

Tested on cubietruck 3.4.103 as module or compiled into kernel.
Compiles under mainline kernel but not tested.

This module should work for the A33 out of the box BUT no guarantee for correct temp reading.
On A33 the calibration value for the THS is placed in the SID register(s).

Is such temp monitoring stuff useful at all ?
What do you think ?

Regards
Heiko


From 615e5e0dda0715d4cb0f483b5a4cf39c68d61157 Mon Sep 17 00:00:00 2001
From: Heiko Schroeter <[email protected]>
Date: Thu, 18 Jun 2015 15:18:30 +0200
Subject: [PATCH 1/1] arm: Added A2X SoC temperature readout

 * Register values are user settable for platform depending
 * power saving etc.
 * THS registers: /sys/bus/platform/devices/a2x_thermal/
 * Naming scheme identical to A23 datasheet.
 * All settable register values in HEX.
 * /sys/bus/platform/devices/a2x_ths/settings shows all THS
 * register HEX values.
 *
 * THS registers are preset on module load, but NOT enabled
 * by default.
 * To start THS:
 * echo "0x10000" > /sys/bus/platform/devices/a2x_thermal/ths_en
 * or
 * module_param: start_on_load=1 starts THS on module load.
 *
 * Temperature output in Celcius*100.

Signed-off-by: Heiko Schroeter <[email protected]>
---
 drivers/hwmon/Kconfig       |  14 +
 drivers/hwmon/Makefile      |   1 +
drivers/hwmon/a2x_thermal.c | 667 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 682 insertions(+)
 create mode 100644 drivers/hwmon/a2x_thermal.c

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 25d9e72..9b996db 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -39,6 +39,20 @@ config HWMON_DEBUG_CHIP

 comment "Native drivers"

+
+config A2X_THERMAL
+        tristate "A2x SoC TP sensor controller"
+        depends on !TOUCHSCREEN_SUN4I_TS
+        help
+          If you say yes here you get support for the hardware temperature
+ monitoring sensor present in the touch screen controller on A2x SoC. + Output via sysfs i.e. /sys/bus/platform/devices/a2x_thermal/ths_data
+          Divied ths_data by 100 to get Celcius.
+ All other THS register settings are user settable by writing to sysfs.
+
+ This driver can be build as a module. If so, the module will called
+          a2x_thermal
+
 config SENSORS_AB8500
     tristate "AB8500 thermal monitoring"
     depends on AB8500_GPADC && AB8500_BM
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index b4a40f1..4e9d9cc 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -161,3 +161,4 @@ obj-$(CONFIG_PMBUS)        += pmbus/

 ccflags-$(CONFIG_HWMON_DEBUG_CHIP) := -DDEBUG

+obj-$(CONFIG_A2X_THERMAL)       += a2x_thermal.o
diff --git a/drivers/hwmon/a2x_thermal.c b/drivers/hwmon/a2x_thermal.c
new file mode 100644
index 0000000..a15bba2
--- /dev/null
+++ b/drivers/hwmon/a2x_thermal.c
@@ -0,0 +1,667 @@
+/*
+ * a2x_thermal.c  -- A2x touchpad temperature readout
+ *
+ * Copyright 2015 Heiko Schroeter
+ *
+ * Author: Heiko Schroeter
+ *         [email protected]
+ *
+ * Ideas taken from various Authors using the hwmon_device interface.
+ *
+ * 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.
+ *
+ *
+ * ##################################
+ *
+ * Register values are user settable for platform depending
+ * power saving etc.
+ * THS registers: /sys/bus/platform/devices/a2x_thermal/
+ * Naming scheme identical to A23 datasheet.
+ * All settable register values in HEX.
+ * /sys/bus/platform/devices/a2x_ths/settings shows all THS
+ * register HEX values.
+ *
+ * THS registers are preset on module load, but NOT enabled
+ * by default.
+ * To start THS:
+ * echo "0x10000" > /sys/bus/platform/devices/a2x_thermal/ths_en
+ * or
+ * module_param: start_on_load=1 starts THS on module load.
+ *
+ * Temperature output in Celcius*100.
+ *
+ * ###################################
+ *
+ * From A23 User Manual (Revision 1.0):
+ *
+ * 3.19 Thermal Sensor Controller
+ * 3.19.1 Overview
+ *
+ * The A23 supports thermal sensor controller to monitor the chip temperature.
+ *
+ * 3.19.2 Clock Tree and ADC Conversion Time
+ *
+ * A/D CONVERSION TIME
+ * When the clock source is 24MHz and the prescaler value M*N is 6,
+ * total 12-bit conversion time is as follows.
+ *
+ * CLK_IN = 24MHz/6 = 4MHz
+ *
+ * Conversion Time = 1/(4MHz/14Cycles) =3.50us
+ *
+ * If  ADC acquire  time  divider  is, then  TACQ  =1/(4MHz/6)  = 1.50us
+ * FS_TIME (configured by the FS_DIV register) bases on the summation
+ * of Conversion Time and TACQ. The FS_TIME  must be greater or equal
+ * than (TACQ  +  Conversion Time)
+ *
+ * FS_TIME >= TACQ + Conversion ITime=5.0us
+ *
+ * This  A/D converter was designed to operate at maximum 24MHz clock,
+ * and the conversion rate can go up to 1 MSPS.
+ *
+ * Register naming convention as in A23 manual.
+ *
+ *
+ * 3.19.3 Thermal Measurement
+ *   T  = (V[AL] - 1669)/6.25 Celsius degree
+ *   The V[AL] should read from thermal value register.
+ *   FORMULA WRONG; offset of 0x10 = 100/6.25 is missing
+ *   correct: T = (V[al] - 1669 + 100)*16 / 100
+ *
+ * 3.19.4 Thermal Sensor Controller Register List
+ * Module Name      Base Address
+ * THS              0x01C25000
+ *
+ * Register Name    Offset        Description
+ * THS_CTRL0        0x00          THS Control Register0
+ * THS_CTRL1        0x04          THS Control Register1
+ * THS_INT          0x10          THS Int Control
+ * THS_DATA_PENDING 0x14          THS Data Status
+ * THS_TPR          0x18          THS Period Register
+ * THS_DATA         0x20          THS Data Register
+ *
+ * 3.19.5 Thermal Sensor Controller Register Description
+ * THS CONTROL REGISTER 0
+ *
+ * Offset:  0x00  Register Name: THS_CTRL0
+ * Bit      Read/     Default/    Description
+ *          Write     Hex
+ * 31:24    R/W       0xF         DATA_FIRST_DLY.
+ * DATA First Convert Delay Time(T_FCDT)setting.
+ *                                Based on Data First Convert Delay Mode
+ *                                select (Bit 23).
+ * T_FCDT = DATA_FIRST_DLY * DATA_FIRST_DLY_MODE
+ * 23       R/W       0x1         DATA_FIRST_DLY_MODE.
+ *                                TDATA First Convert Delay Mode Select.
+ *                                0: CLK_IN/16
+ *                                1: CLK_IN/16*256
+ * 22      /          /           /
+ * 21:20   R/W        0x0         DATA_CLK_DIVIDER.
+ *                                DATA Clock Divider(CLK_IN).
+ *                                00: CLK/2
+ *                                01: CLK/3
+ *                                10: CLK/6
+ *                                11: CLK/1
+ * 19:16   R/W        0x0         FS_DIV.
+ *                                DATA Sample Frequency Divider.
+ *                                0000: CLK_IN/(2 power (20-n))
+ *                                0001: CLK_IN/(2 power (20-n))
+ *                                ....
+ *                                1111: CLK_IN/32
+ * 15:0    R/W        0x0         TACQ.
+ *                                DATA acquire time.
+ *                                CLK_IN/(16*(N+1))
+ *
+ * THS CONTROL REGISTER 1
+ * !! ATTENTION: ERROR in DATASHEET !!
+ * First HEX Value should be BITPOS 0-3 and not 0-4 !
+ * Offset: 0x04 Register Name: THS_CTRL1
+ * Bit      Read/     Default/    Description
+ *          Write     Hex
+ * 31:8     /         /           /
+ * 7        R/W       0x1         CHOP_TEMP_EN.
+ *                                Chop temperature calibration enable.
+ *                                0: Disable
+ *                                1: Enable
+ * 6:5      /         /           /
+ * 4        R/W       0x0         DATA_EN.
+ *                                DATA Function Enable.
+ *                                0: Disable
+ *                                1: Enable
+ * 3:0      R/W       0x1         /
+ *
+ *
+ * From A20 Manual. Description of TP_INT not in A23 Manual
+ * THS (TP) INTERRUPT& FIFO CONTROL REGISTER
+ * Offset: 0x10 Register Name: THS_INT (TP_INT)
+ * Bit      Read/     Default/    Description
+ *          Write     Hex
+ * 31:19    /         /           /
+ * 18       R/W       0x0         TEMP_IRQ_ENABLE.
+ * 17       R/W       0x0         TP_OVERRUN_IRQ_EN.
+ * 16       R/W       0x0         TP_DATA_IRQ_EN.
+ * 15:14    /         /           /
+ * 13       R/W       0x0         TP_DATA_XY_CHANGE.
+ * 12:8     R/W       0xF         TP_FIFO_TRIG_LEVEL.
+ * 7        R/W       0x0         TP_DATA_DRQ_EN.
+ * 6:5      /         /           /
+ * 4        R/W       0x0         TP_FIFO_FLUSH.
+ * 3:2      /         /           /
+ * 1        R/W       0x0         TP_UP_IRQ_EN.
+ * 0        R/W       0x0         TP_DOWN_IRQ_EN.
+ *
+ *
+ * THS DATA STATUS REGISTER
+ * Offset: 0x14 Register Name: DATA_FIFOS
+ * Bit      Read/     Default/    Description
+ *          Write     Hex
+ * 31:19    /         /           /
+ * 18       R/W       0x0         THS_DATA_PENDING.
+ *                                Thermal sensor data pending.
+ *                                0: No Pending
+ *                                1: Thermal sensor data Pending
+ *                                Write ‘1’ to clear this interrupt or
+ *                                automatic clear if data pending
+ *                                condition fails.
+ *
+ * THS PERIOD REGISTER
+ * Offset: 0x18 Register Name: THS_TPR
+ * Bit      Read/     Default/    Description
+ *          Write     Hex
+ * 31:16    /         /           /
+ * 16       R/W       0x0         THS_EN.
+ *                                Thermal sensor enable.
+ * 15:0     R/W       0x0         THS_PER.
+ *                                Thermal sensor Period.
+ *                                4096*(1/clk_in)
+ *
+ * THS DATA REGISTER
+ * Offset: 0x20 Register Name: THS_DATA
+ * Bit      Read/     Default/    Description
+ *          Write     Hex         Default: 0x0000_0000
+ * 31:12    /         /           /
+ * 11:0     R         0x0         THS_DATA.
+ *                                Thermal sensor data.
+ *
+ *
+ */
+
+#define A2X_LICENSE "GPL"
+#define A2X_VERSION "1.0"
+#define A2X_AUTHOR  "Heiko Schroeter <[email protected]>"
+#define A2X_DESC    "A2x SoC THS reading and setting."
+
+/*
+ *  Changelog:
+ *
+ *  2015-06-18     1.0    Initial release
+ *
+ *
+ *
+ */
+
+#include <linux/hwmon-sysfs.h>
+#include <linux/hwmon.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+
+#define THS_MODULE_NAME  "a2x_thermal"
+
+/* Access rights for sysfs */
+#define THS_S_UGO (S_IWUSR | S_IRUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IXOTH)
+
+/* 1k IO MEM Space */
+#define THS_IO_MEM_SIZE  0x400
+
+/* A23 Thermal Registers */
+#define THS        0xF1C25000   /* Base */
+#define THS_CTRL0  ((void *) (THS + 0x00)) /* THS Control Register0 */
+#define THS_CTRL1  ((void *) (THS + 0x04)) /* THS Control Register1 */
+#define THS_INT    ((void *) (THS + 0x10)) /* THS Int Enable */
+#define THS_FIFOS  ((void *) (THS + 0x14)) /* THS Data Status */
+#define THS_TPR    ((void *) (THS + 0x18)) /* THS Period Register */
+#define THS_DATA   ((void *) (THS + 0x20)) /* THS Data Register */
+
+/* Bitpositions in Register */
+/* THS_CTRL0 */
+#define DATA_FIRST_DLY      24
+#define DATA_FIRST_DLY_MODE 23
+#define DATA_CLK_DIVIDER    20
+#define FS_DIV              16
+#define TACQ                 0
+
+/* THS_CTRL1 */
+#define CHOP_TEMP_EN 7
+#define DATA_EN      4
+
+/* THS INT */
+#define TEMP_IRQ_ENABLE 18
+
+/* THS_FIFOS */
+#define DATA_PENDING 18
+
+/* THS_TPR */
+#define THS_EN       16
+#define THS_PER       0
+
+/* THS_DATA */
+#define THS_DATA_MASK 0xFFF
+
+#define THS_EAGAIN -1000
+
+/* Indicate no real value for the start */
+int32_t ths_data = THS_EAGAIN;
+
+/* preset ths register variables */
+/* 200000 50000 f8 80 10 10000 ff8 40000 */
+uint32_t data_first_dly = (0x0F << DATA_FIRST_DLY);
+uint32_t data_first_dly_mode = (0x01 << DATA_FIRST_DLY_MODE);
+uint32_t data_clk_divider = (0x02 << DATA_CLK_DIVIDER);
+uint32_t fs_div = (0x05 << FS_DIV);
+uint32_t tacq = (0xF8 << TACQ);
+uint32_t chop_temp_en = (1 << CHOP_TEMP_EN);
+uint32_t temp_irq_enable = (1 << TEMP_IRQ_ENABLE);
+uint32_t data_en = (1 << DATA_EN);
+/* Do not start by default */
+uint32_t ths_en = (0 << THS_EN);
+uint32_t ths_per = (0xFF8 << THS_PER);
+uint32_t data_pending = (1 << DATA_PENDING);
+
+/* module parameter */
+static bool start_on_load;
+module_param(start_on_load, bool, S_IRUGO);
+MODULE_PARM_DESC(start_on_load, "Start THS conversion on module load");
+
+/*
+ * Setup THS registers
+ */
+int ths_setup(void)
+{
+
+    /* Setup ADC speed for TP conversion */
+    writel(data_first_dly | data_first_dly_mode |
+           data_clk_divider | fs_div | tacq,
+           THS_CTRL0);
+
+    /* Enable TP */
+    writel(chop_temp_en | data_en, THS_CTRL1);
+
+    /* Enable the temperature */
+    writel(ths_en | ths_per, THS_TPR);
+
+    /* Enable the temperature IRQ */
+    writel(temp_irq_enable, THS_INT);
+
+    /* Make register setup atomic*/
+    wmb();
+
+    return 0;
+}
+
+/*
+ * Show THS values.
+ */
+static ssize_t
+show_values(struct device *dev, struct device_attribute *devattr, char *buf)
+{
+    struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+
+    /* Avoid race condition for first call */
+    if (ths_data <= THS_EAGAIN)
+        return -EAGAIN;
+
+    switch (attr->index) {
+    case 0:
+        sprintf(buf, "%d\n", ths_data);
+        break;
+    case 1:
+        sprintf(buf, "0x%x\n", data_clk_divider);
+        break;
+    case 2:
+        sprintf(buf, "0x%x\n", fs_div);
+        break;
+    case 3:
+        sprintf(buf, "0x%x\n", tacq);
+        break;
+    case 4:
+        sprintf(buf, "0x%x\n", chop_temp_en);
+        break;
+    case 5:
+        sprintf(buf, "0x%x\n", data_en);
+        break;
+    case 6:
+        sprintf(buf, "0x%x\n", ths_en);
+        break;
+    case 7:
+        sprintf(buf, "0x%x\n", ths_per);
+        break;
+    case 8:
+        sprintf(buf, "0x%x\n", data_pending);
+        break;
+    case 9:
+        sprintf(buf, "0x%x\n", data_first_dly);
+        break;
+    case 10:
+        sprintf(buf, "0x%x\n", data_first_dly_mode);
+        break;
+    case 11:
+        sprintf(buf, "%x %x %x %x %x %x %x %x\n",
+            data_clk_divider, fs_div, tacq, chop_temp_en,
+            temp_irq_enable, ths_en, ths_per, data_pending);
+        break;
+    default:
+        break;
+    }
+
+    return *buf;
+}
+
+/*
+ * Read THS values and Init THS with new values.
+ * User setable for power saving etc.
+ */
+static ssize_t
+read_values(struct device *dev, struct device_attribute *devattr,
+        const char *buf, size_t n)
+{
+    struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+    long number;
+    int err = 0;
+
+    err = kstrtol(buf, 16, &number);
+    if (err)
+        return err;
+
+    switch (attr->index) {
+    case 1:
+        data_clk_divider = (uint32_t) number;
+        data_clk_divider &= (0xF << DATA_CLK_DIVIDER);
+        break;
+    case 2:
+        fs_div = (uint32_t) number;
+        fs_div &= (0xF << FS_DIV);
+        break;
+    case 3:
+        tacq = (uint32_t) number;
+        tacq &= (0xFF << TACQ);
+        break;
+    case 4:
+        chop_temp_en = (uint32_t) number;
+        chop_temp_en &= (1 << CHOP_TEMP_EN);
+        break;
+    case 5:
+        temp_irq_enable  = (uint32_t) number;
+        temp_irq_enable &= (1 << TEMP_IRQ_ENABLE);
+        break;
+    case 6:
+        ths_en = (uint32_t) number;
+        ths_en &= (1 << THS_EN);
+        break;
+    case 7:
+        ths_per = (uint32_t) number;
+        ths_per &= (0xFFF << THS_PER);
+        break;
+    case 8:
+        data_pending = (uint32_t) number;
+        data_pending &= (1 << DATA_PENDING);
+        break;
+    case 9:
+        data_first_dly = (uint32_t) number;
+        data_first_dly &= (0xF << DATA_FIRST_DLY);
+        break;
+    case 10:
+        data_first_dly_mode = (uint32_t) number;
+        data_first_dly_mode &= (0xF << DATA_FIRST_DLY_MODE);
+        break;
+    default:
+        break;
+    }
+
+    /* init ADC again with new values */
+    ths_setup();
+
+    return n;
+}
+
+static SENSOR_DEVICE_ATTR(ths_data, S_IRUGO, show_values,
+              NULL, 0);
+static SENSOR_DEVICE_ATTR(data_clk_divider, THS_S_UGO, show_values,
+              read_values, 1);
+static SENSOR_DEVICE_ATTR(fs_div, THS_S_UGO, show_values,
+              read_values, 2);
+static SENSOR_DEVICE_ATTR(tacq, THS_S_UGO, show_values,
+              read_values, 3);
+static SENSOR_DEVICE_ATTR(chop_temp_en, THS_S_UGO, show_values,
+              read_values, 4);
+static SENSOR_DEVICE_ATTR(temp_irq_en, THS_S_UGO, show_values,
+              read_values, 5);
+static SENSOR_DEVICE_ATTR(ths_en, THS_S_UGO, show_values,
+              read_values, 6);
+static SENSOR_DEVICE_ATTR(ths_per, THS_S_UGO, show_values,
+              read_values, 7);
+static SENSOR_DEVICE_ATTR(data_pending, THS_S_UGO, show_values,
+              read_values, 8);
+static SENSOR_DEVICE_ATTR(data_first_dly, THS_S_UGO, show_values,
+              read_values, 9);
+static SENSOR_DEVICE_ATTR(data_first_dly_mode, THS_S_UGO, show_values,
+              read_values, 10);
+static SENSOR_DEVICE_ATTR(settings, THS_S_UGO, show_values,
+              NULL, 11);
+
+static struct attribute *ths_attributes[] = {
+    &sensor_dev_attr_ths_data.dev_attr.attr,
+    &sensor_dev_attr_data_clk_divider.dev_attr.attr,
+    &sensor_dev_attr_fs_div.dev_attr.attr,
+    &sensor_dev_attr_tacq.dev_attr.attr,
+    &sensor_dev_attr_chop_temp_en.dev_attr.attr,
+    &sensor_dev_attr_temp_irq_en.dev_attr.attr,
+    &sensor_dev_attr_ths_en.dev_attr.attr,
+    &sensor_dev_attr_ths_per.dev_attr.attr,
+    &sensor_dev_attr_data_pending.dev_attr.attr,
+    &sensor_dev_attr_data_first_dly.dev_attr.attr,
+    &sensor_dev_attr_data_first_dly_mode.dev_attr.attr,
+    &sensor_dev_attr_settings.dev_attr.attr,
+    NULL
+};
+
+static const struct attribute_group ths_group = {
+    .attrs = ths_attributes
+};
+
+/*
+ * Interrupt routine for A2X Touchpad temperature
+ */
+static irqreturn_t ths_handle_irq(int irq, void *pdev)
+{
+    uint32_t reg_val;
+
+    reg_val = readl(THS_FIFOS);
+
+    /* Check if temperature interrupt triggered */
+    if (reg_val & (0x1 << DATA_PENDING)) {
+        /* Clear A2X TP Interrupt */
+        writel(reg_val | (0x1 << DATA_PENDING), THS_FIFOS);
+        /* Read and Calibrate Temperature Data */
+        reg_val = readl(THS_DATA);
+        /* Only 12 bits */
+        reg_val &= 0x00000FFF;
+        /* Calibrate temp value to Celcius*100 */
+        ths_data = ((reg_val - 1669 + 100) << 4);
+        return IRQ_HANDLED;
+    }
+    return IRQ_NONE;
+}
+
+/*
+ * Device probing
+ */
+static int a2x_ths_hwmon_probe(struct platform_device *pdev)
+{
+    int irq = platform_get_irq(pdev, 0);
+    int err = 0;
+    struct device *hwmon_dev;
+
+    /* Modul Parameter check */
+    if (start_on_load)
+        ths_en = (1 << THS_EN);
+
+
+    /* Init THS register */
+    ths_setup();
+
+    /* create Int Handler */
+    err = request_irq(irq, ths_handle_irq,
+              IRQF_DISABLED, pdev->name, pdev);
+    if (err) {
+        dev_err(&pdev->dev, "Cannot request THS IRQ\n");
+        goto probe_err1;
+    }
+
+    /* create sysfs files */
+    err = sysfs_create_group(&pdev->dev.kobj, &ths_group);
+    if (err) {
+        dev_err(&pdev->dev, "Cannot create sysfs group\n");
+        goto probe_err2;
+    }
+    /* register device in HWMON */
+    hwmon_dev = hwmon_device_register(&pdev->dev);
+
+    if (IS_ERR(hwmon_dev)) {
+        err = PTR_ERR(hwmon_dev);
+        goto probe_err3;
+    }
+
+    /* Signal load success */
+    dev_info(&pdev->dev, "TP initialization success\n");
+
+    return 0;
+
+probe_err3:
+    sysfs_remove_group(&pdev->dev.kobj, &ths_group);
+probe_err2:
+    free_irq(irq, pdev);
+probe_err1:
+    return err;
+}
+
+/*
+ * Device remove
+ */
+static int a2x_ths_hwmon_remove(struct platform_device *pdev)
+{
+    int irq = platform_get_irq(pdev, 0);
+
+    /* disable A2X THS INT */
+    writel((0 << THS_EN), THS_TPR);
+    /* release s/w interrupt routine */
+    free_irq(irq, pdev);
+
+    return 0;
+}
+
+
+static struct resource a2x_ths_hwmon_resource[] = {
+    [0] = {
+        .flags  = IORESOURCE_MEM,
+        .start  = THS,
+        .end    = THS + THS_IO_MEM_SIZE - 1
+    },
+    [1] = {
+        .flags  = IORESOURCE_IRQ,
+        .start  = SW_INT_IRQNO_TOUCH_PANEL,
+        .end    = SW_INT_IRQNO_TOUCH_PANEL
+    }
+};
+
+static struct platform_device *a2x_ths_hwmon_device;
+
+static struct platform_driver a2x_ths_hwmon_driver = {
+    .probe    = a2x_ths_hwmon_probe,
+    .remove   = __devexit_p(a2x_ths_hwmon_remove),
+    .driver   = {
+        .name   = THS_MODULE_NAME,
+        .owner  = THIS_MODULE,
+    },
+};
+
+/*
+ * THS Init
+ */
+static int __init a2x_ths_init(void)
+{
+
+    int err = 0;
+
+    /* Get IO Address Range*/
+    if (!request_region(THS, THS_IO_MEM_SIZE, THS_MODULE_NAME)) {
+        pr_err("%s request region failed. Check /proc/ioports.\n",
+               THS_MODULE_NAME);
+        return -EBUSY;
+    }
+    err = platform_driver_register(&a2x_ths_hwmon_driver);
+    if (err) {
+        pr_err("%s driver register failed.\n", THS_MODULE_NAME);
+        goto init_err1;
+    }
+
+    a2x_ths_hwmon_device = platform_device_alloc(THS_MODULE_NAME, -1);
+    if (!a2x_ths_hwmon_device) {
+        pr_err("%s device_alloc failed.\n", THS_MODULE_NAME);
+        err = PTR_ERR(a2x_ths_hwmon_device);
+        goto init_err2;
+    }
+
+    err = platform_device_add_resources(a2x_ths_hwmon_device,
+                        a2x_ths_hwmon_resource,
+                        ARRAY_SIZE(a2x_ths_hwmon_resource));
+    if (err) {
+        pr_err("%s device_add_resources failed.\n", THS_MODULE_NAME);
+        goto init_err3;
+    }
+
+    err = platform_device_add(a2x_ths_hwmon_device);
+    if (err) {
+        pr_err("a2x_tp platform_device_add failed.\n");
+        goto init_err3;
+    }
+
+    return 0;
+
+init_err3:
+    platform_device_put(a2x_ths_hwmon_device);
+init_err2:
+    platform_driver_unregister(&a2x_ths_hwmon_driver);
+init_err1:
+    release_region(THS, THS_IO_MEM_SIZE);
+
+    return err;
+}
+
+/*
+ * Exit
+ */
+static void __exit a2x_ths_exit(void)
+{
+    platform_driver_unregister(&a2x_ths_hwmon_driver);
+    platform_device_unregister(a2x_ths_hwmon_device);
+    release_region(THS, THS_IO_MEM_SIZE);
+}
+
+module_init(a2x_ths_init);
+module_exit(a2x_ths_exit);
+
+MODULE_AUTHOR(A2X_AUTHOR);
+MODULE_DESCRIPTION(A2X_DESC);
+MODULE_LICENSE(A2X_LICENSE);
+MODULE_VERSION(A2X_VERSION);
--
2.1.0

--
You received this message because you are subscribed to the Google Groups 
"linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to