2023-03-30 12:16:28 +01:00
// SPDX-License-Identifier: GPL-2.0
/*
* Renesas RZ / G2L Multi - Function Timer Pulse Unit 3 ( MTU3a ) Core driver
*
* Copyright ( C ) 2023 Renesas Electronics Corporation
*/
# include <linux/bitfield.h>
# include <linux/clk.h>
# include <linux/interrupt.h>
# include <linux/irq.h>
# include <linux/mfd/core.h>
# include <linux/mfd/rz-mtu3.h>
2023-07-11 10:28:41 +01:00
# include <linux/module.h>
2023-07-14 11:47:27 -06:00
# include <linux/mod_devicetable.h>
# include <linux/platform_device.h>
2023-03-30 12:16:28 +01:00
# include <linux/reset.h>
# include <linux/spinlock.h>
# include "rz-mtu3.h"
struct rz_mtu3_priv {
void __iomem * mmio ;
struct reset_control * rstc ;
2023-08-15 08:34:45 +01:00
spinlock_t lock ;
2023-03-30 12:16:28 +01:00
} ;
/******* MTU3 registers (original offset is +0x1200) *******/
static const unsigned long rz_mtu3_8bit_ch_reg_offs [ ] [ 13 ] = {
[ RZ_MTU3_CHAN_0 ] = MTU_8BIT_CH_0 ( 0x104 , 0x090 , 0x100 , 0x128 , 0x101 , 0x102 , 0x103 , 0x126 ) ,
[ RZ_MTU3_CHAN_1 ] = MTU_8BIT_CH_1_2 ( 0x184 , 0x091 , 0x185 , 0x180 , 0x194 , 0x181 , 0x182 ) ,
[ RZ_MTU3_CHAN_2 ] = MTU_8BIT_CH_1_2 ( 0x204 , 0x092 , 0x205 , 0x200 , 0x20c , 0x201 , 0x202 ) ,
[ RZ_MTU3_CHAN_3 ] = MTU_8BIT_CH_3_4_6_7 ( 0x008 , 0x093 , 0x02c , 0x000 , 0x04c , 0x002 , 0x004 , 0x005 , 0x038 ) ,
[ RZ_MTU3_CHAN_4 ] = MTU_8BIT_CH_3_4_6_7 ( 0x009 , 0x094 , 0x02d , 0x001 , 0x04d , 0x003 , 0x006 , 0x007 , 0x039 ) ,
[ RZ_MTU3_CHAN_5 ] = MTU_8BIT_CH_5 ( 0xab2 , 0x1eb , 0xab4 , 0xab6 , 0xa84 , 0xa85 , 0xa86 , 0xa94 , 0xa95 , 0xa96 , 0xaa4 , 0xaa5 , 0xaa6 ) ,
[ RZ_MTU3_CHAN_6 ] = MTU_8BIT_CH_3_4_6_7 ( 0x808 , 0x893 , 0x82c , 0x800 , 0x84c , 0x802 , 0x804 , 0x805 , 0x838 ) ,
[ RZ_MTU3_CHAN_7 ] = MTU_8BIT_CH_3_4_6_7 ( 0x809 , 0x894 , 0x82d , 0x801 , 0x84d , 0x803 , 0x806 , 0x807 , 0x839 ) ,
[ RZ_MTU3_CHAN_8 ] = MTU_8BIT_CH_8 ( 0x404 , 0x098 , 0x400 , 0x406 , 0x401 , 0x402 , 0x403 )
} ;
static const unsigned long rz_mtu3_16bit_ch_reg_offs [ ] [ 12 ] = {
[ RZ_MTU3_CHAN_0 ] = MTU_16BIT_CH_0 ( 0x106 , 0x108 , 0x10a , 0x10c , 0x10e , 0x120 , 0x122 ) ,
[ RZ_MTU3_CHAN_1 ] = MTU_16BIT_CH_1_2 ( 0x186 , 0x188 , 0x18a ) ,
[ RZ_MTU3_CHAN_2 ] = MTU_16BIT_CH_1_2 ( 0x206 , 0x208 , 0x20a ) ,
[ RZ_MTU3_CHAN_3 ] = MTU_16BIT_CH_3_6 ( 0x010 , 0x018 , 0x01a , 0x024 , 0x026 , 0x072 ) ,
[ RZ_MTU3_CHAN_4 ] = MTU_16BIT_CH_4_7 ( 0x012 , 0x01c , 0x01e , 0x028 , 0x2a , 0x074 , 0x076 , 0x040 , 0x044 , 0x046 , 0x048 , 0x04a ) ,
[ RZ_MTU3_CHAN_5 ] = MTU_16BIT_CH_5 ( 0xa80 , 0xa82 , 0xa90 , 0xa92 , 0xaa0 , 0xaa2 ) ,
[ RZ_MTU3_CHAN_6 ] = MTU_16BIT_CH_3_6 ( 0x810 , 0x818 , 0x81a , 0x824 , 0x826 , 0x872 ) ,
[ RZ_MTU3_CHAN_7 ] = MTU_16BIT_CH_4_7 ( 0x812 , 0x81c , 0x81e , 0x828 , 0x82a , 0x874 , 0x876 , 0x840 , 0x844 , 0x846 , 0x848 , 0x84a )
} ;
static const unsigned long rz_mtu3_32bit_ch_reg_offs [ ] [ 5 ] = {
[ RZ_MTU3_CHAN_1 ] = MTU_32BIT_CH_1 ( 0x1a0 , 0x1a4 , 0x1a8 ) ,
[ RZ_MTU3_CHAN_8 ] = MTU_32BIT_CH_8 ( 0x408 , 0x40c , 0x410 , 0x414 , 0x418 )
} ;
static bool rz_mtu3_is_16bit_shared_reg ( u16 offset )
{
return ( offset = = RZ_MTU3_TDDRA | | offset = = RZ_MTU3_TDDRB | |
offset = = RZ_MTU3_TCDRA | | offset = = RZ_MTU3_TCDRB | |
offset = = RZ_MTU3_TCBRA | | offset = = RZ_MTU3_TCBRB | |
offset = = RZ_MTU3_TCNTSA | | offset = = RZ_MTU3_TCNTSB ) ;
}
u16 rz_mtu3_shared_reg_read ( struct rz_mtu3_channel * ch , u16 offset )
{
struct rz_mtu3 * mtu = dev_get_drvdata ( ch - > dev - > parent ) ;
struct rz_mtu3_priv * priv = mtu - > priv_data ;
if ( rz_mtu3_is_16bit_shared_reg ( offset ) )
return readw ( priv - > mmio + offset ) ;
else
return readb ( priv - > mmio + offset ) ;
}
EXPORT_SYMBOL_GPL ( rz_mtu3_shared_reg_read ) ;
u8 rz_mtu3_8bit_ch_read ( struct rz_mtu3_channel * ch , u16 offset )
{
struct rz_mtu3 * mtu = dev_get_drvdata ( ch - > dev - > parent ) ;
struct rz_mtu3_priv * priv = mtu - > priv_data ;
u16 ch_offs ;
ch_offs = rz_mtu3_8bit_ch_reg_offs [ ch - > channel_number ] [ offset ] ;
return readb ( priv - > mmio + ch_offs ) ;
}
EXPORT_SYMBOL_GPL ( rz_mtu3_8bit_ch_read ) ;
u16 rz_mtu3_16bit_ch_read ( struct rz_mtu3_channel * ch , u16 offset )
{
struct rz_mtu3 * mtu = dev_get_drvdata ( ch - > dev - > parent ) ;
struct rz_mtu3_priv * priv = mtu - > priv_data ;
u16 ch_offs ;
/* MTU8 doesn't have 16-bit registers */
if ( ch - > channel_number = = RZ_MTU3_CHAN_8 )
return 0 ;
ch_offs = rz_mtu3_16bit_ch_reg_offs [ ch - > channel_number ] [ offset ] ;
return readw ( priv - > mmio + ch_offs ) ;
}
EXPORT_SYMBOL_GPL ( rz_mtu3_16bit_ch_read ) ;
u32 rz_mtu3_32bit_ch_read ( struct rz_mtu3_channel * ch , u16 offset )
{
struct rz_mtu3 * mtu = dev_get_drvdata ( ch - > dev - > parent ) ;
struct rz_mtu3_priv * priv = mtu - > priv_data ;
u16 ch_offs ;
if ( ch - > channel_number ! = RZ_MTU3_CHAN_1 & & ch - > channel_number ! = RZ_MTU3_CHAN_8 )
return 0 ;
ch_offs = rz_mtu3_32bit_ch_reg_offs [ ch - > channel_number ] [ offset ] ;
return readl ( priv - > mmio + ch_offs ) ;
}
EXPORT_SYMBOL_GPL ( rz_mtu3_32bit_ch_read ) ;
void rz_mtu3_8bit_ch_write ( struct rz_mtu3_channel * ch , u16 offset , u8 val )
{
struct rz_mtu3 * mtu = dev_get_drvdata ( ch - > dev - > parent ) ;
struct rz_mtu3_priv * priv = mtu - > priv_data ;
u16 ch_offs ;
ch_offs = rz_mtu3_8bit_ch_reg_offs [ ch - > channel_number ] [ offset ] ;
writeb ( val , priv - > mmio + ch_offs ) ;
}
EXPORT_SYMBOL_GPL ( rz_mtu3_8bit_ch_write ) ;
void rz_mtu3_16bit_ch_write ( struct rz_mtu3_channel * ch , u16 offset , u16 val )
{
struct rz_mtu3 * mtu = dev_get_drvdata ( ch - > dev - > parent ) ;
struct rz_mtu3_priv * priv = mtu - > priv_data ;
u16 ch_offs ;
/* MTU8 doesn't have 16-bit registers */
if ( ch - > channel_number = = RZ_MTU3_CHAN_8 )
return ;
ch_offs = rz_mtu3_16bit_ch_reg_offs [ ch - > channel_number ] [ offset ] ;
writew ( val , priv - > mmio + ch_offs ) ;
}
EXPORT_SYMBOL_GPL ( rz_mtu3_16bit_ch_write ) ;
void rz_mtu3_32bit_ch_write ( struct rz_mtu3_channel * ch , u16 offset , u32 val )
{
struct rz_mtu3 * mtu = dev_get_drvdata ( ch - > dev - > parent ) ;
struct rz_mtu3_priv * priv = mtu - > priv_data ;
u16 ch_offs ;
if ( ch - > channel_number ! = RZ_MTU3_CHAN_1 & & ch - > channel_number ! = RZ_MTU3_CHAN_8 )
return ;
ch_offs = rz_mtu3_32bit_ch_reg_offs [ ch - > channel_number ] [ offset ] ;
writel ( val , priv - > mmio + ch_offs ) ;
}
EXPORT_SYMBOL_GPL ( rz_mtu3_32bit_ch_write ) ;
void rz_mtu3_shared_reg_write ( struct rz_mtu3_channel * ch , u16 offset , u16 value )
{
struct rz_mtu3 * mtu = dev_get_drvdata ( ch - > dev - > parent ) ;
struct rz_mtu3_priv * priv = mtu - > priv_data ;
if ( rz_mtu3_is_16bit_shared_reg ( offset ) )
writew ( value , priv - > mmio + offset ) ;
else
writeb ( ( u8 ) value , priv - > mmio + offset ) ;
}
EXPORT_SYMBOL_GPL ( rz_mtu3_shared_reg_write ) ;
void rz_mtu3_shared_reg_update_bit ( struct rz_mtu3_channel * ch , u16 offset ,
u16 pos , u8 val )
{
struct rz_mtu3 * mtu = dev_get_drvdata ( ch - > dev - > parent ) ;
struct rz_mtu3_priv * priv = mtu - > priv_data ;
unsigned long tmdr , flags ;
2023-08-15 08:34:45 +01:00
spin_lock_irqsave ( & priv - > lock , flags ) ;
2023-03-30 12:16:28 +01:00
tmdr = rz_mtu3_shared_reg_read ( ch , offset ) ;
__assign_bit ( pos , & tmdr , ! ! val ) ;
rz_mtu3_shared_reg_write ( ch , offset , tmdr ) ;
2023-08-15 08:34:45 +01:00
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
2023-03-30 12:16:28 +01:00
}
EXPORT_SYMBOL_GPL ( rz_mtu3_shared_reg_update_bit ) ;
static u16 rz_mtu3_get_tstr_offset ( struct rz_mtu3_channel * ch )
{
u16 offset ;
switch ( ch - > channel_number ) {
case RZ_MTU3_CHAN_0 :
case RZ_MTU3_CHAN_1 :
case RZ_MTU3_CHAN_2 :
case RZ_MTU3_CHAN_3 :
case RZ_MTU3_CHAN_4 :
case RZ_MTU3_CHAN_8 :
offset = RZ_MTU3_TSTRA ;
break ;
case RZ_MTU3_CHAN_5 :
offset = RZ_MTU3_TSTR ;
break ;
case RZ_MTU3_CHAN_6 :
case RZ_MTU3_CHAN_7 :
offset = RZ_MTU3_TSTRB ;
break ;
default :
offset = 0 ;
break ;
}
return offset ;
}
static u8 rz_mtu3_get_tstr_bit_pos ( struct rz_mtu3_channel * ch )
{
u8 bitpos ;
switch ( ch - > channel_number ) {
case RZ_MTU3_CHAN_0 :
case RZ_MTU3_CHAN_1 :
case RZ_MTU3_CHAN_2 :
case RZ_MTU3_CHAN_6 :
case RZ_MTU3_CHAN_7 :
bitpos = ch - > channel_number ;
break ;
case RZ_MTU3_CHAN_3 :
bitpos = 6 ;
break ;
case RZ_MTU3_CHAN_4 :
bitpos = 7 ;
break ;
case RZ_MTU3_CHAN_5 :
bitpos = 2 ;
break ;
case RZ_MTU3_CHAN_8 :
bitpos = 3 ;
break ;
default :
bitpos = 0 ;
break ;
}
return bitpos ;
}
static void rz_mtu3_start_stop_ch ( struct rz_mtu3_channel * ch , bool start )
{
struct rz_mtu3 * mtu = dev_get_drvdata ( ch - > dev - > parent ) ;
struct rz_mtu3_priv * priv = mtu - > priv_data ;
unsigned long flags , tstr ;
u16 offset ;
u8 bitpos ;
2023-08-15 08:34:44 +01:00
offset = rz_mtu3_get_tstr_offset ( ch ) ;
bitpos = rz_mtu3_get_tstr_bit_pos ( ch ) ;
2023-03-30 12:16:28 +01:00
/* start stop register shared by multiple timer channels */
2023-08-15 08:34:45 +01:00
spin_lock_irqsave ( & priv - > lock , flags ) ;
2023-03-30 12:16:28 +01:00
tstr = rz_mtu3_shared_reg_read ( ch , offset ) ;
__assign_bit ( bitpos , & tstr , start ) ;
rz_mtu3_shared_reg_write ( ch , offset , tstr ) ;
2023-08-15 08:34:45 +01:00
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
2023-03-30 12:16:28 +01:00
}
bool rz_mtu3_is_enabled ( struct rz_mtu3_channel * ch )
{
struct rz_mtu3 * mtu = dev_get_drvdata ( ch - > dev - > parent ) ;
struct rz_mtu3_priv * priv = mtu - > priv_data ;
unsigned long flags , tstr ;
u16 offset ;
u8 bitpos ;
offset = rz_mtu3_get_tstr_offset ( ch ) ;
bitpos = rz_mtu3_get_tstr_bit_pos ( ch ) ;
2023-08-15 08:34:44 +01:00
/* start stop register shared by multiple timer channels */
2023-08-15 08:34:45 +01:00
spin_lock_irqsave ( & priv - > lock , flags ) ;
2023-08-15 08:34:44 +01:00
tstr = rz_mtu3_shared_reg_read ( ch , offset ) ;
2023-08-15 08:34:45 +01:00
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
2023-03-30 12:16:28 +01:00
2023-08-15 08:34:44 +01:00
return tstr & BIT ( bitpos ) ;
2023-03-30 12:16:28 +01:00
}
EXPORT_SYMBOL_GPL ( rz_mtu3_is_enabled ) ;
int rz_mtu3_enable ( struct rz_mtu3_channel * ch )
{
/* enable channel */
rz_mtu3_start_stop_ch ( ch , true ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( rz_mtu3_enable ) ;
void rz_mtu3_disable ( struct rz_mtu3_channel * ch )
{
/* disable channel */
rz_mtu3_start_stop_ch ( ch , false ) ;
}
EXPORT_SYMBOL_GPL ( rz_mtu3_disable ) ;
static void rz_mtu3_reset_assert ( void * data )
{
struct rz_mtu3 * mtu = dev_get_drvdata ( data ) ;
struct rz_mtu3_priv * priv = mtu - > priv_data ;
mfd_remove_devices ( data ) ;
reset_control_assert ( priv - > rstc ) ;
}
static const struct mfd_cell rz_mtu3_devs [ ] = {
{
. name = " rz-mtu3-counter " ,
} ,
{
. name = " pwm-rz-mtu3 " ,
} ,
} ;
static int rz_mtu3_probe ( struct platform_device * pdev )
{
struct rz_mtu3_priv * priv ;
struct rz_mtu3 * ddata ;
unsigned int i ;
int ret ;
ddata = devm_kzalloc ( & pdev - > dev , sizeof ( * ddata ) , GFP_KERNEL ) ;
if ( ! ddata )
return - ENOMEM ;
ddata - > priv_data = devm_kzalloc ( & pdev - > dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! ddata - > priv_data )
return - ENOMEM ;
priv = ddata - > priv_data ;
priv - > mmio = devm_platform_ioremap_resource ( pdev , 0 ) ;
if ( IS_ERR ( priv - > mmio ) )
return PTR_ERR ( priv - > mmio ) ;
priv - > rstc = devm_reset_control_get_exclusive ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( priv - > rstc ) )
return PTR_ERR ( priv - > rstc ) ;
ddata - > clk = devm_clk_get ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( ddata - > clk ) )
return PTR_ERR ( ddata - > clk ) ;
reset_control_deassert ( priv - > rstc ) ;
2023-08-15 08:34:45 +01:00
spin_lock_init ( & priv - > lock ) ;
2023-03-30 12:16:28 +01:00
platform_set_drvdata ( pdev , ddata ) ;
for ( i = 0 ; i < RZ_MTU_NUM_CHANNELS ; i + + ) {
ddata - > channels [ i ] . channel_number = i ;
ddata - > channels [ i ] . is_busy = false ;
mutex_init ( & ddata - > channels [ i ] . lock ) ;
}
ret = mfd_add_devices ( & pdev - > dev , 0 , rz_mtu3_devs ,
ARRAY_SIZE ( rz_mtu3_devs ) , NULL , 0 , NULL ) ;
if ( ret < 0 )
goto err_assert ;
return devm_add_action_or_reset ( & pdev - > dev , rz_mtu3_reset_assert ,
& pdev - > dev ) ;
err_assert :
reset_control_assert ( priv - > rstc ) ;
return ret ;
}
static const struct of_device_id rz_mtu3_of_match [ ] = {
{ . compatible = " renesas,rz-mtu3 " , } ,
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( of , rz_mtu3_of_match ) ;
static struct platform_driver rz_mtu3_driver = {
. probe = rz_mtu3_probe ,
. driver = {
. name = " rz-mtu3 " ,
. of_match_table = rz_mtu3_of_match ,
} ,
} ;
module_platform_driver ( rz_mtu3_driver ) ;
MODULE_AUTHOR ( " Biju Das <biju.das.jz@bp.renesas.com> " ) ;
MODULE_DESCRIPTION ( " Renesas RZ/G2L MTU3a Core Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;