Unverified Commit ee405f1a authored by Arnd Bergmann's avatar Arnd Bergmann
Browse files

Merge tag 'samsung-drivers-6.20' of...

Merge tag 'samsung-drivers-6.20' of https://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux into soc/drivers

Samsung SoC drivers for v6.20

1. Several improvements in Exynos ChipID Socinfo driver and finally
   adding Google GS101 SoC support.

2. Few cleanups from old code.

3. Documenting Axis Artpec-9 SoC PMU (Power Management Unit).

* tag 'samsung-drivers-6.20' of https://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux

:
  ARM: s3c: remove a leftover hwmon-s3c.h header file
  dt-bindings: soc: samsung: exynos-pmu: Drop unnecessary select schema
  soc: samsung: exynos-chipid: add google,gs101-otp support
  soc: samsung: exynos-chipid: downgrade dev_info to dev_dbg for soc info
  soc: samsung: exynos-chipid: rename method
  dt-bindings: nvmem: add google,gs101-otp
  soc: samsung: exynos-chipid: use dev_err_probe where appropiate
  soc: samsung: exynos-chipid: use devm action to unregister soc device
  dt-bindings: samsung: exynos-pmu: Add compatible for ARTPEC-9 SoC

Signed-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parents ee5dde7e 90013132
Loading
Loading
Loading
Loading
+61 −0
Original line number Diff line number Diff line
# 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>;
    };
+1 −22
Original line number Diff line number Diff line
@@ -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
+92 −41
Original line number Diff line number Diff line
@@ -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 (ret < 0)
			return ret;
	}
	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 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;
	}

	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);

	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",
	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);

+0 −36
Original line number Diff line number Diff line
/* 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__ */