123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466 |
- /*
- * Copyright 2012 Freescale Semiconductor, Inc.
- * Copyright 2012 Linaro Ltd.
- *
- * The code contained herein is licensed under the GNU General Public
- * License. You may obtain a copy of the GNU General Public License
- * Version 2 or later at the following locations:
- *
- * http://www.opensource.org/licenses/gpl-license.html
- * http://www.gnu.org/copyleft/gpl.html
- */
- #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/irqchip/mxs.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/pinctrl/consumer.h>
- #include <linux/sys_soc.h>
- #include <asm/mach/arch.h>
- #include <asm/mach/map.h>
- #include <asm/mach/time.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
- 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_get_property(np, "local-mac-address", NULL))
- 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,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;
- 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)
- return;
- soc_dev_attr->family = "Freescale MXS Family";
- soc_dev_attr->soc_id = mxs_get_soc_id();
- soc_dev_attr->revision = mxs_get_revision();
- soc_dev = soc_device_register(soc_dev_attr);
- if (IS_ERR(soc_dev)) {
- 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"))
- 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)")
- .handle_irq = icoll_handle_irq,
- .init_machine = mxs_machine_init,
- .init_late = mxs_pm_init,
- .dt_compat = mxs_dt_compat,
- .restart = mxs_restart,
- MACHINE_END
|