summaryrefslogtreecommitdiffstats
path: root/drivers/soc/fsl/guts.c
diff options
context:
space:
mode:
authorKees Cook <kees+treewide@kernel.org>2026-09-02 15:31:14 -0700
committerKees Cook <kees@kernel.org>2026-09-04 21:37:00 -0700
commit3a2c4d55e32ad65efebdb6de44eef3bfa08bb49d (patch)
treec65086f9bdcd48c6360fb7cb4598bca084da1f32 /drivers/soc/fsl/guts.c
downloadlinux-stable-3a2c4d55e32ad65efebdb6de44eef3bfa08bb49d.tar.gz
linux-stable-3a2c4d55e32ad65efebdb6de44eef3bfa08bb49d.zip
treewide: refresh kmalloc_obj() conversionsgrafted
This is another run of the Coccinelle script for converting kmalloc() family of allocations to kmalloc_obj() via the existing rules in scripts/coccinelle/api/kmalloc_objs.cocci This catches both the set of kmalloc() uses added since the first kmalloc_obj() conversions in v7.0 and adds a large group missed in the first pass due to Coccinelle not interacting well with the cleanup.h scoped_...() family of macros[1]. I worked around this with spatch's "--macro-file" argument to a file with all the scoped_...() macros mapped to Coccinelle's YACFE_ITERATOR[2] as that was the closest viable control flow indicator I could find. Build tested allmodconfig on x86, arm64, arm, loongarch, mips, powerpc, riscv, and s390 with no new warnings. Link: https://lore.kernel.org/lkml/202609021314.8A9C0B8@keescook/ [1] Link: https://github.com/coccinelle/coccinelle/blob/master/standard.h [2] Signed-off-by: Kees Cook <kees+treewide@kernel.org>
Diffstat (limited to 'drivers/soc/fsl/guts.c')
-rw-r--r--drivers/soc/fsl/guts.c721
1 files changed, 721 insertions, 0 deletions
diff --git a/drivers/soc/fsl/guts.c b/drivers/soc/fsl/guts.c
new file mode 100644
index 000000000..28361c63e
--- /dev/null
+++ b/drivers/soc/fsl/guts.c
@@ -0,0 +1,721 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Freescale QorIQ Platforms GUTS Driver
+ *
+ * Copyright (C) 2016 Freescale Semiconductor, Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/of_fdt.h>
+#include <linux/sys_soc.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/fsl/guts.h>
+
+#define DCFG_CCSR 0
+#define DCFG_DCSR 1
+
+#define RCW_TIMEOUT_US 1
+
+#define LS1046A_RCWSR5_SRDS_PRTCL_S1(lane) \
+ GENMASK(19 + 4 * (lane), 16 + 4 * (lane))
+#define LS1046A_SRDS_PRTCL_XFI 1
+#define LS1046A_SRDS_PRTCL_100BASEX_SGMII 3
+
+#define LS1088A_RCWSR29_SRDS_PRTCL_S1_LNn(lane) \
+ GENMASK(19 + 4 * (3 - (lane)), 16 + 4 * (3 - (lane)))
+#define LS1088A_RCWSR30_SRDS_PRTCL_S2_LNn(lane) \
+ GENMASK(3 + 4 * (3 - (lane)), 4 * (3 - (lane)))
+#define LS1088A_SRDS_PRTCL_XFI 1
+#define LS1088A_SRDS_PRTCL_100BASEX_SGMII 3
+
+#define LS2088A_RCWSR29_SRDS_PRTCL_S1 GENMASK(23, 16)
+#define LS2088A_RCWSR30_SRDS_CLK_EN_SEL_XGMII_S1 BIT(14)
+#define LS2088A_RCWSR30_SRDS_CLK_SEL_XGMII_Ln_S1(lane) BIT(6 + (7 - (lane)))
+#define LS2088A_RCWSR30_SRDS_CLK_SEL_MSK GENMASK(13, 6)
+#define LS2088A_SRDS_CLK_SEL_XGMII 1
+#define LS2088A_SRDS_CLK_SEL_GMII 0
+
+struct fsl_soc_die_attr {
+ char *die;
+ u32 svr;
+ u32 mask;
+};
+
+struct fsl_soc_serdes_rcw_override {
+ int offset;
+ int mask;
+ int val;
+};
+
+struct fsl_soc_data {
+ const char *sfp_compat;
+ u32 uid_offset;
+ int num_serdes_blocks;
+ int num_serdes_lanes;
+ int (*serdes_init_rcwcr)(int index);
+ int (*serdes_get_rcw_override)(int index, int lane,
+ enum lynx_lane_mode lane_mode,
+ struct fsl_soc_serdes_rcw_override *override);
+};
+
+enum qoriq_die {
+ DIE_T4240,
+ DIE_T1040,
+ DIE_T2080,
+ DIE_T1024,
+ DIE_LS1043A,
+ DIE_LS2080A,
+ DIE_LS1088A,
+ DIE_LS1012A,
+ DIE_LS1046A,
+ DIE_LS2088A,
+ DIE_LS1021A,
+ DIE_LX2160A,
+ DIE_LS1028A,
+ DIE_MAX,
+};
+
+struct rcw_full_srds_proto {
+ int srds_proto_val;
+ unsigned long xgmii_lanes;
+};
+
+static const struct rcw_full_srds_proto ls2088a_rcw_full_srds1_protos[] = {
+ { 0x05, 0 },
+ { 0x07, 0 },
+ { 0x09, 0 },
+ { 0x0a, 0 },
+ { 0x0c, 0 },
+ { 0x0e, 0 },
+ { 0x10, 0 },
+ { 0x12, 0 },
+ { 0x14, 0 },
+ { 0x16, 0 },
+ { 0x18, 0 },
+ { 0x1a, 0 },
+ { 0x1c, 0 },
+ { 0x1e, 0 },
+ { 0x20, 0 },
+ { 0x22, 0 },
+ { 0x24, 0 },
+ { 0x26, GENMASK(7, 6) },
+ { 0x28, GENMASK(7, 4) },
+ { 0x2a, GENMASK(7, 0) },
+ /* 0x2b: unimplemented because of XAUI1 (lanes E-H) */
+ /* 0x2d: unimplemented because of XAUI1 (lanes E-H) */
+ /* 0x2e: unimplemented because of XAUI1 (lanes E-H) */
+ /* 0x30: unimplemented because of XAUI1 (lanes E-H) */
+ /* 0x32: unimplemented because of XAUI1 (lanes E-H) and XAUI2 (A-D) */
+ /* 0x33: unimplemented because of QSGMII (lanes E-H) */
+ /* 0x35: unimplemented because of QSGMII (lanes A-C) */
+ /* 0x37: unimplemented because of QSGMII (lanes E-F) */
+ { 0x39, 0 },
+ { 0x3b, GENMASK(6, 4) | GENMASK(2, 0) },
+ { 0x4b, GENMASK(7, 4) },
+ { 0x4c, GENMASK(3, 0) },
+ { 0x4d, 0 },
+ { 0x58, 0 },
+};
+
+/* SoC die attribute definition for QorIQ platform */
+static const struct fsl_soc_die_attr fsl_soc_die[] = {
+ /*
+ * Power Architecture-based SoCs T Series
+ */
+
+ /* Die: T4240, SoC: T4240/T4160/T4080 */
+ [DIE_T4240] =
+ { .die = "T4240",
+ .svr = 0x82400000,
+ .mask = 0xfff00000,
+ },
+ /* Die: T1040, SoC: T1040/T1020/T1042/T1022 */
+ [DIE_T1040] =
+ { .die = "T1040",
+ .svr = 0x85200000,
+ .mask = 0xfff00000,
+ },
+ /* Die: T2080, SoC: T2080/T2081 */
+ [DIE_T2080] =
+ { .die = "T2080",
+ .svr = 0x85300000,
+ .mask = 0xfff00000,
+ },
+ /* Die: T1024, SoC: T1024/T1014/T1023/T1013 */
+ [DIE_T1024] =
+ { .die = "T1024",
+ .svr = 0x85400000,
+ .mask = 0xfff00000,
+ },
+
+ /*
+ * ARM-based SoCs LS Series
+ */
+
+ /* Die: LS1043A, SoC: LS1043A/LS1023A */
+ [DIE_LS1043A] =
+ { .die = "LS1043A",
+ .svr = 0x87920000,
+ .mask = 0xffff0000,
+ },
+ /* Die: LS2080A, SoC: LS2080A/LS2040A/LS2085A */
+ [DIE_LS2080A] =
+ { .die = "LS2080A",
+ .svr = 0x87010000,
+ .mask = 0xff3f0000,
+ },
+ /* Die: LS1088A, SoC: LS1088A/LS1048A/LS1084A/LS1044A */
+ [DIE_LS1088A] =
+ { .die = "LS1088A",
+ .svr = 0x87030000,
+ .mask = 0xff3f0000,
+ },
+ /* Die: LS1012A, SoC: LS1012A */
+ [DIE_LS1012A] =
+ { .die = "LS1012A",
+ .svr = 0x87040000,
+ .mask = 0xffff0000,
+ },
+ /* Die: LS1046A, SoC: LS1046A/LS1026A */
+ [DIE_LS1046A] =
+ { .die = "LS1046A",
+ .svr = 0x87070000,
+ .mask = 0xffff0000,
+ },
+ /* Die: LS2088A, SoC: LS2088A/LS2048A/LS2084A/LS2044A */
+ [DIE_LS2088A] =
+ { .die = "LS2088A",
+ .svr = 0x87090000,
+ .mask = 0xff3f0000,
+ },
+ /* Die: LS1021A, SoC: LS1021A/LS1020A/LS1022A */
+ [DIE_LS1021A] =
+ { .die = "LS1021A",
+ .svr = 0x87000000,
+ .mask = 0xfff70000,
+ },
+ /* Die: LX2160A, SoC: LX2160A/LX2120A/LX2080A */
+ [DIE_LX2160A] =
+ { .die = "LX2160A",
+ .svr = 0x87360000,
+ .mask = 0xff3f0000,
+ },
+ /* Die: LS1028A, SoC: LS1028A */
+ [DIE_LS1028A] =
+ { .die = "LS1028A",
+ .svr = 0x870b0000,
+ .mask = 0xff3f0000,
+ },
+ { },
+};
+
+static struct fsl_soc_guts {
+ struct ccsr_guts __iomem *dcfg_ccsr;
+ struct ccsr_guts __iomem *dcfg_dcsr;
+ const struct fsl_soc_data *data;
+ bool little_endian;
+ u32 svr;
+ spinlock_t rcwcr_lock; /* serializes concurrent writes to the RCWCR */
+} soc;
+
+static unsigned int fsl_guts_read(const void __iomem *reg)
+{
+ if (soc.little_endian)
+ return ioread32(reg);
+
+ return ioread32be(reg);
+}
+
+static void fsl_guts_write(void __iomem *reg, u32 val)
+{
+ if (soc.little_endian)
+ iowrite32(val, reg);
+ else
+ iowrite32be(val, reg);
+}
+
+/* Some fields of the Reset Configuration Word (RCW) can be overridden at
+ * runtime by writing to the RCWCRn registers contained within the DCSR space
+ * of the Device Configuration (DCFG) block. The layout of the RCWCRn registers
+ * is identical with the read-only RCWSRn from the CCSR space.
+ */
+static int fsl_guts_rcw_rmw(int offset, u32 val, u32 mask)
+{
+ u32 rcwcr, rcwsr = fsl_guts_read(&soc.dcfg_ccsr->rcwsr[offset]);
+
+ rcwcr = rcwsr & ~mask;
+ rcwcr |= val;
+ fsl_guts_write(&soc.dcfg_dcsr->rcwcr[offset], rcwcr);
+
+ pr_debug("RCW override: RCWSR%d 0x%x -> RCWCR%d 0x%x\n",
+ offset + 1, rcwsr, offset + 1, rcwcr);
+
+ /* Updates to RCWCR should be visible back in RCWSR immediately */
+ return read_poll_timeout_atomic(fsl_guts_read, rcwsr, rcwsr == rcwcr,
+ 0, RCW_TIMEOUT_US, false,
+ &soc.dcfg_ccsr->rcwsr[offset]);
+}
+
+static bool fsl_soc_die_match_one(u32 svr, const struct fsl_soc_die_attr *match)
+{
+ return match->svr == (svr & match->mask);
+}
+
+static const struct fsl_soc_die_attr *fsl_soc_die_match(
+ u32 svr, const struct fsl_soc_die_attr *matches)
+{
+ while (matches->svr) {
+ if (fsl_soc_die_match_one(svr, matches))
+ return matches;
+ matches++;
+ }
+ return NULL;
+}
+
+static int
+fsl_guts_serdes_get_rcw_override(int serdes_idx, int lane,
+ enum lynx_lane_mode lane_mode,
+ struct fsl_soc_serdes_rcw_override *override)
+{
+ const struct fsl_soc_data *soc_data = soc.data;
+
+ if (!soc_data)
+ return -ENODEV;
+
+ /* serdes_idx is one-based */
+ if (serdes_idx > soc_data->num_serdes_blocks || serdes_idx <= 0)
+ return -ERANGE;
+
+ if (lane >= soc_data->num_serdes_lanes || lane < 0)
+ return -ERANGE;
+
+ if (!soc_data->serdes_get_rcw_override) {
+ pr_debug("RCW override not implemented for SoC\n");
+ return -EINVAL;
+ }
+
+ if (!soc.dcfg_dcsr) {
+ pr_debug("Device tree does not define DCFG_DCSR region necessary for RCW override\n");
+ return -EINVAL;
+ }
+
+ return soc_data->serdes_get_rcw_override(serdes_idx, lane, lane_mode,
+ override);
+}
+
+/**
+ * fsl_guts_lane_validate() - Validate that SerDes protocol is implemented and
+ * supported on current SoC
+ * @serdes_idx: one-based SerDes block index
+ * @lane: zero-based lane index within SerDes
+ * @lane_mode: requested SerDes protocol
+ *
+ * Should be called before actually requesting the RCW override procedure to be
+ * applied using %fsl_guts_lane_set_mode()
+ *
+ * Return: 0 if RCW override to protocol is possible, negative error otherwise
+ */
+int fsl_guts_lane_validate(int serdes_idx, int lane, enum lynx_lane_mode lane_mode)
+{
+ struct fsl_soc_serdes_rcw_override override;
+
+ return fsl_guts_serdes_get_rcw_override(serdes_idx, lane, lane_mode,
+ &override);
+}
+EXPORT_SYMBOL_NS_GPL(fsl_guts_lane_validate, "FSL_GUTS");
+
+/**
+ * fsl_guts_lane_set_mode() - apply RCW override procedure for SerDes lane
+ * @serdes_idx: one-based SerDes block index
+ * @lane: zero-based lane index within SerDes
+ * @lane_mode: requested SerDes protocol
+ *
+ * Return: 0 on success, negative error otherwise
+ */
+int fsl_guts_lane_set_mode(int serdes_idx, int lane, enum lynx_lane_mode lane_mode)
+{
+ struct fsl_soc_serdes_rcw_override override;
+ int err;
+
+ err = fsl_guts_serdes_get_rcw_override(serdes_idx, lane, lane_mode,
+ &override);
+ if (err)
+ return err;
+
+ spin_lock(&soc.rcwcr_lock);
+
+ if (soc.data->serdes_init_rcwcr) {
+ err = soc.data->serdes_init_rcwcr(serdes_idx);
+ if (err)
+ goto out_unlock;
+ }
+
+ err = fsl_guts_rcw_rmw(override.offset,
+ override.val << __ffs(override.mask),
+ override.mask);
+ if (err)
+ pr_err("RCW override failed: %pe\n", ERR_PTR(err));
+
+out_unlock:
+ spin_unlock(&soc.rcwcr_lock);
+
+ return err;
+}
+EXPORT_SYMBOL_NS_GPL(fsl_guts_lane_set_mode, "FSL_GUTS");
+
+static u64 fsl_guts_get_soc_uid(const char *compat, unsigned int offset)
+{
+ struct device_node *np;
+ void __iomem *sfp_base;
+ u64 uid;
+
+ np = of_find_compatible_node(NULL, NULL, compat);
+ if (!np)
+ return 0;
+
+ sfp_base = of_iomap(np, 0);
+ if (!sfp_base) {
+ of_node_put(np);
+ return 0;
+ }
+
+ uid = ioread32(sfp_base + offset);
+ uid <<= 32;
+ uid |= ioread32(sfp_base + offset + 4);
+
+ iounmap(sfp_base);
+ of_node_put(np);
+
+ return uid;
+}
+
+static int ls1046a_serdes_get_rcw_override(int index, int lane,
+ enum lynx_lane_mode lane_mode,
+ struct fsl_soc_serdes_rcw_override *override)
+{
+ /* The RCW override procedure has to write to different registers
+ * depending on the SerDes block index.
+ */
+ switch (index) {
+ case 1:
+ override->offset = 4;
+ override->mask = LS1046A_RCWSR5_SRDS_PRTCL_S1(lane);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (lynx_lane_mode_uses_xgmii_mac(lane_mode))
+ override->val = LS1046A_SRDS_PRTCL_XFI;
+ else if (lynx_lane_mode_uses_gmii_mac(lane_mode))
+ override->val = LS1046A_SRDS_PRTCL_100BASEX_SGMII;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ls1088a_serdes_get_rcw_override(int index, int lane,
+ enum lynx_lane_mode lane_mode,
+ struct fsl_soc_serdes_rcw_override *override)
+{
+ /* The RCW override procedure has to write to different registers
+ * depending on the SerDes block index.
+ */
+ switch (index) {
+ case 1:
+ override->offset = 28;
+ override->mask = LS1088A_RCWSR29_SRDS_PRTCL_S1_LNn(lane);
+ break;
+ case 2:
+ override->offset = 29;
+ override->mask = LS1088A_RCWSR30_SRDS_PRTCL_S2_LNn(lane);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (lynx_lane_mode_uses_xgmii_mac(lane_mode))
+ override->val = LS1088A_SRDS_PRTCL_XFI;
+ else if (lynx_lane_mode_uses_gmii_mac(lane_mode))
+ override->val = LS1088A_SRDS_PRTCL_100BASEX_SGMII;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+static const struct rcw_full_srds_proto *ls2088a_get_full_serdes1_proto(void)
+{
+ u32 rcwsr29 = fsl_guts_read(&soc.dcfg_ccsr->rcwsr[28]);
+ u32 srds_prtcl_s1 = FIELD_GET(LS2088A_RCWSR29_SRDS_PRTCL_S1, rcwsr29);
+
+ for (int i = 0; i < ARRAY_SIZE(ls2088a_rcw_full_srds1_protos); i++) {
+ const struct rcw_full_srds_proto *proto;
+
+ proto = &ls2088a_rcw_full_srds1_protos[i];
+ if (proto->srds_proto_val == srds_prtcl_s1)
+ return proto;
+ }
+
+ return NULL;
+}
+
+static int ls2088a_serdes_get_rcw_override(int index, int lane,
+ enum lynx_lane_mode lane_mode,
+ struct fsl_soc_serdes_rcw_override *override)
+{
+ switch (index) {
+ case 1:
+ override->offset = 29;
+ override->mask = LS2088A_RCWSR30_SRDS_CLK_SEL_XGMII_Ln_S1(lane);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* RCW override only supported if we know how to handle the initial
+ * RCWSR29[SRDS_PRTCL_S1] value and turn it into an override.
+ */
+ if (!ls2088a_get_full_serdes1_proto()) {
+ u32 rcwsr30 = fsl_guts_read(&soc.dcfg_ccsr->rcwsr[29]);
+
+ /* If a SerDes-level override is already in place (probably
+ * left there by a previous boot stage), use it.
+ */
+ if (!(rcwsr30 & LS2088A_RCWSR30_SRDS_CLK_EN_SEL_XGMII_S1))
+ return -EINVAL;
+ }
+
+ if (lynx_lane_mode_uses_xgmii_mac(lane_mode))
+ override->val = LS2088A_SRDS_CLK_SEL_XGMII;
+ else if (lynx_lane_mode_uses_gmii_mac(lane_mode))
+ override->val = LS2088A_SRDS_CLK_SEL_GMII;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ls2088a_serdes_init_rcwcr(int serdes_idx)
+{
+ const struct rcw_full_srds_proto *srds_prtcl_s1;
+ const struct fsl_soc_data *soc_data = soc.data;
+ u32 rcwsr30;
+ int i, err;
+
+ /* SerDes 2 supports only SGMII for networking. There should be
+ * no need for RCW override
+ */
+ if (serdes_idx != 1)
+ return -EINVAL;
+
+ /* SRDS_CLK_EN_SEL_XGMII_S1: SerDes Clock Enable Select XGMII Serdes 1:
+ * Enables to select GMII/XGMII clock according to
+ * SRDS_CLK_SEL_XGMII_Ln_S1.
+ * If the GMII/XGMII select override has already been set, use it.
+ * Otherwise, derive an initial override for all lanes based on the
+ * full SerDes protocol table.
+ */
+ rcwsr30 = fsl_guts_read(&soc.dcfg_ccsr->rcwsr[29]);
+ if (rcwsr30 & LS2088A_RCWSR30_SRDS_CLK_EN_SEL_XGMII_S1) {
+ pr_debug("RCWSR30 = 0x%x, using this.\n", rcwsr30);
+ return 0;
+ }
+
+ srds_prtcl_s1 = ls2088a_get_full_serdes1_proto();
+
+ rcwsr30 = LS2088A_RCWSR30_SRDS_CLK_EN_SEL_XGMII_S1;
+
+ /* We need to configure the initial state of all lanes for
+ * the SerDes block #1
+ */
+ for_each_set_bit(i, &srds_prtcl_s1->xgmii_lanes, soc_data->num_serdes_lanes)
+ rcwsr30 |= LS2088A_RCWSR30_SRDS_CLK_SEL_XGMII_Ln_S1(i);
+
+ pr_debug("Setting initial RCWSR30 = 0x%x based on SRDS_PRTCL_S1 = 0x%x\n",
+ rcwsr30, srds_prtcl_s1->srds_proto_val);
+
+ err = fsl_guts_rcw_rmw(29, rcwsr30,
+ LS2088A_RCWSR30_SRDS_CLK_EN_SEL_XGMII_S1 |
+ LS2088A_RCWSR30_SRDS_CLK_SEL_MSK);
+ if (err) {
+ pr_err("Setting up initial RCWCR failed: %pe\n", ERR_PTR(err));
+ return err;
+ }
+
+ return 0;
+}
+
+static const struct fsl_soc_data ls1088a_data = {
+ .serdes_get_rcw_override = ls1088a_serdes_get_rcw_override,
+ .num_serdes_blocks = 2,
+ .num_serdes_lanes = 4,
+};
+
+static const struct fsl_soc_data ls1046a_data = {
+ .serdes_get_rcw_override = ls1046a_serdes_get_rcw_override,
+ .num_serdes_blocks = 2,
+ .num_serdes_lanes = 4,
+};
+
+static const struct fsl_soc_data ls2088a_data = {
+ .serdes_get_rcw_override = ls2088a_serdes_get_rcw_override,
+ .serdes_init_rcwcr = ls2088a_serdes_init_rcwcr,
+ .num_serdes_blocks = 2,
+ .num_serdes_lanes = 8,
+};
+
+static const struct fsl_soc_data ls1028a_data = {
+ .sfp_compat = "fsl,ls1028a-sfp",
+ .uid_offset = 0x21c,
+ .num_serdes_blocks = 1,
+ .num_serdes_lanes = 4,
+};
+
+/*
+ * Table for matching compatible strings, for device tree
+ * guts node, for Freescale QorIQ SOCs.
+ */
+static const struct of_device_id fsl_guts_of_match[] = {
+ { .compatible = "fsl,qoriq-device-config-1.0", },
+ { .compatible = "fsl,qoriq-device-config-2.0", },
+ { .compatible = "fsl,p1010-guts", },
+ { .compatible = "fsl,p1020-guts", },
+ { .compatible = "fsl,p1021-guts", },
+ { .compatible = "fsl,p1022-guts", },
+ { .compatible = "fsl,p1023-guts", },
+ { .compatible = "fsl,p2020-guts", },
+ { .compatible = "fsl,bsc9131-guts", },
+ { .compatible = "fsl,bsc9132-guts", },
+ { .compatible = "fsl,mpc8536-guts", },
+ { .compatible = "fsl,mpc8544-guts", },
+ { .compatible = "fsl,mpc8548-guts", },
+ { .compatible = "fsl,mpc8568-guts", },
+ { .compatible = "fsl,mpc8569-guts", },
+ { .compatible = "fsl,mpc8572-guts", },
+ { .compatible = "fsl,ls1021a-dcfg", },
+ { .compatible = "fsl,ls1043a-dcfg", },
+ { .compatible = "fsl,ls2080a-dcfg", .data = &ls2088a_data},
+ { .compatible = "fsl,ls1088a-dcfg", .data = &ls1088a_data},
+ { .compatible = "fsl,ls1012a-dcfg", },
+ { .compatible = "fsl,ls1046a-dcfg", .data = &ls1046a_data},
+ { .compatible = "fsl,lx2160a-dcfg", },
+ { .compatible = "fsl,ls1028a-dcfg", .data = &ls1028a_data},
+ {}
+};
+
+static int __init fsl_guts_init(void)
+{
+ struct soc_device_attribute *soc_dev_attr = NULL;
+ static struct soc_device *soc_dev;
+ const struct fsl_soc_die_attr *soc_die;
+ const struct of_device_id *match;
+ struct device_node *np;
+ u64 soc_uid = 0;
+ int ret;
+
+ spin_lock_init(&soc.rcwcr_lock);
+
+ np = of_find_matching_node_and_match(NULL, fsl_guts_of_match, &match);
+ if (!np)
+ return 0;
+ soc.data = match->data;
+
+ soc.dcfg_ccsr = of_iomap(np, DCFG_CCSR);
+ if (!soc.dcfg_ccsr) {
+ of_node_put(np);
+ ret = -ENOMEM;
+ goto err_clear_soc_data;
+ }
+ /* DCFG_DCSR is optional */
+ soc.dcfg_dcsr = of_iomap(np, DCFG_DCSR);
+
+ soc.little_endian = of_property_read_bool(np, "little-endian");
+ soc.svr = fsl_guts_read(&soc.dcfg_ccsr->svr);
+ of_node_put(np);
+
+ /* Register soc device */
+ soc_dev_attr = kzalloc_obj(*soc_dev_attr);
+ if (!soc_dev_attr) {
+ ret = -ENOMEM;
+ goto err_unmap_dcfg_ccsr_dcsr;
+ }
+
+ ret = soc_attr_read_machine(soc_dev_attr);
+ if (ret)
+ of_machine_read_compatible(&soc_dev_attr->machine, 0);
+
+ soc_die = fsl_soc_die_match(soc.svr, fsl_soc_die);
+ if (soc_die) {
+ soc_dev_attr->family = kasprintf(GFP_KERNEL, "QorIQ %s",
+ soc_die->die);
+ } else {
+ soc_dev_attr->family = kasprintf(GFP_KERNEL, "QorIQ");
+ }
+ if (!soc_dev_attr->family) {
+ ret = -ENOMEM;
+ goto err_free_soc_dev_attr;
+ }
+
+ soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "svr:0x%08x", soc.svr);
+ if (!soc_dev_attr->soc_id) {
+ ret = -ENOMEM;
+ goto err_free_family;
+ }
+
+ soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%d.%d",
+ (soc.svr >> 4) & 0xf, soc.svr & 0xf);
+ if (!soc_dev_attr->revision) {
+ ret = -ENOMEM;
+ goto err_free_soc_id;
+ }
+
+ if (soc.data)
+ soc_uid = fsl_guts_get_soc_uid(soc.data->sfp_compat,
+ soc.data->uid_offset);
+ if (soc_uid)
+ soc_dev_attr->serial_number = kasprintf(GFP_KERNEL, "%016llX",
+ soc_uid);
+
+ soc_dev = soc_device_register(soc_dev_attr);
+ if (IS_ERR(soc_dev)) {
+ ret = PTR_ERR(soc_dev);
+ goto err_free_serial_number;
+ }
+
+ pr_info("Machine: %s\n", soc_dev_attr->machine);
+ pr_info("SoC family: %s\n", soc_dev_attr->family);
+ pr_info("SoC ID: %s, Revision: %s\n",
+ soc_dev_attr->soc_id, soc_dev_attr->revision);
+
+ return 0;
+
+err_free_serial_number:
+ kfree(soc_dev_attr->serial_number);
+ kfree(soc_dev_attr->revision);
+err_free_soc_id:
+ kfree(soc_dev_attr->soc_id);
+err_free_family:
+ kfree(soc_dev_attr->family);
+err_free_soc_dev_attr:
+ kfree(soc_dev_attr);
+err_unmap_dcfg_ccsr_dcsr:
+ if (soc.dcfg_dcsr) {
+ iounmap(soc.dcfg_dcsr);
+ soc.dcfg_dcsr = NULL;
+ }
+ iounmap(soc.dcfg_ccsr);
+ soc.dcfg_ccsr = NULL;
+err_clear_soc_data:
+ soc.data = NULL;
+
+ return ret;
+}
+core_initcall(fsl_guts_init);