From patchwork Thu Jul 31 21:28:04 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Emilio_L=C3=B3pez?= X-Patchwork-Id: 4659121 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 140E19F377 for ; Thu, 31 Jul 2014 21:32:45 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id F162F2012E for ; Thu, 31 Jul 2014 21:32:43 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id E389F20145 for ; Thu, 31 Jul 2014 21:32:42 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1XCxw0-0001Zv-9L; Thu, 31 Jul 2014 21:30:48 +0000 Received: from yotta.elopez.com.ar ([2a00:1768:1004:d00d:c0de:4:f00d:cafe]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1XCxvD-0008EY-GA for linux-arm-kernel@lists.infradead.org; Thu, 31 Jul 2014 21:30:01 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=elopez.com.ar; s=mail; h=Content-Transfer-Encoding:Content-Type:MIME-Version:References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From; bh=QVT5LDalexB22pS2LcMqVtAGThr7sUhZ51hBltrQg1A=; b=jW94J+CUwXAOX077s9CjbbKAYEbqzV/2kV4nwbXNpMKIpQFTGLmYr/RgLAsAJwp+wgVJM9N0gwOJ4JmJ37gkh8ybJ0POVa61ZGjMfMdmLMAKHUKKhrtKi4W2m8czT5MzeXQr7VoplNt6m/9PzLGMVJ8joWrxbcmQhx/0p4H+EpYHCRBNN+JHr7LZMGTeJNiOyMPY0lI9T0rSXs8YqKnZ0AYaxL3nEROARvBXwpI1bX5a8HdfMz+/viky8dt6LiDoDPyrkmv0qh6asEcjMB/HDVNMjdDYlX8kHfxXXVvcYkBKjhdEqKW4ej85HgWAWO7HlU5qTUmUrtO5ebXzJhfz3Q==; Received: from [181.164.71.8] (helo=desktop.lan) by yotta.elopez.com.ar with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_CBC_SHA256:128) (Exim 4.82_1-5b7a7c0-XX) id 1XCxul-0003Cn-T5; Thu, 31 Jul 2014 18:29:32 -0300 From: =?UTF-8?q?Emilio=20L=C3=B3pez?= To: Maxime Ripard , Mike Turquette Subject: [PATCH 1/9] ARM: sunxi: introduce SoC identification support Date: Thu, 31 Jul 2014 18:28:04 -0300 Message-Id: <1406842092-25207-2-git-send-email-emilio@elopez.com.ar> X-Mailer: git-send-email 2.0.3 In-Reply-To: <1406842092-25207-1-git-send-email-emilio@elopez.com.ar> References: <1406842092-25207-1-git-send-email-emilio@elopez.com.ar> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20140731_142959_836673_461330E4 X-CRM114-Status: GOOD ( 20.07 ) X-Spam-Score: -0.8 (/) Cc: Arnd Bergmann , =?UTF-8?q?Emilio=20L=C3=B3pez?= , codekipper@gmail.com, jonsmirl@gmail.com, Olof Johansson , linux-arm-kernel@lists.infradead.org X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-2.5 required=5.0 tests=BAYES_00,DKIM_SIGNED, RP_MATCHES_RCVD, T_DKIM_INVALID, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This commit adds SoC bus support on the sunxi platform, and exposes information such as the hardware revision to userspace and other kernel clients during init. A message with this information is also printed to the kernel log to ease future bug triaging. Signed-off-by: Emilio López --- arch/arm/mach-sunxi/Kconfig | 1 + arch/arm/mach-sunxi/Makefile | 2 +- arch/arm/mach-sunxi/sunxi-soc-id.c | 226 +++++++++++++++++++++++++++++++++++++ arch/arm/mach-sunxi/sunxi-soc-id.h | 6 + 4 files changed, 234 insertions(+), 1 deletion(-) create mode 100644 arch/arm/mach-sunxi/sunxi-soc-id.c create mode 100644 arch/arm/mach-sunxi/sunxi-soc-id.h diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig index 6434e3b..4a199df 100644 --- a/arch/arm/mach-sunxi/Kconfig +++ b/arch/arm/mach-sunxi/Kconfig @@ -5,6 +5,7 @@ menuconfig ARCH_SUNXI select GENERIC_IRQ_CHIP select PINCTRL select PINCTRL_SUNXI + select SOC_BUS select SUN4I_TIMER if ARCH_SUNXI diff --git a/arch/arm/mach-sunxi/Makefile b/arch/arm/mach-sunxi/Makefile index 27b168f..589239b 100644 --- a/arch/arm/mach-sunxi/Makefile +++ b/arch/arm/mach-sunxi/Makefile @@ -1,2 +1,2 @@ -obj-$(CONFIG_ARCH_SUNXI) += sunxi.o +obj-$(CONFIG_ARCH_SUNXI) += sunxi.o sunxi-soc-id.o obj-$(CONFIG_SMP) += platsmp.o diff --git a/arch/arm/mach-sunxi/sunxi-soc-id.c b/arch/arm/mach-sunxi/sunxi-soc-id.c new file mode 100644 index 0000000..c7eff1c --- /dev/null +++ b/arch/arm/mach-sunxi/sunxi-soc-id.c @@ -0,0 +1,226 @@ +/* + * SoC revision detection for sunxi SoCs + * + * Copyright 2014 Emilio López + * + * Emilio López + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "sunxi-soc-id.h" + +/* + * On the A10 SoC, we can read the revision information from the timer + * block. The detection logic is extracted from similar code on the + * Allwinner vendor tree, as this is undocumented on the user manual + */ + +#define TIMER_SOC_REV_REG 0x13c +#define TIMER_SOC_REV_CLEAR(val) ((val) & ~(0x3 << 6)) +#define TIMER_SOC_REV_GET(val) (((val) >> 6) & 0x3) + +static const struct of_device_id sun4i_timer_compatible[] __initconst = { + { .compatible = "allwinner,sun4i-a10-timer", }, + {}, +}; + +static int __init sun4i_get_soc_revision(void) +{ + struct device_node *np; + void __iomem *base; + u32 val; + int ret; + + /* Find the timer node */ + np = of_find_matching_node(NULL, sun4i_timer_compatible); + if (!np) + return -ENODEV; + + /* Temporarily map it for reading */ + base = of_iomap(np, 0); + if (!base) { + of_node_put(np); + return -ENOMEM; + } + + /* Clear the SoC revision bits and rewrite the register */ + val = readl(base + TIMER_SOC_REV_REG); + val = TIMER_SOC_REV_CLEAR(val); + writel(val, base + TIMER_SOC_REV_REG); + + /* Now read it again and see what shows up */ + val = readl(base + TIMER_SOC_REV_REG); + val = TIMER_SOC_REV_GET(val); + + switch (val) { + case 0: /* revision A */ + ret = 'A'; + case 3: /* revision B */ + ret = 'B'; + default: /* revision C */ + ret = 'C'; + } + + iounmap(base); + of_node_put(np); + + return ret; +} + +/* + * On the sun5i SoCs (A10S, A13), we can read the revision information + * from the first bits in the Security ID. The detection logic is + * extracted from similar code on the Allwinner vendor tree, as this + * is undocumented on the user manual. + */ + +static const struct of_device_id sun5i_sid_compatible[] __initconst = { + { .compatible = "allwinner,sun4i-a10-sid", }, + {}, +}; + +static int __init sun5i_get_soc_revision(void) +{ + struct device_node *np; + void __iomem *sid; + u32 val; + int ret; + + /* Find the SID node */ + np = of_find_matching_node(NULL, sun5i_sid_compatible); + if (!np) + return -ENODEV; + + /* Temporarily map it for reading */ + sid = of_iomap(np, 0); + if (!sid) { + of_node_put(np); + return -ENOMEM; + } + + /* Read and extract the chip revision from the SID */ + val = readl(sid); + val = (val >> 8) & 0xffffff; + + switch (val) { + case 0: /* A10S/A13 rev A */ + case 0x162541: /* A10S/A13 rev A */ + case 0x162565: /* A13 rev A */ + ret = 'A'; + break; + case 0x162542: /* A10S/A13 rev B */ + ret = 'B'; + break; + default: /* Unknown chip revision */ + ret = -ENODATA; + } + + iounmap(sid); + of_node_put(np); + + return ret; +} + +int __init sunxi_soc_revision(void) +{ + static int revision = -ENODEV; + + /* Try to query the hardware just once */ + if (!IS_ERR_VALUE(revision)) + return revision; + + if (of_machine_is_compatible("allwinner,sun4i-a10")) { + revision = sun4i_get_soc_revision(); + } else if (of_machine_is_compatible("allwinner,sun5i-a10s") || + of_machine_is_compatible("allwinner,sun5i-a13")) { + revision = sun5i_get_soc_revision(); + } + + return revision; +} + +/* Matches for the sunxi SoCs we know of */ +static const struct of_device_id soc_matches[] __initconst = { + { .compatible = "allwinner,sun4i-a10", .data = "A10 (sun4i)" }, + { .compatible = "allwinner,sun5i-a13", .data = "A13 (sun5i)" }, + { .compatible = "allwinner,sun5i-a10s", .data = "A10S (sun5i)" }, + { .compatible = "allwinner,sun6i-a31", .data = "A31 (sun6i)" }, + { .compatible = "allwinner,sun7i-a20", .data = "A20 (sun7i)" }, + { .compatible = "allwinner,sun8i-a23", .data = "A23 (sun8i)" }, + { /* sentinel */ }, +}; + +static int __init sunxi_register_soc_device(void) +{ + struct soc_device_attribute *soc_dev_attr; + struct soc_device *soc_dev; + const struct of_device_id *match; + struct device_node *root; + int revision; + + /* Only run on sunxi SoCs that we know of */ + root = of_find_node_by_path("/"); + match = of_match_node(soc_matches, root); + if (!match) + goto exit; + + soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); + if (!soc_dev_attr) + goto exit; + + /* Read the machine name if available */ + of_property_read_string(root, "model", &soc_dev_attr->machine); + + soc_dev_attr->family = kstrdup("Allwinner A Series", GFP_KERNEL); + soc_dev_attr->soc_id = kstrdup(match->data, GFP_KERNEL); + + /* Revision may not always be available */ + revision = sunxi_soc_revision(); + if (IS_ERR_VALUE(revision)) + soc_dev_attr->revision = kstrdup("Unknown", GFP_KERNEL); + else + soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%c", revision); + + soc_dev = soc_device_register(soc_dev_attr); + if (IS_ERR(soc_dev)) + goto free_struct; + + /* + * Print an informational line mentioning the hardware details. + * It may come in handy during bug reports, as some early SoC + * revisions have hardware quirks and do not get much testing. + */ + pr_info("SoC bus registered, running %s %s, revision %s\n", + soc_dev_attr->family, soc_dev_attr->soc_id, + soc_dev_attr->revision); + + return 0; + +free_struct: + kfree(soc_dev_attr->family); + kfree(soc_dev_attr->soc_id); + kfree(soc_dev_attr->revision); + kfree(soc_dev_attr); +exit: + of_node_put(root); + + return 0; +} +postcore_initcall(sunxi_register_soc_device) diff --git a/arch/arm/mach-sunxi/sunxi-soc-id.h b/arch/arm/mach-sunxi/sunxi-soc-id.h new file mode 100644 index 0000000..d49c245 --- /dev/null +++ b/arch/arm/mach-sunxi/sunxi-soc-id.h @@ -0,0 +1,6 @@ +#ifndef _SUNXI_SOC_ID_H +#define _SUNXI_SOC_ID_H + +int __init sunxi_soc_revision(void); + +#endif /* _SUNXI_SOC_ID_H */