Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/torvalds/linux.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/soc/samsung/exynos-chipid.yaml (renamed from Documentation/devicetree/bindings/arm/samsung/exynos-chipid.yaml)2
-rw-r--r--Documentation/devicetree/bindings/soc/samsung/exynos-pmu.yaml (renamed from Documentation/devicetree/bindings/arm/samsung/pmu.yaml)4
-rw-r--r--Documentation/devicetree/bindings/soc/samsung/exynos-usi.yaml159
-rw-r--r--MAINTAINERS1
-rw-r--r--drivers/soc/samsung/Kconfig14
-rw-r--r--drivers/soc/samsung/Makefile2
-rw-r--r--drivers/soc/samsung/exynos-chipid.c3
-rw-r--r--drivers/soc/samsung/exynos-pmu.c2
-rw-r--r--drivers/soc/samsung/exynos-usi.c285
-rw-r--r--include/dt-bindings/soc/samsung,exynos-usi.h17
10 files changed, 487 insertions, 2 deletions
diff --git a/Documentation/devicetree/bindings/arm/samsung/exynos-chipid.yaml b/Documentation/devicetree/bindings/soc/samsung/exynos-chipid.yaml
index bfc352a2fdd6..4bb8efb83ac1 100644
--- a/Documentation/devicetree/bindings/arm/samsung/exynos-chipid.yaml
+++ b/Documentation/devicetree/bindings/soc/samsung/exynos-chipid.yaml
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
%YAML 1.2
---
-$id: http://devicetree.org/schemas/arm/samsung/exynos-chipid.yaml#
+$id: http://devicetree.org/schemas/soc/samsung/exynos-chipid.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Samsung Exynos SoC series Chipid driver
diff --git a/Documentation/devicetree/bindings/arm/samsung/pmu.yaml b/Documentation/devicetree/bindings/soc/samsung/exynos-pmu.yaml
index 17678d9686c1..c30a6437030d 100644
--- a/Documentation/devicetree/bindings/arm/samsung/pmu.yaml
+++ b/Documentation/devicetree/bindings/soc/samsung/exynos-pmu.yaml
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
%YAML 1.2
---
-$id: http://devicetree.org/schemas/arm/samsung/pmu.yaml#
+$id: http://devicetree.org/schemas/soc/samsung/exynos-pmu.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Samsung Exynos SoC series Power Management Unit (PMU)
@@ -24,6 +24,7 @@ select:
- samsung,exynos5420-pmu
- samsung,exynos5433-pmu
- samsung,exynos7-pmu
+ - samsung,exynos850-pmu
- samsung-s5pv210-pmu
required:
- compatible
@@ -41,6 +42,7 @@ properties:
- samsung,exynos5420-pmu
- samsung,exynos5433-pmu
- samsung,exynos7-pmu
+ - samsung,exynos850-pmu
- samsung-s5pv210-pmu
- const: syscon
diff --git a/Documentation/devicetree/bindings/soc/samsung/exynos-usi.yaml b/Documentation/devicetree/bindings/soc/samsung/exynos-usi.yaml
new file mode 100644
index 000000000000..0af4821fae5e
--- /dev/null
+++ b/Documentation/devicetree/bindings/soc/samsung/exynos-usi.yaml
@@ -0,0 +1,159 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/soc/samsung/exynos-usi.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Samsung's Exynos USI (Universal Serial Interface) binding
+
+maintainers:
+ - Sam Protsenko <semen.protsenko@linaro.org>
+ - Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com>
+
+description: |
+ USI IP-core provides selectable serial protocol (UART, SPI or High-Speed I2C).
+ USI shares almost all internal circuits within each protocol, so only one
+ protocol can be chosen at a time. USI is modeled as a node with zero or more
+ child nodes, each representing a serial sub-node device. The mode setting
+ selects which particular function will be used.
+
+ Refer to next bindings documentation for information on protocol subnodes that
+ can exist under USI node:
+
+ [1] Documentation/devicetree/bindings/serial/samsung_uart.yaml
+ [2] Documentation/devicetree/bindings/i2c/i2c-exynos5.txt
+ [3] Documentation/devicetree/bindings/spi/spi-samsung.txt
+
+properties:
+ $nodename:
+ pattern: "^usi@[0-9a-f]+$"
+
+ compatible:
+ enum:
+ - samsung,exynos850-usi # for USIv2 (Exynos850, ExynosAutoV9)
+
+ reg: true
+
+ clocks: true
+
+ clock-names: true
+
+ ranges: true
+
+ "#address-cells":
+ const: 1
+
+ "#size-cells":
+ const: 1
+
+ samsung,sysreg:
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ description:
+ Should be phandle/offset pair. The phandle to System Register syscon node
+ (for the same domain where this USI controller resides) and the offset
+ of SW_CONF register for this USI controller.
+
+ samsung,mode:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ Selects USI function (which serial protocol to use). Refer to
+ <include/dt-bindings/soc/samsung,exynos-usi.h> for valid USI mode values.
+
+ samsung,clkreq-on:
+ type: boolean
+ description:
+ Enable this property if underlying protocol requires the clock to be
+ continuously provided without automatic gating. As suggested by SoC
+ manual, it should be set in case of SPI/I2C slave, UART Rx and I2C
+ multi-master mode. Usually this property is needed if USI mode is set
+ to "UART".
+
+ This property is optional.
+
+patternProperties:
+ # All other properties should be child nodes
+ "^(serial|spi|i2c)@[0-9a-f]+$":
+ type: object
+ description: Child node describing underlying USI serial protocol
+
+required:
+ - compatible
+ - ranges
+ - "#address-cells"
+ - "#size-cells"
+ - samsung,sysreg
+ - samsung,mode
+
+if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - samsung,exynos850-usi
+
+then:
+ properties:
+ reg:
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: Bus (APB) clock
+ - description: Operating clock for UART/SPI/I2C protocol
+
+ clock-names:
+ items:
+ - const: pclk
+ - const: ipclk
+
+ required:
+ - reg
+ - clocks
+ - clock-names
+
+else:
+ properties:
+ reg: false
+ clocks: false
+ clock-names: false
+ samsung,clkreq-on: false
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/soc/samsung,exynos-usi.h>
+
+ usi0: usi@138200c0 {
+ compatible = "samsung,exynos850-usi";
+ reg = <0x138200c0 0x20>;
+ samsung,sysreg = <&sysreg_peri 0x1010>;
+ samsung,mode = <USI_V2_UART>;
+ samsung,clkreq-on; /* needed for UART mode */
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+ clocks = <&cmu_peri 32>, <&cmu_peri 31>;
+ clock-names = "pclk", "ipclk";
+
+ serial_0: serial@13820000 {
+ compatible = "samsung,exynos850-uart";
+ reg = <0x13820000 0xc0>;
+ interrupts = <GIC_SPI 227 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&cmu_peri 32>, <&cmu_peri 31>;
+ clock-names = "uart", "clk_uart_baud0";
+ status = "disabled";
+ };
+
+ hsi2c_0: i2c@13820000 {
+ compatible = "samsung,exynosautov9-hsi2c";
+ reg = <0x13820000 0xc0>;
+ interrupts = <GIC_SPI 227 IRQ_TYPE_LEVEL_HIGH>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&cmu_peri 32>, <&cmu_peri 31>;
+ clock-names = "hsi2c_pclk", "hsi2c";
+ status = "disabled";
+ };
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index 5250298d2817..e2d6a021126f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2542,6 +2542,7 @@ Q: https://patchwork.kernel.org/project/linux-samsung-soc/list/
F: Documentation/arm/samsung/
F: Documentation/devicetree/bindings/arm/samsung/
F: Documentation/devicetree/bindings/power/pd-samsung.yaml
+F: Documentation/devicetree/bindings/soc/samsung/
F: arch/arm/boot/dts/exynos*
F: arch/arm/boot/dts/s3c*
F: arch/arm/boot/dts/s5p*
diff --git a/drivers/soc/samsung/Kconfig b/drivers/soc/samsung/Kconfig
index e2cedef1e8d1..a9f8b224322e 100644
--- a/drivers/soc/samsung/Kconfig
+++ b/drivers/soc/samsung/Kconfig
@@ -23,6 +23,20 @@ config EXYNOS_CHIPID
Support for Samsung Exynos SoC ChipID and Adaptive Supply Voltage.
This driver can also be built as module (exynos_chipid).
+config EXYNOS_USI
+ tristate "Exynos USI (Universal Serial Interface) driver"
+ default ARCH_EXYNOS && ARM64
+ depends on ARCH_EXYNOS || COMPILE_TEST
+ select MFD_SYSCON
+ help
+ Enable support for USI block. USI (Universal Serial Interface) is an
+ IP-core found in modern Samsung Exynos SoCs, like Exynos850 and
+ ExynosAutoV0. USI block can be configured to provide one of the
+ following serial protocols: UART, SPI or High Speed I2C.
+
+ This driver allows one to configure USI for desired protocol, which
+ is usually done in USI node in Device Tree.
+
config EXYNOS_PMU
bool "Exynos PMU controller driver" if COMPILE_TEST
depends on ARCH_EXYNOS || ((ARM || ARM64) && COMPILE_TEST)
diff --git a/drivers/soc/samsung/Makefile b/drivers/soc/samsung/Makefile
index 2ae4bea804cf..9f59d1905ab0 100644
--- a/drivers/soc/samsung/Makefile
+++ b/drivers/soc/samsung/Makefile
@@ -4,6 +4,8 @@ obj-$(CONFIG_EXYNOS_ASV_ARM) += exynos5422-asv.o
obj-$(CONFIG_EXYNOS_CHIPID) += exynos_chipid.o
exynos_chipid-y += exynos-chipid.o exynos-asv.o
+obj-$(CONFIG_EXYNOS_USI) += exynos-usi.o
+
obj-$(CONFIG_EXYNOS_PMU) += exynos-pmu.o
obj-$(CONFIG_EXYNOS_PMU_ARM_DRIVERS) += exynos3250-pmu.o exynos4-pmu.o \
diff --git a/drivers/soc/samsung/exynos-chipid.c b/drivers/soc/samsung/exynos-chipid.c
index a28053ec7e6a..2746d05936d3 100644
--- a/drivers/soc/samsung/exynos-chipid.c
+++ b/drivers/soc/samsung/exynos-chipid.c
@@ -42,6 +42,7 @@ static const struct exynos_soc_id {
unsigned int id;
} soc_ids[] = {
/* List ordered by SoC name */
+ /* Compatible with: samsung,exynos4210-chipid */
{ "EXYNOS3250", 0xE3472000 },
{ "EXYNOS4210", 0x43200000 }, /* EVT0 revision */
{ "EXYNOS4210", 0x43210000 },
@@ -55,6 +56,8 @@ static const struct exynos_soc_id {
{ "EXYNOS5440", 0xE5440000 },
{ "EXYNOS5800", 0xE5422000 },
{ "EXYNOS7420", 0xE7420000 },
+ /* Compatible with: samsung,exynos850-chipid */
+ { "EXYNOS7885", 0xE7885000 },
{ "EXYNOS850", 0xE3830000 },
{ "EXYNOSAUTOV9", 0xAAA80000 },
};
diff --git a/drivers/soc/samsung/exynos-pmu.c b/drivers/soc/samsung/exynos-pmu.c
index a18c93a4646c..732c86ce2be8 100644
--- a/drivers/soc/samsung/exynos-pmu.c
+++ b/drivers/soc/samsung/exynos-pmu.c
@@ -94,6 +94,8 @@ static const struct of_device_id exynos_pmu_of_device_ids[] = {
.compatible = "samsung,exynos5433-pmu",
}, {
.compatible = "samsung,exynos7-pmu",
+ }, {
+ .compatible = "samsung,exynos850-pmu",
},
{ /*sentinel*/ },
};
diff --git a/drivers/soc/samsung/exynos-usi.c b/drivers/soc/samsung/exynos-usi.c
new file mode 100644
index 000000000000..114352695ac2
--- /dev/null
+++ b/drivers/soc/samsung/exynos-usi.c
@@ -0,0 +1,285 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021 Linaro Ltd.
+ * Author: Sam Protsenko <semen.protsenko@linaro.org>
+ *
+ * Samsung Exynos USI driver (Universal Serial Interface).
+ */
+
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include <dt-bindings/soc/samsung,exynos-usi.h>
+
+/* USIv2: System Register: SW_CONF register bits */
+#define USI_V2_SW_CONF_NONE 0x0
+#define USI_V2_SW_CONF_UART BIT(0)
+#define USI_V2_SW_CONF_SPI BIT(1)
+#define USI_V2_SW_CONF_I2C BIT(2)
+#define USI_V2_SW_CONF_MASK (USI_V2_SW_CONF_UART | USI_V2_SW_CONF_SPI | \
+ USI_V2_SW_CONF_I2C)
+
+/* USIv2: USI register offsets */
+#define USI_CON 0x04
+#define USI_OPTION 0x08
+
+/* USIv2: USI register bits */
+#define USI_CON_RESET BIT(0)
+#define USI_OPTION_CLKREQ_ON BIT(1)
+#define USI_OPTION_CLKSTOP_ON BIT(2)
+
+enum exynos_usi_ver {
+ USI_VER2 = 2,
+};
+
+struct exynos_usi_variant {
+ enum exynos_usi_ver ver; /* USI IP-core version */
+ unsigned int sw_conf_mask; /* SW_CONF mask for all protocols */
+ size_t min_mode; /* first index in exynos_usi_modes[] */
+ size_t max_mode; /* last index in exynos_usi_modes[] */
+ size_t num_clks; /* number of clocks to assert */
+ const char * const *clk_names; /* clock names to assert */
+};
+
+struct exynos_usi {
+ struct device *dev;
+ void __iomem *regs; /* USI register map */
+ struct clk_bulk_data *clks; /* USI clocks */
+
+ size_t mode; /* current USI SW_CONF mode index */
+ bool clkreq_on; /* always provide clock to IP */
+
+ /* System Register */
+ struct regmap *sysreg; /* System Register map */
+ unsigned int sw_conf; /* SW_CONF register offset in sysreg */
+
+ const struct exynos_usi_variant *data;
+};
+
+struct exynos_usi_mode {
+ const char *name; /* mode name */
+ unsigned int val; /* mode register value */
+};
+
+static const struct exynos_usi_mode exynos_usi_modes[] = {
+ [USI_V2_NONE] = { .name = "none", .val = USI_V2_SW_CONF_NONE },
+ [USI_V2_UART] = { .name = "uart", .val = USI_V2_SW_CONF_UART },
+ [USI_V2_SPI] = { .name = "spi", .val = USI_V2_SW_CONF_SPI },
+ [USI_V2_I2C] = { .name = "i2c", .val = USI_V2_SW_CONF_I2C },
+};
+
+static const char * const exynos850_usi_clk_names[] = { "pclk", "ipclk" };
+static const struct exynos_usi_variant exynos850_usi_data = {
+ .ver = USI_VER2,
+ .sw_conf_mask = USI_V2_SW_CONF_MASK,
+ .min_mode = USI_V2_NONE,
+ .max_mode = USI_V2_I2C,
+ .num_clks = ARRAY_SIZE(exynos850_usi_clk_names),
+ .clk_names = exynos850_usi_clk_names,
+};
+
+static const struct of_device_id exynos_usi_dt_match[] = {
+ {
+ .compatible = "samsung,exynos850-usi",
+ .data = &exynos850_usi_data,
+ },
+ { } /* sentinel */
+};
+MODULE_DEVICE_TABLE(of, exynos_usi_dt_match);
+
+/**
+ * exynos_usi_set_sw_conf - Set USI block configuration mode
+ * @usi: USI driver object
+ * @mode: Mode index
+ *
+ * Select underlying serial protocol (UART/SPI/I2C) in USI IP-core.
+ *
+ * Return: 0 on success, or negative error code on failure.
+ */
+static int exynos_usi_set_sw_conf(struct exynos_usi *usi, size_t mode)
+{
+ unsigned int val;
+ int ret;
+
+ if (mode < usi->data->min_mode || mode > usi->data->max_mode)
+ return -EINVAL;
+
+ val = exynos_usi_modes[mode].val;
+ ret = regmap_update_bits(usi->sysreg, usi->sw_conf,
+ usi->data->sw_conf_mask, val);
+ if (ret)
+ return ret;
+
+ usi->mode = mode;
+ dev_dbg(usi->dev, "protocol: %s\n", exynos_usi_modes[usi->mode].name);
+
+ return 0;
+}
+
+/**
+ * exynos_usi_enable - Initialize USI block
+ * @usi: USI driver object
+ *
+ * USI IP-core start state is "reset" (on startup and after CPU resume). This
+ * routine enables the USI block by clearing the reset flag. It also configures
+ * HWACG behavior (needed e.g. for UART Rx). It should be performed before
+ * underlying protocol becomes functional.
+ *
+ * Return: 0 on success, or negative error code on failure.
+ */
+static int exynos_usi_enable(const struct exynos_usi *usi)
+{
+ u32 val;
+ int ret;
+
+ ret = clk_bulk_prepare_enable(usi->data->num_clks, usi->clks);
+ if (ret)
+ return ret;
+
+ /* Enable USI block */
+ val = readl(usi->regs + USI_CON);
+ val &= ~USI_CON_RESET;
+ writel(val, usi->regs + USI_CON);
+ udelay(1);
+
+ /* Continuously provide the clock to USI IP w/o gating */
+ if (usi->clkreq_on) {
+ val = readl(usi->regs + USI_OPTION);
+ val &= ~USI_OPTION_CLKSTOP_ON;
+ val |= USI_OPTION_CLKREQ_ON;
+ writel(val, usi->regs + USI_OPTION);
+ }
+
+ clk_bulk_disable_unprepare(usi->data->num_clks, usi->clks);
+
+ return ret;
+}
+
+static int exynos_usi_configure(struct exynos_usi *usi)
+{
+ int ret;
+
+ ret = exynos_usi_set_sw_conf(usi, usi->mode);
+ if (ret)
+ return ret;
+
+ if (usi->data->ver == USI_VER2)
+ return exynos_usi_enable(usi);
+
+ return 0;
+}
+
+static int exynos_usi_parse_dt(struct device_node *np, struct exynos_usi *usi)
+{
+ int ret;
+ u32 mode;
+
+ ret = of_property_read_u32(np, "samsung,mode", &mode);
+ if (ret)
+ return ret;
+ if (mode < usi->data->min_mode || mode > usi->data->max_mode)
+ return -EINVAL;
+ usi->mode = mode;
+
+ usi->sysreg = syscon_regmap_lookup_by_phandle(np, "samsung,sysreg");
+ if (IS_ERR(usi->sysreg))
+ return PTR_ERR(usi->sysreg);
+
+ ret = of_property_read_u32_index(np, "samsung,sysreg", 1,
+ &usi->sw_conf);
+ if (ret)
+ return ret;
+
+ usi->clkreq_on = of_property_read_bool(np, "samsung,clkreq-on");
+
+ return 0;
+}
+
+static int exynos_usi_get_clocks(struct exynos_usi *usi)
+{
+ const size_t num = usi->data->num_clks;
+ struct device *dev = usi->dev;
+ size_t i;
+
+ if (num == 0)
+ return 0;
+
+ usi->clks = devm_kcalloc(dev, num, sizeof(*usi->clks), GFP_KERNEL);
+ if (!usi->clks)
+ return -ENOMEM;
+
+ for (i = 0; i < num; ++i)
+ usi->clks[i].id = usi->data->clk_names[i];
+
+ return devm_clk_bulk_get(dev, num, usi->clks);
+}
+
+static int exynos_usi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct exynos_usi *usi;
+ int ret;
+
+ usi = devm_kzalloc(dev, sizeof(*usi), GFP_KERNEL);
+ if (!usi)
+ return -ENOMEM;
+
+ usi->dev = dev;
+ platform_set_drvdata(pdev, usi);
+
+ usi->data = of_device_get_match_data(dev);
+ if (!usi->data)
+ return -EINVAL;
+
+ ret = exynos_usi_parse_dt(np, usi);
+ if (ret)
+ return ret;
+
+ ret = exynos_usi_get_clocks(usi);
+ if (ret)
+ return ret;
+
+ if (usi->data->ver == USI_VER2) {
+ usi->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(usi->regs))
+ return PTR_ERR(usi->regs);
+ }
+
+ ret = exynos_usi_configure(usi);
+ if (ret)
+ return ret;
+
+ /* Make it possible to embed protocol nodes into USI np */
+ return of_platform_populate(np, NULL, NULL, dev);
+}
+
+static int __maybe_unused exynos_usi_resume_noirq(struct device *dev)
+{
+ struct exynos_usi *usi = dev_get_drvdata(dev);
+
+ return exynos_usi_configure(usi);
+}
+
+static const struct dev_pm_ops exynos_usi_pm = {
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(NULL, exynos_usi_resume_noirq)
+};
+
+static struct platform_driver exynos_usi_driver = {
+ .driver = {
+ .name = "exynos-usi",
+ .pm = &exynos_usi_pm,
+ .of_match_table = exynos_usi_dt_match,
+ },
+ .probe = exynos_usi_probe,
+};
+module_platform_driver(exynos_usi_driver);
+
+MODULE_DESCRIPTION("Samsung USI driver");
+MODULE_AUTHOR("Sam Protsenko <semen.protsenko@linaro.org>");
+MODULE_LICENSE("GPL");
diff --git a/include/dt-bindings/soc/samsung,exynos-usi.h b/include/dt-bindings/soc/samsung,exynos-usi.h
new file mode 100644
index 000000000000..a01af169d249
--- /dev/null
+++ b/include/dt-bindings/soc/samsung,exynos-usi.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+/*
+ * Copyright (c) 2021 Linaro Ltd.
+ * Author: Sam Protsenko <semen.protsenko@linaro.org>
+ *
+ * Device Tree bindings for Samsung Exynos USI (Universal Serial Interface).
+ */
+
+#ifndef __DT_BINDINGS_SAMSUNG_EXYNOS_USI_H
+#define __DT_BINDINGS_SAMSUNG_EXYNOS_USI_H
+
+#define USI_V2_NONE 0
+#define USI_V2_UART 1
+#define USI_V2_SPI 2
+#define USI_V2_I2C 3
+
+#endif /* __DT_BINDINGS_SAMSUNG_EXYNOS_USI_H */