2011-05-09 20:56:46 +04:00
/*
* Broadcom specific AMBA
* ChipCommon core driver
*
* Copyright 2005 , Broadcom Corporation
2011-07-04 22:50:05 +04:00
* Copyright 2006 , 2007 , Michael Buesch < m @ bues . ch >
2012-12-05 21:45:59 +04:00
* Copyright 2012 , Hauke Mehrtens < hauke @ hauke - m . de >
2011-05-09 20:56:46 +04:00
*
* Licensed under the GNU / GPL . See COPYING for details .
*/
# include "bcma_private.h"
2012-12-05 21:46:01 +04:00
# include <linux/bcm47xx_wdt.h>
2011-07-28 05:21:04 +04:00
# include <linux/export.h>
2012-12-05 21:46:02 +04:00
# include <linux/platform_device.h>
2011-05-09 20:56:46 +04:00
# include <linux/bcma/bcma.h>
static inline u32 bcma_cc_write32_masked ( struct bcma_drv_cc * cc , u16 offset ,
u32 mask , u32 value )
{
value & = mask ;
value | = bcma_cc_read32 ( cc , offset ) & ~ mask ;
bcma_cc_write32 ( cc , offset , value ) ;
return value ;
}
2013-03-27 20:23:11 +04:00
u32 bcma_chipco_get_alp_clock ( struct bcma_drv_cc * cc )
2012-12-05 21:45:59 +04:00
{
if ( cc - > capabilities & BCMA_CC_CAP_PMU )
2012-12-07 15:56:56 +04:00
return bcma_pmu_get_alp_clock ( cc ) ;
2012-12-05 21:45:59 +04:00
return 20000000 ;
}
2013-03-27 20:23:11 +04:00
EXPORT_SYMBOL_GPL ( bcma_chipco_get_alp_clock ) ;
2012-12-05 21:45:59 +04:00
2016-07-25 21:33:56 +03:00
static bool bcma_core_cc_has_pmu_watchdog ( struct bcma_drv_cc * cc )
{
struct bcma_bus * bus = cc - > core - > bus ;
if ( cc - > capabilities & BCMA_CC_CAP_PMU ) {
if ( bus - > chipinfo . id = = BCMA_CHIP_ID_BCM53573 ) {
WARN ( bus - > chipinfo . rev < = 1 , " No watchdog available \n " ) ;
/* 53573B0 and 53573B1 have bugged PMU watchdog. It can
* be enabled but timer can ' t be bumped . Use CC one
* instead .
*/
return false ;
}
return true ;
} else {
return false ;
}
}
2012-12-05 21:46:00 +04:00
static u32 bcma_chipco_watchdog_get_max_timer ( struct bcma_drv_cc * cc )
{
struct bcma_bus * bus = cc - > core - > bus ;
u32 nb ;
2016-07-25 21:33:56 +03:00
if ( bcma_core_cc_has_pmu_watchdog ( cc ) ) {
2012-12-05 21:46:00 +04:00
if ( bus - > chipinfo . id = = BCMA_CHIP_ID_BCM4706 )
nb = 32 ;
else if ( cc - > core - > id . rev < 26 )
nb = 16 ;
else
nb = ( cc - > core - > id . rev > = 37 ) ? 32 : 24 ;
} else {
nb = 28 ;
}
if ( nb = = 32 )
return 0xffffffff ;
else
return ( 1 < < nb ) - 1 ;
}
2012-12-05 21:46:01 +04:00
static u32 bcma_chipco_watchdog_timer_set_wdt ( struct bcm47xx_wdt * wdt ,
u32 ticks )
{
struct bcma_drv_cc * cc = bcm47xx_wdt_get_drvdata ( wdt ) ;
return bcma_chipco_watchdog_timer_set ( cc , ticks ) ;
}
static u32 bcma_chipco_watchdog_timer_set_ms_wdt ( struct bcm47xx_wdt * wdt ,
u32 ms )
{
struct bcma_drv_cc * cc = bcm47xx_wdt_get_drvdata ( wdt ) ;
u32 ticks ;
ticks = bcma_chipco_watchdog_timer_set ( cc , cc - > ticks_per_ms * ms ) ;
return ticks / cc - > ticks_per_ms ;
}
static int bcma_chipco_watchdog_ticks_per_ms ( struct bcma_drv_cc * cc )
{
struct bcma_bus * bus = cc - > core - > bus ;
if ( cc - > capabilities & BCMA_CC_CAP_PMU ) {
if ( bus - > chipinfo . id = = BCMA_CHIP_ID_BCM4706 )
2014-12-27 22:24:28 +03:00
/* 4706 CC and PMU watchdogs are clocked at 1/4 of ALP
* clock
*/
2012-12-07 15:56:56 +04:00
return bcma_chipco_get_alp_clock ( cc ) / 4000 ;
2012-12-05 21:46:01 +04:00
else
/* based on 32KHz ILP clock */
return 32 ;
} else {
2012-12-07 15:56:56 +04:00
return bcma_chipco_get_alp_clock ( cc ) / 1000 ;
2012-12-05 21:46:01 +04:00
}
}
2012-12-05 21:46:00 +04:00
2012-12-05 21:46:02 +04:00
int bcma_chipco_watchdog_register ( struct bcma_drv_cc * cc )
{
2016-07-25 21:33:56 +03:00
struct bcma_bus * bus = cc - > core - > bus ;
2012-12-05 21:46:02 +04:00
struct bcm47xx_wdt wdt = { } ;
struct platform_device * pdev ;
2016-07-25 21:33:56 +03:00
if ( bus - > chipinfo . id = = BCMA_CHIP_ID_BCM53573 & &
bus - > chipinfo . rev < = 1 ) {
pr_debug ( " No watchdog on 53573A0 / 53573A1 \n " ) ;
return 0 ;
}
2012-12-05 21:46:02 +04:00
wdt . driver_data = cc ;
wdt . timer_set = bcma_chipco_watchdog_timer_set_wdt ;
wdt . timer_set_ms = bcma_chipco_watchdog_timer_set_ms_wdt ;
2014-12-27 22:24:28 +03:00
wdt . max_timer_ms =
bcma_chipco_watchdog_get_max_timer ( cc ) / cc - > ticks_per_ms ;
2012-12-05 21:46:02 +04:00
pdev = platform_device_register_data ( NULL , " bcm47xx-wdt " ,
2016-07-25 21:33:56 +03:00
bus - > num , & wdt ,
2012-12-05 21:46:02 +04:00
sizeof ( wdt ) ) ;
if ( IS_ERR ( pdev ) )
return PTR_ERR ( pdev ) ;
cc - > watchdog = pdev ;
return 0 ;
}
2016-02-12 12:15:45 +03:00
static void bcma_core_chipcommon_flash_detect ( struct bcma_drv_cc * cc )
{
struct bcma_bus * bus = cc - > core - > bus ;
switch ( cc - > capabilities & BCMA_CC_CAP_FLASHT ) {
case BCMA_CC_FLASHT_STSER :
case BCMA_CC_FLASHT_ATSER :
bcma_debug ( bus , " Found serial flash \n " ) ;
bcma_sflash_init ( cc ) ;
break ;
case BCMA_CC_FLASHT_PARA :
bcma_debug ( bus , " Found parallel flash \n " ) ;
bcma_pflash_init ( cc ) ;
break ;
default :
bcma_err ( bus , " Flash type not supported \n " ) ;
}
if ( cc - > core - > id . rev = = 38 | |
bus - > chipinfo . id = = BCMA_CHIP_ID_BCM4706 ) {
if ( cc - > capabilities & BCMA_CC_CAP_NFLASH ) {
bcma_debug ( bus , " Found NAND flash \n " ) ;
bcma_nflash_init ( cc ) ;
}
}
}
2012-09-29 22:29:49 +04:00
void bcma_core_chipcommon_early_init ( struct bcma_drv_cc * cc )
2011-05-09 20:56:46 +04:00
{
2016-01-22 20:02:54 +03:00
struct bcma_bus * bus = cc - > core - > bus ;
2012-09-29 22:29:49 +04:00
if ( cc - > early_setup_done )
2011-07-23 03:20:07 +04:00
return ;
2012-11-21 02:24:27 +04:00
spin_lock_init ( & cc - > gpio_lock ) ;
2011-05-09 20:56:46 +04:00
if ( cc - > core - > id . rev > = 11 )
cc - > status = bcma_cc_read32 ( cc , BCMA_CC_CHIPSTAT ) ;
cc - > capabilities = bcma_cc_read32 ( cc , BCMA_CC_CAP ) ;
if ( cc - > core - > id . rev > = 35 )
cc - > capabilities_ext = bcma_cc_read32 ( cc , BCMA_CC_CAP_EXT ) ;
2012-09-29 22:29:49 +04:00
if ( cc - > capabilities & BCMA_CC_CAP_PMU )
bcma_pmu_early_init ( cc ) ;
2016-02-12 12:15:45 +03:00
if ( bus - > hosttype = = BCMA_HOSTTYPE_SOC )
bcma_core_chipcommon_flash_detect ( cc ) ;
2012-09-29 22:29:49 +04:00
cc - > early_setup_done = true ;
}
void bcma_core_chipcommon_init ( struct bcma_drv_cc * cc )
{
u32 leddc_on = 10 ;
u32 leddc_off = 90 ;
if ( cc - > setup_done )
return ;
bcma_core_chipcommon_early_init ( cc ) ;
2011-05-11 04:08:09 +04:00
if ( cc - > core - > id . rev > = 20 ) {
2013-06-26 12:02:11 +04:00
u32 pullup = 0 , pulldown = 0 ;
if ( cc - > core - > bus - > chipinfo . id = = BCMA_CHIP_ID_BCM43142 ) {
pullup = 0x402e0 ;
pulldown = 0x20500 ;
}
bcma_cc_write32 ( cc , BCMA_CC_GPIOPULLUP , pullup ) ;
bcma_cc_write32 ( cc , BCMA_CC_GPIOPULLDOWN , pulldown ) ;
2011-05-11 04:08:09 +04:00
}
2011-05-09 20:56:46 +04:00
if ( cc - > capabilities & BCMA_CC_CAP_PMU )
bcma_pmu_init ( cc ) ;
if ( cc - > capabilities & BCMA_CC_CAP_PCTL )
2012-07-06 00:07:32 +04:00
bcma_err ( cc - > core - > bus , " Power control not implemented! \n " ) ;
2011-07-14 23:49:19 +04:00
if ( cc - > core - > id . rev > = 16 ) {
if ( cc - > core - > bus - > sprom . leddc_on_time & &
cc - > core - > bus - > sprom . leddc_off_time ) {
leddc_on = cc - > core - > bus - > sprom . leddc_on_time ;
leddc_off = cc - > core - > bus - > sprom . leddc_off_time ;
}
bcma_cc_write32 ( cc , BCMA_CC_GPIOTIMER ,
( ( leddc_on < < BCMA_CC_GPIOTIMER_ONTIME_SHIFT ) |
( leddc_off < < BCMA_CC_GPIOTIMER_OFFTIME_SHIFT ) ) ) ;
}
2012-12-05 21:46:01 +04:00
cc - > ticks_per_ms = bcma_chipco_watchdog_ticks_per_ms ( cc ) ;
2011-07-23 03:20:07 +04:00
cc - > setup_done = true ;
2011-05-09 20:56:46 +04:00
}
/* Set chip watchdog reset timer to fire in 'ticks' backplane cycles */
2012-12-05 21:46:01 +04:00
u32 bcma_chipco_watchdog_timer_set ( struct bcma_drv_cc * cc , u32 ticks )
2011-05-09 20:56:46 +04:00
{
2012-12-05 21:46:00 +04:00
u32 maxt ;
maxt = bcma_chipco_watchdog_get_max_timer ( cc ) ;
2016-07-25 21:33:56 +03:00
if ( bcma_core_cc_has_pmu_watchdog ( cc ) ) {
2012-12-05 21:46:00 +04:00
if ( ticks = = 1 )
ticks = 2 ;
else if ( ticks > maxt )
ticks = maxt ;
2016-01-19 10:45:26 +03:00
bcma_pmu_write32 ( cc , BCMA_CC_PMU_WATCHDOG , ticks ) ;
2012-12-05 21:46:00 +04:00
} else {
2015-01-24 02:23:21 +03:00
struct bcma_bus * bus = cc - > core - > bus ;
if ( bus - > chipinfo . id ! = BCMA_CHIP_ID_BCM4707 & &
2016-01-24 18:37:33 +03:00
bus - > chipinfo . id ! = BCMA_CHIP_ID_BCM47094 & &
2015-01-24 02:23:21 +03:00
bus - > chipinfo . id ! = BCMA_CHIP_ID_BCM53018 )
bcma_core_set_clockmode ( cc - > core ,
ticks ? BCMA_CLKMODE_FAST : BCMA_CLKMODE_DYNAMIC ) ;
2012-12-05 21:46:00 +04:00
if ( ticks > maxt )
ticks = maxt ;
/* instant NMI */
bcma_cc_write32 ( cc , BCMA_CC_WATCHDOG , ticks ) ;
}
2012-12-05 21:46:01 +04:00
return ticks ;
2011-05-09 20:56:46 +04:00
}
void bcma_chipco_irq_mask ( struct bcma_drv_cc * cc , u32 mask , u32 value )
{
bcma_cc_write32_masked ( cc , BCMA_CC_IRQMASK , mask , value ) ;
}
u32 bcma_chipco_irq_status ( struct bcma_drv_cc * cc , u32 mask )
{
return bcma_cc_read32 ( cc , BCMA_CC_IRQSTAT ) & mask ;
}
u32 bcma_chipco_gpio_in ( struct bcma_drv_cc * cc , u32 mask )
{
return bcma_cc_read32 ( cc , BCMA_CC_GPIOIN ) & mask ;
}
u32 bcma_chipco_gpio_out ( struct bcma_drv_cc * cc , u32 mask , u32 value )
{
2012-11-21 02:24:27 +04:00
unsigned long flags ;
u32 res ;
spin_lock_irqsave ( & cc - > gpio_lock , flags ) ;
res = bcma_cc_write32_masked ( cc , BCMA_CC_GPIOOUT , mask , value ) ;
spin_unlock_irqrestore ( & cc - > gpio_lock , flags ) ;
return res ;
2011-05-09 20:56:46 +04:00
}
2013-03-27 20:23:12 +04:00
EXPORT_SYMBOL_GPL ( bcma_chipco_gpio_out ) ;
2011-05-09 20:56:46 +04:00
u32 bcma_chipco_gpio_outen ( struct bcma_drv_cc * cc , u32 mask , u32 value )
{
2012-11-21 02:24:27 +04:00
unsigned long flags ;
u32 res ;
spin_lock_irqsave ( & cc - > gpio_lock , flags ) ;
res = bcma_cc_write32_masked ( cc , BCMA_CC_GPIOOUTEN , mask , value ) ;
spin_unlock_irqrestore ( & cc - > gpio_lock , flags ) ;
return res ;
2011-05-09 20:56:46 +04:00
}
2013-03-27 20:23:12 +04:00
EXPORT_SYMBOL_GPL ( bcma_chipco_gpio_outen ) ;
2011-05-09 20:56:46 +04:00
2012-11-21 02:24:29 +04:00
/*
* If the bit is set to 0 , chipcommon controlls this GPIO ,
* if the bit is set to 1 , it is used by some part of the chip and not our code .
*/
2011-05-09 20:56:46 +04:00
u32 bcma_chipco_gpio_control ( struct bcma_drv_cc * cc , u32 mask , u32 value )
{
2012-11-21 02:24:27 +04:00
unsigned long flags ;
u32 res ;
spin_lock_irqsave ( & cc - > gpio_lock , flags ) ;
res = bcma_cc_write32_masked ( cc , BCMA_CC_GPIOCTL , mask , value ) ;
spin_unlock_irqrestore ( & cc - > gpio_lock , flags ) ;
return res ;
2011-05-09 20:56:46 +04:00
}
EXPORT_SYMBOL_GPL ( bcma_chipco_gpio_control ) ;
u32 bcma_chipco_gpio_intmask ( struct bcma_drv_cc * cc , u32 mask , u32 value )
{
2012-11-21 02:24:27 +04:00
unsigned long flags ;
u32 res ;
spin_lock_irqsave ( & cc - > gpio_lock , flags ) ;
res = bcma_cc_write32_masked ( cc , BCMA_CC_GPIOIRQ , mask , value ) ;
spin_unlock_irqrestore ( & cc - > gpio_lock , flags ) ;
return res ;
2011-05-09 20:56:46 +04:00
}
u32 bcma_chipco_gpio_polarity ( struct bcma_drv_cc * cc , u32 mask , u32 value )
{
2012-11-21 02:24:27 +04:00
unsigned long flags ;
u32 res ;
spin_lock_irqsave ( & cc - > gpio_lock , flags ) ;
res = bcma_cc_write32_masked ( cc , BCMA_CC_GPIOPOL , mask , value ) ;
spin_unlock_irqrestore ( & cc - > gpio_lock , flags ) ;
return res ;
2011-05-09 20:56:46 +04:00
}
2011-07-23 03:20:10 +04:00
2012-11-21 02:24:28 +04:00
u32 bcma_chipco_gpio_pullup ( struct bcma_drv_cc * cc , u32 mask , u32 value )
{
unsigned long flags ;
u32 res ;
if ( cc - > core - > id . rev < 20 )
return 0 ;
spin_lock_irqsave ( & cc - > gpio_lock , flags ) ;
res = bcma_cc_write32_masked ( cc , BCMA_CC_GPIOPULLUP , mask , value ) ;
spin_unlock_irqrestore ( & cc - > gpio_lock , flags ) ;
return res ;
}
u32 bcma_chipco_gpio_pulldown ( struct bcma_drv_cc * cc , u32 mask , u32 value )
{
unsigned long flags ;
u32 res ;
if ( cc - > core - > id . rev < 20 )
return 0 ;
spin_lock_irqsave ( & cc - > gpio_lock , flags ) ;
res = bcma_cc_write32_masked ( cc , BCMA_CC_GPIOPULLDOWN , mask , value ) ;
spin_unlock_irqrestore ( & cc - > gpio_lock , flags ) ;
return res ;
2011-05-09 20:56:46 +04:00
}
2011-07-23 03:20:10 +04:00
2017-01-13 14:23:35 +03:00
# ifdef CONFIG_BCMA_DRIVER_MIPS
void bcma_chipco_serial_init ( struct bcma_drv_cc * cc )
2011-07-23 03:20:10 +04:00
{
unsigned int irq ;
u32 baud_base ;
u32 i ;
unsigned int ccrev = cc - > core - > id . rev ;
struct bcma_serial_port * ports = cc - > serial_ports ;
if ( ccrev > = 11 & & ccrev ! = 15 ) {
2012-12-07 15:56:56 +04:00
baud_base = bcma_chipco_get_alp_clock ( cc ) ;
2011-07-23 03:20:10 +04:00
if ( ccrev > = 21 ) {
/* Turn off UART clock before switching clocksource. */
bcma_cc_write32 ( cc , BCMA_CC_CORECTL ,
bcma_cc_read32 ( cc , BCMA_CC_CORECTL )
& ~ BCMA_CC_CORECTL_UARTCLKEN ) ;
}
/* Set the override bit so we don't divide it */
bcma_cc_write32 ( cc , BCMA_CC_CORECTL ,
bcma_cc_read32 ( cc , BCMA_CC_CORECTL )
| BCMA_CC_CORECTL_UARTCLK0 ) ;
if ( ccrev > = 21 ) {
/* Re-enable the UART clock. */
bcma_cc_write32 ( cc , BCMA_CC_CORECTL ,
bcma_cc_read32 ( cc , BCMA_CC_CORECTL )
| BCMA_CC_CORECTL_UARTCLKEN ) ;
}
} else {
2014-12-27 22:24:28 +03:00
bcma_err ( cc - > core - > bus , " serial not supported on this device ccrev: 0x%x \n " ,
ccrev ) ;
2011-07-23 03:20:10 +04:00
return ;
}
2014-11-01 18:54:55 +03:00
irq = bcma_core_irq ( cc - > core , 0 ) ;
2011-07-23 03:20:10 +04:00
/* Determine the registers of the UARTs */
cc - > nr_serial_ports = ( cc - > capabilities & BCMA_CC_CAP_NRUART ) ;
for ( i = 0 ; i < cc - > nr_serial_ports ; i + + ) {
ports [ i ] . regs = cc - > core - > io_addr + BCMA_CC_UART0_DATA +
( i * 256 ) ;
ports [ i ] . irq = irq ;
ports [ i ] . baud_base = baud_base ;
ports [ i ] . reg_shift = 0 ;
}
}
2017-01-13 14:23:35 +03:00
# endif /* CONFIG_BCMA_DRIVER_MIPS */