2012-03-31 21:26:57 +08:00
/*
* 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>
2013-03-29 13:36:05 +08:00
# include <linux/clk/mxs.h>
2012-03-31 21:26:57 +08:00
# include <linux/clkdev.h>
2012-07-13 14:15:34 +08:00
# include <linux/delay.h>
2012-03-31 21:26:57 +08:00
# include <linux/err.h>
2012-07-13 14:15:34 +08:00
# include <linux/gpio.h>
2012-03-31 21:26:57 +08:00
# include <linux/init.h>
2013-03-25 21:34:51 +08:00
# include <linux/irqchip/mxs.h>
2013-07-08 16:01:40 -07:00
# include <linux/reboot.h>
2012-07-07 23:12:03 +08:00
# include <linux/micrel_phy.h>
2013-03-29 09:45:31 +08:00
# include <linux/of_address.h>
2012-03-31 21:26:57 +08:00
# include <linux/of_platform.h>
2012-07-07 23:12:03 +08:00
# include <linux/phy.h>
2012-07-13 14:15:34 +08:00
# include <linux/pinctrl/consumer.h>
2013-06-04 10:18:45 -03:00
# include <linux/sys_soc.h>
2012-03-31 21:26:57 +08:00
# include <asm/mach/arch.h>
2013-03-29 13:07:34 +08:00
# include <asm/mach/map.h>
2012-03-31 21:26:57 +08:00
# include <asm/mach/time.h>
2013-03-29 09:45:31 +08:00
# include <asm/system_misc.h>
2013-03-29 13:53:11 +08:00
2013-03-29 14:41:27 +08:00
# include "pm.h"
2013-03-29 13:53:11 +08:00
/* 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
2013-06-04 10:18:45 -03:00
# 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
2013-03-29 13:53:11 +08:00
# define MXS_GPIO_NR(bank, nr) ((bank) * 32 + (nr))
# define MXS_SET_ADDR 0x4
# define MXS_CLR_ADDR 0x8
# define MXS_TOG_ADDR 0xc
2013-06-04 10:18:45 -03:00
static u32 chipid ;
static u32 socid ;
2013-08-05 10:51:14 +02:00
static void __iomem * reset_addr ;
2013-03-29 13:53:11 +08:00
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 ) ;
}
2012-03-31 21:26:57 +08:00
2013-03-29 13:27:55 +08:00
# 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 ;
}
2012-06-19 22:38:14 +08:00
enum mac_oui {
OUI_FSL ,
OUI_DENX ,
2012-10-07 10:36:28 +08:00
OUI_CRYSTALFONTZ ,
2013-11-09 12:14:16 +01:00
OUI_I2SE ,
2013-11-30 09:51:12 +01:00
OUI_ARMADEUS ,
2012-06-19 22:38:14 +08:00
} ;
static void __init update_fec_mac_prop ( enum mac_oui oui )
{
struct device_node * np , * from = NULL ;
2012-07-13 14:13:55 +08:00
struct property * newmac ;
2012-06-19 22:38:14 +08:00
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 ;
2012-09-25 13:32:18 +02:00
2012-06-19 22:38:14 +08:00
from = np ;
2012-09-25 13:32:18 +02:00
if ( of_get_property ( np , " local-mac-address " , NULL ) )
continue ;
2012-06-19 22:38:14 +08:00
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 ;
2012-10-07 10:36:28 +08:00
case OUI_CRYSTALFONTZ :
macaddr [ 0 ] = 0x58 ;
macaddr [ 1 ] = 0xb9 ;
macaddr [ 2 ] = 0xe1 ;
break ;
2013-11-09 12:14:16 +01:00
case OUI_I2SE :
macaddr [ 0 ] = 0x00 ;
macaddr [ 1 ] = 0x01 ;
macaddr [ 2 ] = 0x87 ;
break ;
2013-11-30 09:51:12 +01:00
case OUI_ARMADEUS :
macaddr [ 0 ] = 0x00 ;
macaddr [ 1 ] = 0x1e ;
macaddr [ 2 ] = 0xac ;
break ;
2012-06-19 22:38:14 +08:00
}
val = ocotp [ i ] ;
macaddr [ 3 ] = ( val > > 16 ) & 0xff ;
macaddr [ 4 ] = ( val > > 8 ) & 0xff ;
macaddr [ 5 ] = ( val > > 0 ) & 0xff ;
2012-10-02 16:58:46 +00:00
of_update_property ( np , newmac ) ;
2012-06-19 22:38:14 +08:00
}
}
2012-07-07 21:21:38 +08:00
static inline void enable_clk_enet_out ( void )
2012-03-31 21:26:57 +08:00
{
2012-07-07 21:21:38 +08:00
struct clk * clk = clk_get_sys ( " enet_out " , NULL ) ;
2012-03-31 21:26:57 +08:00
if ( ! IS_ERR ( clk ) )
clk_prepare_enable ( clk ) ;
2012-07-07 21:21:38 +08:00
}
2012-06-19 22:38:14 +08:00
2012-07-07 21:21:38 +08:00
static void __init imx28_evk_init ( void )
{
2012-06-19 22:38:14 +08:00
update_fec_mac_prop ( OUI_FSL ) ;
2012-06-25 21:21:46 +08:00
2012-08-01 11:20:16 +08:00
mxs_saif_clkmux_select ( MXS_DIGCTL_SAIF_CLKMUX_EXTMSTR0 ) ;
2012-03-31 21:26:57 +08:00
}
2013-11-30 09:51:12 +01:00
static void __init imx28_apf28_init ( void )
{
update_fec_mac_prop ( OUI_ARMADEUS ) ;
}
2012-07-07 23:12:03 +08:00
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 ) )
2012-09-23 16:58:50 +00:00
phy_register_fixup_for_uid ( PHY_ID_KSZ8051 , MICREL_PHY_ID_MASK ,
2012-07-07 23:12:03 +08:00
apx4devkit_phy_fixup ) ;
}
2013-06-13 15:43:44 +02:00
static void __init crystalfontz_init ( void )
2012-10-07 10:36:28 +08:00
{
update_fec_mac_prop ( OUI_CRYSTALFONTZ ) ;
}
2013-11-09 12:14:16 +01:00
static void __init duckbill_init ( void )
{
update_fec_mac_prop ( OUI_I2SE ) ;
}
2013-09-30 00:41:29 +02:00
static void __init m28cu3_init ( void )
{
update_fec_mac_prop ( OUI_DENX ) ;
}
2013-06-04 10:18:45 -03:00
static const char __init * mxs_get_soc_id ( void )
2013-01-26 13:40:37 +08:00
{
2013-06-04 10:18:45 -03:00
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 )
2013-08-13 10:28:02 -03:00
return kasprintf ( GFP_KERNEL , " %d.%d " , ( rev > > 4 ) & 0xf ,
2013-06-04 10:18:45 -03:00
rev & 0xf ) ;
else
return kasprintf ( GFP_KERNEL , " %s " , " Unknown " ) ;
2013-01-26 13:40:37 +08:00
}
2013-08-05 10:51:14 +02:00
# 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 ;
}
2013-12-05 14:28:06 +01:00
static void __init eukrea_mbmx283lc_init ( void )
{
mxs_saif_clkmux_select ( MXS_DIGCTL_SAIF_CLKMUX_EXTMSTR0 ) ;
}
2012-03-31 21:26:57 +08:00
static void __init mxs_machine_init ( void )
{
2013-06-04 10:18:45 -03:00
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 ) ;
2012-03-31 21:26:57 +08:00
if ( of_machine_is_compatible ( " fsl,imx28-evk " ) )
imx28_evk_init ( ) ;
2013-11-30 09:51:12 +01:00
if ( of_machine_is_compatible ( " armadeus,imx28-apf28 " ) )
imx28_apf28_init ( ) ;
2012-07-07 23:12:03 +08:00
else if ( of_machine_is_compatible ( " bluegiga,apx4devkit " ) )
apx4devkit_init ( ) ;
2013-07-01 15:23:22 +02:00
else if ( of_machine_is_compatible ( " crystalfontz,cfa10036 " ) )
2013-06-13 15:43:44 +02:00
crystalfontz_init ( ) ;
2013-12-05 14:28:06 +01:00
else if ( of_machine_is_compatible ( " eukrea,mbmx283lc " ) )
eukrea_mbmx283lc_init ( ) ;
2017-04-10 11:08:55 +02:00
else if ( of_machine_is_compatible ( " i2se,duckbill " ) | |
of_machine_is_compatible ( " i2se,duckbill-2 " ) )
2013-11-09 12:14:16 +01:00
duckbill_init ( ) ;
2013-09-30 00:41:29 +02:00
else if ( of_machine_is_compatible ( " msr,m28cu3 " ) )
m28cu3_init ( ) ;
2012-03-31 21:26:57 +08:00
2016-06-01 14:53:05 +08:00
of_platform_default_populate ( NULL , NULL , parent ) ;
2012-07-13 14:15:34 +08:00
2013-08-05 10:51:14 +02:00
mxs_restart_init ( ) ;
2012-03-31 21:26:57 +08:00
}
2013-03-29 09:45:31 +08:00
# define MXS_CLKCTRL_RESET_CHIP (1 << 1)
/*
* Reset the system . It is called by machine_restart ( ) .
*/
2013-07-08 16:01:40 -07:00
static void mxs_restart ( enum reboot_mode mode , const char * cmd )
2013-03-29 09:45:31 +08:00
{
2013-08-05 10:51:14 +02:00
if ( reset_addr ) {
/* reset the chip */
__mxs_setl ( MXS_CLKCTRL_RESET_CHIP , reset_addr ) ;
2013-03-29 09:45:31 +08:00
2013-08-05 10:51:14 +02:00
pr_err ( " Failed to assert the chip reset \n " ) ;
2013-03-29 09:45:31 +08:00
2013-08-05 10:51:14 +02:00
/* Delay to allow the serial port to show the message */
mdelay ( 50 ) ;
}
2013-03-29 09:45:31 +08:00
/* We'll take a jump through zero as a poor second */
soft_restart ( 0 ) ;
}
2015-07-27 18:27:52 -04:00
static const char * const mxs_dt_compat [ ] __initconst = {
2012-03-31 21:26:57 +08:00
" fsl,imx28 " ,
2013-03-29 14:04:07 +08:00
" fsl,imx23 " ,
2012-03-31 21:26:57 +08:00
NULL ,
} ;
2013-03-29 14:04:07 +08:00
DT_MACHINE_START ( MXS , " Freescale MXS (Device Tree) " )
2012-08-20 10:14:56 +08:00
. handle_irq = icoll_handle_irq ,
2012-03-31 21:26:57 +08:00
. init_machine = mxs_machine_init ,
2013-03-29 14:41:27 +08:00
. init_late = mxs_pm_init ,
2013-03-29 14:04:07 +08:00
. dt_compat = mxs_dt_compat ,
2012-03-31 21:26:57 +08:00
. restart = mxs_restart ,
MACHINE_END