From 081804a132de6802f64daf8af6704b21abeb86eb Mon Sep 17 00:00:00 2001
From: Jan Luebbe <jlu@pengutronix.de>
Date: Sun, 21 Jul 2013 00:27:27 +0200
Subject: [PATCH 21/23] hwmon: add driver for the AM335x bandgap temperature
 sensor

Signed-off-by: Jan Luebbe <jlu@pengutronix.de>
---
 Documentation/devicetree/bindings/hwmon/am335x-bandgap.txt |   12 
 arch/arm/boot/dts/am33xx.dtsi                              |    5 
 drivers/hwmon/Kconfig                                      |    7 
 drivers/hwmon/Makefile                                     |    1 
 drivers/hwmon/am335x-bandgap.c                             |  164 +++++++++++++
 5 files changed, 189 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/hwmon/am335x-bandgap.txt
 create mode 100644 drivers/hwmon/am335x-bandgap.c

Index: linux-3.12.24-rt38-r8s8/Documentation/devicetree/bindings/hwmon/am335x-bandgap.txt
===================================================================
--- /dev/null
+++ linux-3.12.24-rt38-r8s8/Documentation/devicetree/bindings/hwmon/am335x-bandgap.txt
@@ -0,0 +1,12 @@
+TI AM335x bandgap temperature sensor
+------------------------------------
+
+Requires node properties:
+- compatible: Should be "ti,am335x-bandgap"
+- reg: Should contain registers location and length
+
+Example:
+       bandgap@44e10448 {
+               compatible = "ti,am335x-bandgap";
+               reg = <0x44e10448 0x8>;
+       };
Index: linux-3.12.24-rt38-r8s8/arch/arm/boot/dts/am33xx.dtsi
===================================================================
--- linux-3.12.24-rt38-r8s8.orig/arch/arm/boot/dts/am33xx.dtsi
+++ linux-3.12.24-rt38-r8s8/arch/arm/boot/dts/am33xx.dtsi
@@ -756,6 +756,11 @@
                        interrupts = <37>;
                        resets = <&prcm 0>;
                };
+
+               bandgap@44e10448 {
+                       compatible = "ti,am335x-bandgap";
+                       reg = <0x44e10448 0x8>;
+               };
        };
 };
 
Index: linux-3.12.24-rt38-r8s8/drivers/hwmon/Kconfig
===================================================================
--- linux-3.12.24-rt38-r8s8.orig/drivers/hwmon/Kconfig
+++ linux-3.12.24-rt38-r8s8/drivers/hwmon/Kconfig
@@ -264,6 +264,13 @@ config SENSORS_ADT7475
          This driver can also be build as a module.  If so, the module
          will be called adt7475.
 
+config SENSORS_AM335X_BANDGAP
+       tristate "Texas Instruments AM335x SoC temperature sensor"
+       depends on SOC_AM33XX
+       help
+         If you say yes here you get support for the temperature
+         sensor found on the AM335x line of SoCs from Texas Instruments.
+
 config SENSORS_ASC7621
        tristate "Andigilog aSC7621"
        depends on I2C
Index: linux-3.12.24-rt38-r8s8/drivers/hwmon/Makefile
===================================================================
--- linux-3.12.24-rt38-r8s8.orig/drivers/hwmon/Makefile
+++ linux-3.12.24-rt38-r8s8/drivers/hwmon/Makefile
@@ -42,6 +42,7 @@ obj-$(CONFIG_SENSORS_ADT7411) += adt7411
 obj-$(CONFIG_SENSORS_ADT7462)  += adt7462.o
 obj-$(CONFIG_SENSORS_ADT7470)  += adt7470.o
 obj-$(CONFIG_SENSORS_ADT7475)  += adt7475.o
+obj-$(CONFIG_SENSORS_AM335X_BANDGAP) += am335x-bandgap.o
 obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o
 obj-$(CONFIG_SENSORS_ASC7621)  += asc7621.o
 obj-$(CONFIG_SENSORS_ATXP1)    += atxp1.o
Index: linux-3.12.24-rt38-r8s8/drivers/hwmon/am335x-bandgap.c
===================================================================
--- /dev/null
+++ linux-3.12.24-rt38-r8s8/drivers/hwmon/am335x-bandgap.c
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2013 Jan Luebbe <j.luebbe@pengutronix.de>
+ *
+ * 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.
+ *
+ * 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 <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+
+#define DRV_NAME       "am335x-bandgap"
+
+#define BANDGAP_CTRL                   0x0
+#define BANDGAP_CTRL_DTEMP_MASK                0x0000FF00
+#define BANDGAP_CTRL_DTEMP_OFF         8
+#define BANDGAP_CTRL_BGROFF            BIT(6)
+#define BANDGAP_CTRL_SOC               BIT(4)
+#define BANDGAP_CTRL_CLRZ              BIT(3) /* 0 = clear */
+#define BANDGAP_CTRL_CONTCONV          BIT(2)
+#define BANDGAP_CTRL_ECOZ              BIT(1)
+#define BANDGAP_CTRL_TSHUT             BIT(0)
+
+#define BANDGAP_TRIM                   0x4
+#define BANDGAP_TRIM_DTRBGAPC_MASK     0xFF000000
+#define BANDGAP_TRIM_DTRBGAPC_OFF      24
+#define BANDGAP_TRIM_DTRBGAPV_MASK     0x00FF0000
+#define BANDGAP_TRIM_DTRBGAPV_OFF      16
+#define BANDGAP_TRIM_DTRTEMPS_MASK     0x0000FF00
+#define BANDGAP_TRIM_DTRTEMPS_OFF      8
+#define BANDGAP_TRIM_DTRTEMPSC_MASK    0x000000FF
+#define BANDGAP_TRIM_DTRTEMPSC_OFF     0
+
+struct am335x_bandgap {
+       u32 __iomem *regs;
+       struct device *hwmon_dev;
+};
+
+static ssize_t show_name(struct device *dev, struct device_attribute
+                        *devattr, char *buf)
+{
+       return sprintf(buf, "%s\n", DRV_NAME);
+}
+
+static ssize_t show_input(struct device *dev,
+                         struct device_attribute *devattr, char *buf)
+{
+       struct am335x_bandgap *data = dev_get_drvdata(dev);
+       u32 val, temp;
+
+       /* read measurement */
+       val = readl(data->regs + BANDGAP_CTRL);
+
+       /* compute temperature */
+       val = (val & BANDGAP_CTRL_DTEMP_MASK) >> BANDGAP_CTRL_DTEMP_OFF;
+       temp = val * 1000;
+
+       return sprintf(buf, "%d\n", temp);
+}
+
+static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_input, NULL, 0);
+
+struct attribute *am335x_bandgap_attributes[] = {
+       &sensor_dev_attr_name.dev_attr.attr,
+       &sensor_dev_attr_temp1_input.dev_attr.attr,
+       NULL
+};
+
+static const struct attribute_group am335x_bandgap_group = {
+       .attrs = am335x_bandgap_attributes,
+};
+
+static int am335x_bandgap_probe(struct platform_device *pdev)
+{
+       struct am335x_bandgap *data;
+       struct resource *res;
+       int err;
+
+       data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res)
+               return -ENODEV;
+
+       data->regs = devm_request_and_ioremap(&pdev->dev, res);
+       if (!data->regs)
+               return -ENODEV;
+
+       platform_set_drvdata(pdev, data);
+
+       err = sysfs_create_group(&pdev->dev.kobj, &am335x_bandgap_group);
+       if (err < 0) {
+               dev_err(&pdev->dev, "Create sysfs group failed (%d)\n", err);
+               return err;
+       }
+
+       data->hwmon_dev = hwmon_device_register(&pdev->dev);
+       if (IS_ERR(data->hwmon_dev)) {
+               err = PTR_ERR(data->hwmon_dev);
+               dev_err(&pdev->dev, "Class registration failed (%d)\n", err);
+               goto exit_sysfs_group;
+       }
+
+       /* enable HW sensor */
+       writel(BANDGAP_CTRL_SOC | BANDGAP_CTRL_CLRZ | BANDGAP_CTRL_CONTCONV,
+               data->regs + BANDGAP_CTRL);
+
+       return 0;
+
+exit_sysfs_group:
+       sysfs_remove_group(&pdev->dev.kobj, &am335x_bandgap_group);
+       return err;
+}
+
+static int am335x_bandgap_remove(struct platform_device *pdev)
+{
+       struct am335x_bandgap *data = platform_get_drvdata(pdev);
+
+       /* disable HW sensor */
+       writel(0x0, data->regs + BANDGAP_CTRL);
+
+       hwmon_device_unregister(data->hwmon_dev);
+       sysfs_remove_group(&pdev->dev.kobj, &am335x_bandgap_group);
+
+       return 0;
+}
+
+static const struct of_device_id am335x_bandgap_match[] = {
+       { .compatible = "ti,am335x-bandgap" },
+       {},
+};
+
+static struct platform_driver am335x_bandgap_driver = {
+       .driver = {
+               .owner = THIS_MODULE,
+               .name = DRV_NAME,
+               .of_match_table = of_match_ptr(am335x_bandgap_match),
+       },
+       .probe  = am335x_bandgap_probe,
+       .remove = am335x_bandgap_remove,
+};
+
+module_platform_driver(am335x_bandgap_driver);
+
+MODULE_AUTHOR("Jan Luebbe <j.luebbe@pengutronix.de>");
+MODULE_DESCRIPTION("AM335x temperature sensor driver");
+MODULE_LICENSE("GPL");