diff options
| -rw-r--r-- | Documentation/devicetree/bindings/nvmem/google,gs101-otp.yaml | 61 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/soc/samsung/exynos-pmu.yaml | 23 | ||||
| -rw-r--r-- | drivers/soc/samsung/exynos-chipid.c | 133 | ||||
| -rw-r--r-- | include/linux/platform_data/hwmon-s3c.h | 36 |
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, ®_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__ */ |
