--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Soc driver for Cirrus EP93xx chips.
+ * Copyright (C) 2022 Nikita Shubin <nikita.shubin@maquefel.me>
+ *
+ * Based on a rewrite of arch/arm/mach-ep93xx/core.c
+ * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
+ * Copyright (C) 2007 Herbert Valerio Riedel <hvr@gnu.org>
+ *
+ * Thanks go to Michael Burian and Ray Lehtiniemi for their key
+ * role in the ep93xx linux community
+ */
+
+#include <linux/soc/cirrus/ep93xx.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+
+#define EP93XX_SYSCON_DEVCFG 0x80
+
+#define EP93XX_SWLOCK_MAGICK 0xaa
+#define EP93XX_SYSCON_SWLOCK 0xc0
+#define EP93XX_SYSCON_SYSCFG 0x9c
+#define EP93XX_SYSCON_SYSCFG_REV_MASK (0xf0000000)
+#define EP93XX_SYSCON_SYSCFG_REV_SHIFT (28)
+
+static struct regmap *map;
+
+/* EP93xx System Controller software locked register write */
+void ep93xx_syscon_swlocked_write(unsigned int val, unsigned int reg)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+
+ regmap_write(map, EP93XX_SYSCON_SWLOCK, EP93XX_SWLOCK_MAGICK);
+ regmap_write(map, reg, val);
+
+ local_irq_restore(flags);
+}
+EXPORT_SYMBOL_GPL(ep93xx_syscon_swlocked_write);
+
+void ep93xx_devcfg_set_clear(unsigned int set_bits, unsigned int clear_bits)
+{
+ unsigned long flags;
+ unsigned int val;
+
+ local_irq_save(flags);
+
+ regmap_read(map, EP93XX_SYSCON_DEVCFG, &val);
+ val &= ~clear_bits;
+ val |= set_bits;
+ regmap_write(map, EP93XX_SYSCON_SWLOCK, EP93XX_SWLOCK_MAGICK);
+ regmap_write(map, EP93XX_SYSCON_DEVCFG, val);
+
+ local_irq_restore(flags);
+}
+EXPORT_SYMBOL_GPL(ep93xx_devcfg_set_clear);
+
+void ep93xx_swlocked_update_bits(unsigned int reg,
+ unsigned int mask,
+ unsigned int val)
+{
+ unsigned long flags;
+ unsigned int tmp, orig;
+
+ local_irq_save(flags);
+
+ regmap_read(map, EP93XX_SYSCON_DEVCFG, &orig);
+ tmp = orig & ~mask;
+ tmp |= val & mask;
+ if (tmp != orig) {
+ regmap_write(map, EP93XX_SYSCON_SWLOCK, EP93XX_SWLOCK_MAGICK);
+ regmap_write(map, reg, tmp);
+ }
+
+ local_irq_restore(flags);
+}
+EXPORT_SYMBOL_GPL(ep93xx_swlocked_update_bits);
+
+/**
+ * ep93xx_chip_revision() - returns the EP93xx chip revision
+ *
+ */
+unsigned int ep93xx_chip_revision(void)
+{
+ unsigned int val;
+
+ regmap_read(map, EP93XX_SYSCON_SYSCFG, &val);
+ val &= EP93XX_SYSCON_SYSCFG_REV_MASK;
+ val >>= EP93XX_SYSCON_SYSCFG_REV_SHIFT;
+ return val;
+}
+EXPORT_SYMBOL_GPL(ep93xx_chip_revision);
+
+static const char __init *ep93xx_get_soc_rev(void)
+{
+ int rev = ep93xx_chip_revision();
+
+ switch (rev) {
+ case EP93XX_CHIP_REV_D0:
+ return "D0";
+ case EP93XX_CHIP_REV_D1:
+ return "D1";
+ case EP93XX_CHIP_REV_E0:
+ return "E0";
+ case EP93XX_CHIP_REV_E1:
+ return "E1";
+ case EP93XX_CHIP_REV_E2:
+ return "E2";
+ default:
+ return "unknown";
+ }
+}
+
+static int __init ep93xx_soc_init(void)
+{
+ /* Multiplatform guard, only proceed on ep93xx */
+ if (!of_machine_is_compatible("cirrus,ep9301"))
+ return 0;
+
+ map = syscon_regmap_lookup_by_compatible("cirrus,ep9301-syscon");
+ if (IS_ERR(map))
+ return PTR_ERR(map);
+
+ pr_info("EP93xx SoC revision %s\n", ep93xx_get_soc_rev());
+
+ return 0;
+}
+
+core_initcall(ep93xx_soc_init);
+
#define EP93XX_CHIP_REV_E1 6
#define EP93XX_CHIP_REV_E2 7
-#ifdef CONFIG_ARCH_EP93XX
+#if defined(CONFIG_EP93XX_SOC_COMMON)
int ep93xx_pwm_acquire_gpio(struct platform_device *pdev);
void ep93xx_pwm_release_gpio(struct platform_device *pdev);
int ep93xx_ide_acquire_gpio(struct platform_device *pdev);
void ep93xx_keypad_release_gpio(struct platform_device *pdev);
int ep93xx_i2s_acquire(void);
void ep93xx_i2s_release(void);
-unsigned int ep93xx_chip_revision(void);
-
#else
static inline int ep93xx_pwm_acquire_gpio(struct platform_device *pdev) { return 0; }
static inline void ep93xx_pwm_release_gpio(struct platform_device *pdev) {}
static inline void ep93xx_keypad_release_gpio(struct platform_device *pdev) {}
static inline int ep93xx_i2s_acquire(void) { return 0; }
static inline void ep93xx_i2s_release(void) {}
-static inline unsigned int ep93xx_chip_revision(void) { return 0; }
+#endif
+#if defined(CONFIG_ARCH_EP93XX)
+unsigned int ep93xx_chip_revision(void);
+#if defined(CONFIG_EP93XX_SOC)
+void ep93xx_devcfg_set_clear(unsigned int set_bits, unsigned int clear_bits);
+void ep93xx_syscon_swlocked_write(unsigned int val, unsigned int reg);
+void ep93xx_swlocked_update_bits(unsigned int reg,
+ unsigned int mask, unsigned int val);
+#endif
+#else
+static inline unsigned int ep93xx_chip_revision(void) { return 0; }
#endif
#endif