linux-stable/arch/arm/mach-mxs/mach-mxs.c
Fabio Estevam c8705471b9 ARM: mxs: Do not search for "fsl,clkctrl"
The "fsl,clkctrl" compatible string is not documented.

It is used only to find the base address of the clock controller.

Instead of searching for an undocumented compatible string, search
for "fsl,imx23-clkctrl" and "fsl,imx28-clkctrl".

Signed-off-by: Fabio Estevam <festevam@denx.de>
Signed-off-by: Shawn Guo <shawnguo@kernel.org>
2023-12-06 11:21:43 +08:00

480 lines
10 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2012 Freescale Semiconductor, Inc.
* Copyright 2012 Linaro Ltd.
*/
#include <linux/clk.h>
#include <linux/clk/mxs.h>
#include <linux/clkdev.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/reboot.h>
#include <linux/micrel_phy.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/phy.h>
#include <linux/sys_soc.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <asm/mach/time.h>
#include <asm/system_info.h>
#include <asm/system_misc.h>
#include "pm.h"
/* MXS DIGCTL SAIF CLKMUX */
#define MXS_DIGCTL_SAIF_CLKMUX_DIRECT 0x0
#define MXS_DIGCTL_SAIF_CLKMUX_CROSSINPUT 0x1
#define MXS_DIGCTL_SAIF_CLKMUX_EXTMSTR0 0x2
#define MXS_DIGCTL_SAIF_CLKMUX_EXTMSTR1 0x3
#define HW_DIGCTL_CHIPID 0x310
#define HW_DIGCTL_CHIPID_MASK (0xffff << 16)
#define HW_DIGCTL_REV_MASK 0xff
#define HW_DIGCTL_CHIPID_MX23 (0x3780 << 16)
#define HW_DIGCTL_CHIPID_MX28 (0x2800 << 16)
#define MXS_CHIP_REVISION_1_0 0x10
#define MXS_CHIP_REVISION_1_1 0x11
#define MXS_CHIP_REVISION_1_2 0x12
#define MXS_CHIP_REVISION_1_3 0x13
#define MXS_CHIP_REVISION_1_4 0x14
#define MXS_CHIP_REV_UNKNOWN 0xff
#define MXS_GPIO_NR(bank, nr) ((bank) * 32 + (nr))
#define MXS_SET_ADDR 0x4
#define MXS_CLR_ADDR 0x8
#define MXS_TOG_ADDR 0xc
#define HW_OCOTP_OPS2 19 /* offset 0x150 */
#define HW_OCOTP_OPS3 20 /* offset 0x160 */
static u32 chipid;
static u32 socid;
static void __iomem *reset_addr;
static inline void __mxs_setl(u32 mask, void __iomem *reg)
{
__raw_writel(mask, reg + MXS_SET_ADDR);
}
static inline void __mxs_clrl(u32 mask, void __iomem *reg)
{
__raw_writel(mask, reg + MXS_CLR_ADDR);
}
static inline void __mxs_togl(u32 mask, void __iomem *reg)
{
__raw_writel(mask, reg + MXS_TOG_ADDR);
}
#define OCOTP_WORD_OFFSET 0x20
#define OCOTP_WORD_COUNT 0x20
#define BM_OCOTP_CTRL_BUSY (1 << 8)
#define BM_OCOTP_CTRL_ERROR (1 << 9)
#define BM_OCOTP_CTRL_RD_BANK_OPEN (1 << 12)
static DEFINE_MUTEX(ocotp_mutex);
static u32 ocotp_words[OCOTP_WORD_COUNT];
static const u32 *mxs_get_ocotp(void)
{
struct device_node *np;
void __iomem *ocotp_base;
int timeout = 0x400;
size_t i;
static int once;
if (once)
return ocotp_words;
np = of_find_compatible_node(NULL, NULL, "fsl,ocotp");
ocotp_base = of_iomap(np, 0);
WARN_ON(!ocotp_base);
mutex_lock(&ocotp_mutex);
/*
* clk_enable(hbus_clk) for ocotp can be skipped
* as it must be on when system is running.
*/
/* try to clear ERROR bit */
__mxs_clrl(BM_OCOTP_CTRL_ERROR, ocotp_base);
/* check both BUSY and ERROR cleared */
while ((__raw_readl(ocotp_base) &
(BM_OCOTP_CTRL_BUSY | BM_OCOTP_CTRL_ERROR)) && --timeout)
cpu_relax();
if (unlikely(!timeout))
goto error_unlock;
/* open OCOTP banks for read */
__mxs_setl(BM_OCOTP_CTRL_RD_BANK_OPEN, ocotp_base);
/* approximately wait 32 hclk cycles */
udelay(1);
/* poll BUSY bit becoming cleared */
timeout = 0x400;
while ((__raw_readl(ocotp_base) & BM_OCOTP_CTRL_BUSY) && --timeout)
cpu_relax();
if (unlikely(!timeout))
goto error_unlock;
for (i = 0; i < OCOTP_WORD_COUNT; i++)
ocotp_words[i] = __raw_readl(ocotp_base + OCOTP_WORD_OFFSET +
i * 0x10);
/* close banks for power saving */
__mxs_clrl(BM_OCOTP_CTRL_RD_BANK_OPEN, ocotp_base);
once = 1;
mutex_unlock(&ocotp_mutex);
return ocotp_words;
error_unlock:
mutex_unlock(&ocotp_mutex);
pr_err("%s: timeout in reading OCOTP\n", __func__);
return NULL;
}
enum mac_oui {
OUI_FSL,
OUI_DENX,
OUI_CRYSTALFONTZ,
OUI_I2SE,
OUI_ARMADEUS,
};
static void __init update_fec_mac_prop(enum mac_oui oui)
{
struct device_node *np, *from = NULL;
struct property *newmac;
const u32 *ocotp = mxs_get_ocotp();
u8 *macaddr;
u32 val;
int i;
for (i = 0; i < 2; i++) {
np = of_find_compatible_node(from, NULL, "fsl,imx28-fec");
if (!np)
return;
from = np;
if (of_property_present(np, "local-mac-address"))
continue;
newmac = kzalloc(sizeof(*newmac) + 6, GFP_KERNEL);
if (!newmac)
return;
newmac->value = newmac + 1;
newmac->length = 6;
newmac->name = kstrdup("local-mac-address", GFP_KERNEL);
if (!newmac->name) {
kfree(newmac);
return;
}
/*
* OCOTP only stores the last 4 octets for each mac address,
* so hard-code OUI here.
*/
macaddr = newmac->value;
switch (oui) {
case OUI_FSL:
macaddr[0] = 0x00;
macaddr[1] = 0x04;
macaddr[2] = 0x9f;
break;
case OUI_DENX:
macaddr[0] = 0xc0;
macaddr[1] = 0xe5;
macaddr[2] = 0x4e;
break;
case OUI_CRYSTALFONTZ:
macaddr[0] = 0x58;
macaddr[1] = 0xb9;
macaddr[2] = 0xe1;
break;
case OUI_I2SE:
macaddr[0] = 0x00;
macaddr[1] = 0x01;
macaddr[2] = 0x87;
break;
case OUI_ARMADEUS:
macaddr[0] = 0x00;
macaddr[1] = 0x1e;
macaddr[2] = 0xac;
break;
}
val = ocotp[i];
macaddr[3] = (val >> 16) & 0xff;
macaddr[4] = (val >> 8) & 0xff;
macaddr[5] = (val >> 0) & 0xff;
of_update_property(np, newmac);
}
}
static inline void enable_clk_enet_out(void)
{
struct clk *clk = clk_get_sys("enet_out", NULL);
if (!IS_ERR(clk))
clk_prepare_enable(clk);
}
static void __init imx28_evk_init(void)
{
update_fec_mac_prop(OUI_FSL);
mxs_saif_clkmux_select(MXS_DIGCTL_SAIF_CLKMUX_EXTMSTR0);
}
static void __init imx28_apf28_init(void)
{
update_fec_mac_prop(OUI_ARMADEUS);
}
static int apx4devkit_phy_fixup(struct phy_device *phy)
{
phy->dev_flags |= MICREL_PHY_50MHZ_CLK;
return 0;
}
static void __init apx4devkit_init(void)
{
enable_clk_enet_out();
if (IS_BUILTIN(CONFIG_PHYLIB))
phy_register_fixup_for_uid(PHY_ID_KSZ8051, MICREL_PHY_ID_MASK,
apx4devkit_phy_fixup);
}
static void __init crystalfontz_init(void)
{
update_fec_mac_prop(OUI_CRYSTALFONTZ);
}
static void __init duckbill_init(void)
{
update_fec_mac_prop(OUI_I2SE);
}
static void __init m28cu3_init(void)
{
update_fec_mac_prop(OUI_DENX);
}
static const char __init *mxs_get_soc_id(void)
{
struct device_node *np;
void __iomem *digctl_base;
np = of_find_compatible_node(NULL, NULL, "fsl,imx23-digctl");
digctl_base = of_iomap(np, 0);
WARN_ON(!digctl_base);
chipid = readl(digctl_base + HW_DIGCTL_CHIPID);
socid = chipid & HW_DIGCTL_CHIPID_MASK;
iounmap(digctl_base);
of_node_put(np);
switch (socid) {
case HW_DIGCTL_CHIPID_MX23:
return "i.MX23";
case HW_DIGCTL_CHIPID_MX28:
return "i.MX28";
default:
return "Unknown";
}
}
static u32 __init mxs_get_cpu_rev(void)
{
u32 rev = chipid & HW_DIGCTL_REV_MASK;
switch (socid) {
case HW_DIGCTL_CHIPID_MX23:
switch (rev) {
case 0x0:
return MXS_CHIP_REVISION_1_0;
case 0x1:
return MXS_CHIP_REVISION_1_1;
case 0x2:
return MXS_CHIP_REVISION_1_2;
case 0x3:
return MXS_CHIP_REVISION_1_3;
case 0x4:
return MXS_CHIP_REVISION_1_4;
default:
return MXS_CHIP_REV_UNKNOWN;
}
case HW_DIGCTL_CHIPID_MX28:
switch (rev) {
case 0x0:
return MXS_CHIP_REVISION_1_1;
case 0x1:
return MXS_CHIP_REVISION_1_2;
default:
return MXS_CHIP_REV_UNKNOWN;
}
default:
return MXS_CHIP_REV_UNKNOWN;
}
}
static const char __init *mxs_get_revision(void)
{
u32 rev = mxs_get_cpu_rev();
if (rev != MXS_CHIP_REV_UNKNOWN)
return kasprintf(GFP_KERNEL, "%d.%d", (rev >> 4) & 0xf,
rev & 0xf);
else
return kasprintf(GFP_KERNEL, "%s", "Unknown");
}
#define MX23_CLKCTRL_RESET_OFFSET 0x120
#define MX28_CLKCTRL_RESET_OFFSET 0x1e0
static int __init mxs_restart_init(void)
{
struct device_node *np;
np = of_find_compatible_node(NULL, NULL, "fsl,imx23-clkctrl");
if (!np)
np = of_find_compatible_node(NULL, NULL, "fsl,imx28-clkctrl");
reset_addr = of_iomap(np, 0);
if (!reset_addr)
return -ENODEV;
if (of_device_is_compatible(np, "fsl,imx23-clkctrl"))
reset_addr += MX23_CLKCTRL_RESET_OFFSET;
else
reset_addr += MX28_CLKCTRL_RESET_OFFSET;
of_node_put(np);
return 0;
}
static void __init eukrea_mbmx283lc_init(void)
{
mxs_saif_clkmux_select(MXS_DIGCTL_SAIF_CLKMUX_EXTMSTR0);
}
static void __init mxs_machine_init(void)
{
struct device_node *root;
struct device *parent;
struct soc_device *soc_dev;
struct soc_device_attribute *soc_dev_attr;
u64 soc_uid = 0;
const u32 *ocotp = mxs_get_ocotp();
int ret;
soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
if (!soc_dev_attr)
return;
root = of_find_node_by_path("/");
ret = of_property_read_string(root, "model", &soc_dev_attr->machine);
if (ret) {
kfree(soc_dev_attr);
return;
}
soc_dev_attr->family = "Freescale MXS Family";
soc_dev_attr->soc_id = mxs_get_soc_id();
soc_dev_attr->revision = mxs_get_revision();
if (socid == HW_DIGCTL_CHIPID_MX23) {
soc_uid = system_serial_low = ocotp[HW_OCOTP_OPS3];
} else if (socid == HW_DIGCTL_CHIPID_MX28) {
soc_uid = system_serial_high = ocotp[HW_OCOTP_OPS2];
soc_uid <<= 32;
system_serial_low = ocotp[HW_OCOTP_OPS3];
soc_uid |= system_serial_low;
}
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)) {
kfree(soc_dev_attr->serial_number);
kfree(soc_dev_attr->revision);
kfree(soc_dev_attr);
return;
}
parent = soc_device_to_device(soc_dev);
if (of_machine_is_compatible("fsl,imx28-evk"))
imx28_evk_init();
if (of_machine_is_compatible("armadeus,imx28-apf28"))
imx28_apf28_init();
else if (of_machine_is_compatible("bluegiga,apx4devkit"))
apx4devkit_init();
else if (of_machine_is_compatible("crystalfontz,cfa10036"))
crystalfontz_init();
else if (of_machine_is_compatible("eukrea,mbmx283lc"))
eukrea_mbmx283lc_init();
else if (of_machine_is_compatible("i2se,duckbill") ||
of_machine_is_compatible("i2se,duckbill-2"))
duckbill_init();
else if (of_machine_is_compatible("msr,m28cu3"))
m28cu3_init();
of_platform_default_populate(NULL, NULL, parent);
mxs_restart_init();
}
#define MXS_CLKCTRL_RESET_CHIP (1 << 1)
/*
* Reset the system. It is called by machine_restart().
*/
static void mxs_restart(enum reboot_mode mode, const char *cmd)
{
if (reset_addr) {
/* reset the chip */
__mxs_setl(MXS_CLKCTRL_RESET_CHIP, reset_addr);
pr_err("Failed to assert the chip reset\n");
/* Delay to allow the serial port to show the message */
mdelay(50);
}
/* We'll take a jump through zero as a poor second */
soft_restart(0);
}
static const char *const mxs_dt_compat[] __initconst = {
"fsl,imx28",
"fsl,imx23",
NULL,
};
DT_MACHINE_START(MXS, "Freescale MXS (Device Tree)")
.init_machine = mxs_machine_init,
.init_late = mxs_pm_init,
.dt_compat = mxs_dt_compat,
.restart = mxs_restart,
MACHINE_END