summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/nvmem/google,gs101-otp.yaml61
-rw-r--r--Documentation/devicetree/bindings/soc/samsung/exynos-pmu.yaml23
-rw-r--r--drivers/soc/samsung/exynos-chipid.c133
-rw-r--r--include/linux/platform_data/hwmon-s3c.h36
4 files changed, 154 insertions, 99 deletions
diff --git a/Documentation/devicetree/bindings/nvmem/google,gs101-otp.yaml b/Documentation/devicetree/bindings/nvmem/google,gs101-otp.yaml
new file mode 100644
index 000000000000..99e322c72f9e
--- /dev/null
+++ b/Documentation/devicetree/bindings/nvmem/google,gs101-otp.yaml
@@ -0,0 +1,61 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/nvmem/google,gs101-otp.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Google GS101 OTP Controller
+
+maintainers:
+ - Tudor Ambarus <tudor.ambarus@linaro.org>
+
+description: |
+ OTP controller drives a NVMEM memory where system or user specific data
+ can be stored. The OTP controller register space is of interest as well
+ because it contains dedicated registers where it stores the Product ID
+ and the Chip ID (apart other things like TMU or ASV info).
+
+allOf:
+ - $ref: nvmem.yaml#
+
+properties:
+ compatible:
+ items:
+ - const: google,gs101-otp
+
+ clocks:
+ maxItems: 1
+
+ clock-names:
+ const: pclk
+
+ interrupts:
+ maxItems: 1
+
+ reg:
+ maxItems: 1
+
+ power-domains:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+ - interrupts
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/google,gs101.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+ efuse@10000000 {
+ compatible = "google,gs101-otp";
+ reg = <0x10000000 0xf084>;
+ clocks = <&cmu_misc CLK_GOUT_MISC_OTP_CON_TOP_PCLK>;
+ clock-names = "pclk";
+ interrupts = <GIC_SPI 752 IRQ_TYPE_LEVEL_HIGH>;
+ };
diff --git a/Documentation/devicetree/bindings/soc/samsung/exynos-pmu.yaml b/Documentation/devicetree/bindings/soc/samsung/exynos-pmu.yaml
index 6de47489ee42..a6bb3093b10a 100644
--- a/Documentation/devicetree/bindings/soc/samsung/exynos-pmu.yaml
+++ b/Documentation/devicetree/bindings/soc/samsung/exynos-pmu.yaml
@@ -9,28 +9,6 @@ title: Samsung Exynos SoC series Power Management Unit (PMU)
maintainers:
- Krzysztof Kozlowski <krzk@kernel.org>
-# Custom select to avoid matching all nodes with 'syscon'
-select:
- properties:
- compatible:
- contains:
- enum:
- - google,gs101-pmu
- - samsung,exynos3250-pmu
- - samsung,exynos4210-pmu
- - samsung,exynos4212-pmu
- - samsung,exynos4412-pmu
- - samsung,exynos5250-pmu
- - samsung,exynos5260-pmu
- - samsung,exynos5410-pmu
- - samsung,exynos5420-pmu
- - samsung,exynos5433-pmu
- - samsung,exynos7-pmu
- - samsung,exynos850-pmu
- - samsung-s5pv210-pmu
- required:
- - compatible
-
properties:
compatible:
oneOf:
@@ -52,6 +30,7 @@ properties:
- const: syscon
- items:
- enum:
+ - axis,artpec9-pmu
- samsung,exynos2200-pmu
- samsung,exynos7870-pmu
- samsung,exynos7885-pmu
diff --git a/drivers/soc/samsung/exynos-chipid.c b/drivers/soc/samsung/exynos-chipid.c
index d3b4b5508e0c..6ef9751e2509 100644
--- a/drivers/soc/samsung/exynos-chipid.c
+++ b/drivers/soc/samsung/exynos-chipid.c
@@ -14,7 +14,9 @@
#include <linux/array_size.h>
#include <linux/device.h>
-#include <linux/errno.h>
+#include <linux/device/devres.h>
+#include <linux/err.h>
+#include <linux/ioport.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
@@ -27,9 +29,11 @@
#include "exynos-asv.h"
struct exynos_chipid_variant {
- unsigned int rev_reg; /* revision register offset */
+ unsigned int main_rev_reg; /* main revision register offset */
+ unsigned int sub_rev_reg; /* sub revision register offset */
unsigned int main_rev_shift; /* main revision offset in rev_reg */
unsigned int sub_rev_shift; /* sub revision offset in rev_reg */
+ bool efuse;
};
struct exynos_chipid_info {
@@ -68,9 +72,11 @@ static const struct exynos_soc_id {
{ "EXYNOS990", 0xE9830000 },
{ "EXYNOSAUTOV9", 0xAAA80000 },
{ "EXYNOSAUTOV920", 0x0A920000 },
+ /* Compatible with: google,gs101-otp */
+ { "GS101", 0x9845000 },
};
-static const char *product_id_to_soc_id(unsigned int product_id)
+static const char *exynos_product_id_to_name(unsigned int product_id)
{
int i;
@@ -80,8 +86,8 @@ static const char *product_id_to_soc_id(unsigned int product_id)
return NULL;
}
-static int exynos_chipid_get_chipid_info(struct regmap *regmap,
- const struct exynos_chipid_variant *data,
+static int exynos_chipid_get_chipid_info(struct device *dev,
+ struct regmap *regmap, const struct exynos_chipid_variant *data,
struct exynos_chipid_info *soc_info)
{
int ret;
@@ -89,21 +95,61 @@ static int exynos_chipid_get_chipid_info(struct regmap *regmap,
ret = regmap_read(regmap, EXYNOS_CHIPID_REG_PRO_ID, &val);
if (ret < 0)
- return ret;
+ return dev_err_probe(dev, ret, "failed to read Product ID\n");
soc_info->product_id = val & EXYNOS_MASK;
- if (data->rev_reg != EXYNOS_CHIPID_REG_PRO_ID) {
- ret = regmap_read(regmap, data->rev_reg, &val);
+ if (data->sub_rev_reg == EXYNOS_CHIPID_REG_PRO_ID) {
+ /* exynos4210 case */
+ main_rev = (val >> data->main_rev_shift) & EXYNOS_REV_PART_MASK;
+ sub_rev = (val >> data->sub_rev_shift) & EXYNOS_REV_PART_MASK;
+ } else {
+ unsigned int val2;
+
+ ret = regmap_read(regmap, data->sub_rev_reg, &val2);
if (ret < 0)
- return ret;
+ return dev_err_probe(dev, ret,
+ "failed to read revision\n");
+
+ if (data->main_rev_reg == EXYNOS_CHIPID_REG_PRO_ID)
+ /* gs101 case */
+ main_rev = (val >> data->main_rev_shift) & EXYNOS_REV_PART_MASK;
+ else
+ /* exynos850 case */
+ main_rev = (val2 >> data->main_rev_shift) & EXYNOS_REV_PART_MASK;
+
+ sub_rev = (val2 >> data->sub_rev_shift) & EXYNOS_REV_PART_MASK;
}
- main_rev = (val >> data->main_rev_shift) & EXYNOS_REV_PART_MASK;
- sub_rev = (val >> data->sub_rev_shift) & EXYNOS_REV_PART_MASK;
+
soc_info->revision = (main_rev << EXYNOS_REV_PART_SHIFT) | sub_rev;
return 0;
}
+static struct regmap *exynos_chipid_get_efuse_regmap(struct platform_device *pdev)
+{
+ struct resource *res;
+ void __iomem *base;
+
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(base))
+ return ERR_CAST(base);
+
+ const struct regmap_config reg_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .use_relaxed_mmio = true,
+ .max_register = (resource_size(res) - reg_config.reg_stride),
+ };
+
+ return devm_regmap_init_mmio_clk(&pdev->dev, "pclk", base, &reg_config);
+}
+
+static void exynos_chipid_unregister_soc(void *data)
+{
+ soc_device_unregister(data);
+}
+
static int exynos_chipid_probe(struct platform_device *pdev)
{
const struct exynos_chipid_variant *drv_data;
@@ -117,13 +163,19 @@ static int exynos_chipid_probe(struct platform_device *pdev)
drv_data = of_device_get_match_data(dev);
if (!drv_data)
- return -EINVAL;
+ return dev_err_probe(dev, -EINVAL,
+ "failed to get match data\n");
+
+ if (drv_data->efuse)
+ regmap = exynos_chipid_get_efuse_regmap(pdev);
+ else
+ regmap = device_node_to_regmap(dev->of_node);
- regmap = device_node_to_regmap(dev->of_node);
if (IS_ERR(regmap))
- return PTR_ERR(regmap);
+ return dev_err_probe(dev, PTR_ERR(regmap),
+ "failed to get regmap\n");
- ret = exynos_chipid_get_chipid_info(regmap, drv_data, &soc_info);
+ ret = exynos_chipid_get_chipid_info(dev, regmap, drv_data, &soc_info);
if (ret < 0)
return ret;
@@ -141,55 +193,55 @@ static int exynos_chipid_probe(struct platform_device *pdev)
soc_info.revision);
if (!soc_dev_attr->revision)
return -ENOMEM;
- soc_dev_attr->soc_id = product_id_to_soc_id(soc_info.product_id);
- if (!soc_dev_attr->soc_id) {
- pr_err("Unknown SoC\n");
- return -ENODEV;
- }
+
+ soc_dev_attr->soc_id = exynos_product_id_to_name(soc_info.product_id);
+ if (!soc_dev_attr->soc_id)
+ return dev_err_probe(dev, -ENODEV, "Unknown SoC\n");
/* please note that the actual registration will be deferred */
soc_dev = soc_device_register(soc_dev_attr);
if (IS_ERR(soc_dev))
- return PTR_ERR(soc_dev);
+ return dev_err_probe(dev, PTR_ERR(soc_dev),
+ "failed to register to the soc interface\n");
- ret = exynos_asv_init(dev, regmap);
+ ret = devm_add_action_or_reset(dev, exynos_chipid_unregister_soc,
+ soc_dev);
if (ret)
- goto err;
+ return dev_err_probe(dev, ret, "failed to add devm action\n");
- platform_set_drvdata(pdev, soc_dev);
+ ret = exynos_asv_init(dev, regmap);
+ if (ret)
+ return ret;
- dev_info(dev, "Exynos: CPU[%s] PRO_ID[0x%x] REV[0x%x] Detected\n",
- soc_dev_attr->soc_id, soc_info.product_id, soc_info.revision);
+ dev_dbg(dev, "Exynos: CPU[%s] PRO_ID[0x%x] REV[0x%x] Detected\n",
+ soc_dev_attr->soc_id, soc_info.product_id, soc_info.revision);
return 0;
-
-err:
- soc_device_unregister(soc_dev);
-
- return ret;
-}
-
-static void exynos_chipid_remove(struct platform_device *pdev)
-{
- struct soc_device *soc_dev = platform_get_drvdata(pdev);
-
- soc_device_unregister(soc_dev);
}
static const struct exynos_chipid_variant exynos4210_chipid_drv_data = {
- .rev_reg = 0x0,
.main_rev_shift = 4,
.sub_rev_shift = 0,
};
static const struct exynos_chipid_variant exynos850_chipid_drv_data = {
- .rev_reg = 0x10,
+ .main_rev_reg = 0x10,
+ .sub_rev_reg = 0x10,
.main_rev_shift = 20,
.sub_rev_shift = 16,
};
+static const struct exynos_chipid_variant gs101_chipid_drv_data = {
+ .sub_rev_reg = 0x10,
+ .sub_rev_shift = 16,
+ .efuse = true,
+};
+
static const struct of_device_id exynos_chipid_of_device_ids[] = {
{
+ .compatible = "google,gs101-otp",
+ .data = &gs101_chipid_drv_data,
+ }, {
.compatible = "samsung,exynos4210-chipid",
.data = &exynos4210_chipid_drv_data,
}, {
@@ -206,7 +258,6 @@ static struct platform_driver exynos_chipid_driver = {
.of_match_table = exynos_chipid_of_device_ids,
},
.probe = exynos_chipid_probe,
- .remove = exynos_chipid_remove,
};
module_platform_driver(exynos_chipid_driver);
diff --git a/include/linux/platform_data/hwmon-s3c.h b/include/linux/platform_data/hwmon-s3c.h
deleted file mode 100644
index 7d21e0c41037..000000000000
--- a/include/linux/platform_data/hwmon-s3c.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * Copyright 2005 Simtec Electronics
- * Ben Dooks <ben@simtec.co.uk>
- * http://armlinux.simtec.co.uk/
- *
- * S3C - HWMon interface for ADC
-*/
-
-#ifndef __HWMON_S3C_H__
-#define __HWMON_S3C_H__
-
-/**
- * s3c_hwmon_chcfg - channel configuration
- * @name: The name to give this channel.
- * @mult: Multiply the ADC value read by this.
- * @div: Divide the value from the ADC by this.
- *
- * The value read from the ADC is converted to a value that
- * hwmon expects (mV) by result = (value_read * @mult) / @div.
- */
-struct s3c_hwmon_chcfg {
- const char *name;
- unsigned int mult;
- unsigned int div;
-};
-
-/**
- * s3c_hwmon_pdata - HWMON platform data
- * @in: One configuration for each possible channel used.
- */
-struct s3c_hwmon_pdata {
- struct s3c_hwmon_chcfg *in[8];
-};
-
-#endif /* __HWMON_S3C_H__ */