2022-09-30 01:53:59 +03:00
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright ( C ) 2021 BAIKAL ELECTRONICS , JSC
*
* Authors :
* Serge Semin < Sergey . Semin @ baikalelectronics . ru >
*
* Baikal - T1 CCU Resets interface driver
*/
# define pr_fmt(fmt) "bt1-ccu-rst: " fmt
# include <linux/bits.h>
# include <linux/delay.h>
# include <linux/kernel.h>
# include <linux/of.h>
# include <linux/printk.h>
# include <linux/regmap.h>
# include <linux/reset-controller.h>
# include <linux/slab.h>
# include <dt-bindings/reset/bt1-ccu.h>
# include "ccu-rst.h"
# define CCU_AXI_MAIN_BASE 0x030
# define CCU_AXI_DDR_BASE 0x034
# define CCU_AXI_SATA_BASE 0x038
# define CCU_AXI_GMAC0_BASE 0x03C
# define CCU_AXI_GMAC1_BASE 0x040
# define CCU_AXI_XGMAC_BASE 0x044
# define CCU_AXI_PCIE_M_BASE 0x048
# define CCU_AXI_PCIE_S_BASE 0x04C
# define CCU_AXI_USB_BASE 0x050
# define CCU_AXI_HWA_BASE 0x054
# define CCU_AXI_SRAM_BASE 0x058
2022-09-30 01:54:01 +03:00
# define CCU_SYS_DDR_BASE 0x02c
2022-09-30 01:53:59 +03:00
# define CCU_SYS_SATA_REF_BASE 0x060
# define CCU_SYS_APB_BASE 0x064
2022-09-30 01:54:01 +03:00
# define CCU_SYS_PCIE_BASE 0x144
2022-09-30 01:53:59 +03:00
# define CCU_RST_DELAY_US 1
# define CCU_RST_TRIG(_base, _ofs) \
{ \
2022-09-30 01:54:01 +03:00
. type = CCU_RST_TRIG , \
. base = _base , \
. mask = BIT ( _ofs ) , \
}
# define CCU_RST_DIR(_base, _ofs) \
{ \
. type = CCU_RST_DIR , \
2022-09-30 01:53:59 +03:00
. base = _base , \
. mask = BIT ( _ofs ) , \
}
struct ccu_rst_info {
2022-09-30 01:54:01 +03:00
enum ccu_rst_type type ;
2022-09-30 01:53:59 +03:00
unsigned int base ;
unsigned int mask ;
} ;
/*
* Each AXI - bus clock divider is equipped with the corresponding clock - consumer
* domain reset ( it ' s self - deasserted reset control ) .
*/
static const struct ccu_rst_info axi_rst_info [ ] = {
[ CCU_AXI_MAIN_RST ] = CCU_RST_TRIG ( CCU_AXI_MAIN_BASE , 1 ) ,
[ CCU_AXI_DDR_RST ] = CCU_RST_TRIG ( CCU_AXI_DDR_BASE , 1 ) ,
[ CCU_AXI_SATA_RST ] = CCU_RST_TRIG ( CCU_AXI_SATA_BASE , 1 ) ,
[ CCU_AXI_GMAC0_RST ] = CCU_RST_TRIG ( CCU_AXI_GMAC0_BASE , 1 ) ,
[ CCU_AXI_GMAC1_RST ] = CCU_RST_TRIG ( CCU_AXI_GMAC1_BASE , 1 ) ,
[ CCU_AXI_XGMAC_RST ] = CCU_RST_TRIG ( CCU_AXI_XGMAC_BASE , 1 ) ,
[ CCU_AXI_PCIE_M_RST ] = CCU_RST_TRIG ( CCU_AXI_PCIE_M_BASE , 1 ) ,
[ CCU_AXI_PCIE_S_RST ] = CCU_RST_TRIG ( CCU_AXI_PCIE_S_BASE , 1 ) ,
[ CCU_AXI_USB_RST ] = CCU_RST_TRIG ( CCU_AXI_USB_BASE , 1 ) ,
[ CCU_AXI_HWA_RST ] = CCU_RST_TRIG ( CCU_AXI_HWA_BASE , 1 ) ,
[ CCU_AXI_SRAM_RST ] = CCU_RST_TRIG ( CCU_AXI_SRAM_BASE , 1 ) ,
} ;
/*
* SATA reference clock domain and APB - bus domain are connected with the
* sefl - deasserted reset control , which can be activated via the corresponding
* clock divider register . DDR and PCIe sub - domains can be reset with directly
* controlled reset signals . Resetting the DDR controller though won ' t end up
* well while the Linux kernel is working .
*/
static const struct ccu_rst_info sys_rst_info [ ] = {
[ CCU_SYS_SATA_REF_RST ] = CCU_RST_TRIG ( CCU_SYS_SATA_REF_BASE , 1 ) ,
[ CCU_SYS_APB_RST ] = CCU_RST_TRIG ( CCU_SYS_APB_BASE , 1 ) ,
2022-09-30 01:54:01 +03:00
[ CCU_SYS_DDR_FULL_RST ] = CCU_RST_DIR ( CCU_SYS_DDR_BASE , 1 ) ,
[ CCU_SYS_DDR_INIT_RST ] = CCU_RST_DIR ( CCU_SYS_DDR_BASE , 2 ) ,
[ CCU_SYS_PCIE_PCS_PHY_RST ] = CCU_RST_DIR ( CCU_SYS_PCIE_BASE , 0 ) ,
[ CCU_SYS_PCIE_PIPE0_RST ] = CCU_RST_DIR ( CCU_SYS_PCIE_BASE , 4 ) ,
[ CCU_SYS_PCIE_CORE_RST ] = CCU_RST_DIR ( CCU_SYS_PCIE_BASE , 8 ) ,
[ CCU_SYS_PCIE_PWR_RST ] = CCU_RST_DIR ( CCU_SYS_PCIE_BASE , 9 ) ,
[ CCU_SYS_PCIE_STICKY_RST ] = CCU_RST_DIR ( CCU_SYS_PCIE_BASE , 10 ) ,
[ CCU_SYS_PCIE_NSTICKY_RST ] = CCU_RST_DIR ( CCU_SYS_PCIE_BASE , 11 ) ,
[ CCU_SYS_PCIE_HOT_RST ] = CCU_RST_DIR ( CCU_SYS_PCIE_BASE , 12 ) ,
2022-09-30 01:53:59 +03:00
} ;
static int ccu_rst_reset ( struct reset_controller_dev * rcdev , unsigned long idx )
{
struct ccu_rst * rst = to_ccu_rst ( rcdev ) ;
const struct ccu_rst_info * info = & rst - > rsts_info [ idx ] ;
2022-09-30 01:54:01 +03:00
if ( info - > type ! = CCU_RST_TRIG )
return - EOPNOTSUPP ;
2022-09-30 01:53:59 +03:00
regmap_update_bits ( rst - > sys_regs , info - > base , info - > mask , info - > mask ) ;
/* The next delay must be enough to cover all the resets. */
udelay ( CCU_RST_DELAY_US ) ;
return 0 ;
}
2022-09-30 01:54:01 +03:00
static int ccu_rst_set ( struct reset_controller_dev * rcdev ,
unsigned long idx , bool high )
{
struct ccu_rst * rst = to_ccu_rst ( rcdev ) ;
const struct ccu_rst_info * info = & rst - > rsts_info [ idx ] ;
if ( info - > type ! = CCU_RST_DIR )
return high ? - EOPNOTSUPP : 0 ;
return regmap_update_bits ( rst - > sys_regs , info - > base ,
info - > mask , high ? info - > mask : 0 ) ;
}
static int ccu_rst_assert ( struct reset_controller_dev * rcdev ,
unsigned long idx )
{
return ccu_rst_set ( rcdev , idx , true ) ;
}
static int ccu_rst_deassert ( struct reset_controller_dev * rcdev ,
unsigned long idx )
{
return ccu_rst_set ( rcdev , idx , false ) ;
}
static int ccu_rst_status ( struct reset_controller_dev * rcdev ,
unsigned long idx )
{
struct ccu_rst * rst = to_ccu_rst ( rcdev ) ;
const struct ccu_rst_info * info = & rst - > rsts_info [ idx ] ;
u32 val ;
if ( info - > type ! = CCU_RST_DIR )
return - EOPNOTSUPP ;
regmap_read ( rst - > sys_regs , info - > base , & val ) ;
return ! ! ( val & info - > mask ) ;
}
2022-09-30 01:53:59 +03:00
static const struct reset_control_ops ccu_rst_ops = {
. reset = ccu_rst_reset ,
2022-09-30 01:54:01 +03:00
. assert = ccu_rst_assert ,
. deassert = ccu_rst_deassert ,
. status = ccu_rst_status ,
2022-09-30 01:53:59 +03:00
} ;
struct ccu_rst * ccu_rst_hw_register ( const struct ccu_rst_init_data * rst_init )
{
struct ccu_rst * rst ;
int ret ;
if ( ! rst_init )
return ERR_PTR ( - EINVAL ) ;
rst = kzalloc ( sizeof ( * rst ) , GFP_KERNEL ) ;
if ( ! rst )
return ERR_PTR ( - ENOMEM ) ;
rst - > sys_regs = rst_init - > sys_regs ;
if ( of_device_is_compatible ( rst_init - > np , " baikal,bt1-ccu-axi " ) ) {
rst - > rcdev . nr_resets = ARRAY_SIZE ( axi_rst_info ) ;
rst - > rsts_info = axi_rst_info ;
} else if ( of_device_is_compatible ( rst_init - > np , " baikal,bt1-ccu-sys " ) ) {
rst - > rcdev . nr_resets = ARRAY_SIZE ( sys_rst_info ) ;
rst - > rsts_info = sys_rst_info ;
} else {
pr_err ( " Incompatible DT node '%s' specified \n " ,
of_node_full_name ( rst_init - > np ) ) ;
ret = - EINVAL ;
goto err_kfree_rst ;
}
rst - > rcdev . owner = THIS_MODULE ;
rst - > rcdev . ops = & ccu_rst_ops ;
rst - > rcdev . of_node = rst_init - > np ;
ret = reset_controller_register ( & rst - > rcdev ) ;
if ( ret ) {
pr_err ( " Couldn't register '%s' reset controller \n " ,
of_node_full_name ( rst_init - > np ) ) ;
goto err_kfree_rst ;
}
return rst ;
err_kfree_rst :
kfree ( rst ) ;
return ERR_PTR ( ret ) ;
}
void ccu_rst_hw_unregister ( struct ccu_rst * rst )
{
reset_controller_unregister ( & rst - > rcdev ) ;
kfree ( rst ) ;
}