Hi Lee Jones, Thanks for the feedback. > Subject: Re: [PATCH v13 2/6] mfd: Add Renesas RZ/G2L MTU3a core driver > > On Sat, 04 Mar 2023, Lee Jones wrote: > > > On Thu, 16 Feb 2023, Biju Das wrote: > > > > > The RZ/G2L multi-function timer pulse unit 3 (MTU3a) is embedded in > > > the Renesas RZ/G2L family SoCs. It consists of eight 16-bit timer > > > channels and one 32-bit timer channel. It supports the following > > > functions > > > - Counter > > > - Timer > > > - PWM > > > > > > The 8/16/32 bit registers are mixed in each channel. > > > > > > Add MTU3a core driver for RZ/G2L SoC. The core driver shares the clk > > > and channel register access for the other child devices like > > > Counter, PWM and Clock event. > > > > > > Signed-off-by: Biju Das <biju.das.jz@xxxxxxxxxxxxxx> > > > --- > > > Ref: > > > > > > > > > v12->v13: > > > * Moved RZ_MTU3_TMDR1_* macros from pwm driver to rz-mtu3.h. > > > v11->v2: > > > * Moved the core driver from timer to MFD. > > > * Moved header fine from clocksource/rz-mtu3.h->linux/mfd/rz-mtu3.h > > > * Removed Select MFD_CORE option from config. > > > v10->v11: > > > * No change. > > > v9->v10: > > > * No change. > > > v8->v9: > > > * No change. > > > v7->v8: > > > * Add locking for RMW on rz_mtu3_shared_reg_update_bit() > > > * Replaced enum rz_mtu3_functions with channel busy flag > > > * Added API for request and release a channel. > > > v6->v7: > > > * Added channel specific mutex to avoid races between child devices > > > (for eg: pwm and counter) > > > * Added rz_mtu3_shared_reg_update_bit() to update bit. > > > v5->v6: > > > * Updated commit and KConfig description > > > * Selected MFD_CORE to avoid build error if CONFIG_MFD_CORE not set. > > > * Improved error handling in probe(). > > > * Updated MODULE_DESCRIPTION and title. > > > v4->v5: > > > * Moved core driver from MFD to timer > > > * Child devices instatiated using mfd_add_devices() > > > v3->v4: > > > * A single driver that registers both the counter and the pwm > functionalities > > > that binds against "renesas,rz-mtu3". > > > * Moved PM handling from child devices to here. > > > * replaced include/linux/mfd/rz-mtu3.h->drivers/mfd/rz-mtu3.h > > > * Removed "remove" callback > > > v2->v3: > > > * removed unwanted header files > > > * Added LUT for 32 bit registers as it needed for 32-bit cascade > counting. > > > * Exported 32 bit read/write functions. > > > v1->v2: > > > * Changed the compatible name > > > * Replaced devm_reset_control_get->devm_reset_control_get_exclusive > > > * Renamed function names rzg2l_mtu3->rz_mtu3 as this is generic IP > > > in RZ family SoC's. > > > --- > > > drivers/mfd/Kconfig | 10 + > > > drivers/mfd/Makefile | 1 + > > > drivers/mfd/rz-mtu3.c | 458 ++++++++++++++++++++++++++++++++++++ > > > include/linux/mfd/rz-mtu3.h | 243 +++++++++++++++++++ > > > 4 files changed, 712 insertions(+) > > > create mode 100644 drivers/mfd/rz-mtu3.c create mode 100644 > > > include/linux/mfd/rz-mtu3.h > > > > > > diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index > > > fcc141e067b9..e16c550c5b05 100644 > > > --- a/drivers/mfd/Kconfig > > > +++ b/drivers/mfd/Kconfig > > > @@ -1308,6 +1308,16 @@ config MFD_SC27XX_PMIC > > > This driver provides common support for accessing the SC27xx PMICs, > > > and it also adds the irq_chip parts for handling the PMIC chip > events. > > > > > > +config RZ_MTU3 > > > + bool "Renesas RZ/G2L MTU3a core driver" > > > + depends on (ARCH_RZG2L && OF) || COMPILE_TEST > > > + help > > > + Select this option to enable Renesas RZ/G2L MTU3a core driver for > > > + the Multi-Function Timer Pulse Unit 3 (MTU3a) hardware available > > > + on SoCs from Renesas. The core driver shares the clk and channel > > > + register access for the other child devices like Counter, PWM, > > > + Clock Source, and Clock event. > > > + > > > config ABX500_CORE > > > bool "ST-Ericsson ABX500 Mixed Signal Circuit register functions" > > > depends on ARCH_U8500 || COMPILE_TEST diff --git > > > a/drivers/mfd/Makefile b/drivers/mfd/Makefile index > > > 2f6c89d1e277..1d2392f06f78 100644 > > > --- a/drivers/mfd/Makefile > > > +++ b/drivers/mfd/Makefile > > > @@ -174,6 +174,7 @@ pcf50633-objs := pcf50633-core.o > pcf50633-irq.o > > > obj-$(CONFIG_MFD_PCF50633) += pcf50633.o > > > obj-$(CONFIG_PCF50633_ADC) += pcf50633-adc.o > > > obj-$(CONFIG_PCF50633_GPIO) += pcf50633-gpio.o > > > +obj-$(CONFIG_RZ_MTU3) += rz-mtu3.o > > > obj-$(CONFIG_ABX500_CORE) += abx500-core.o > > > obj-$(CONFIG_MFD_DB8500_PRCMU) += db8500-prcmu.o > > > # ab8500-core need to come after db8500-prcmu (which provides the > > > channel) diff --git a/drivers/mfd/rz-mtu3.c b/drivers/mfd/rz-mtu3.c > > > new file mode 100644 index 000000000000..ad6bb6b28694 > > > --- /dev/null > > > +++ b/drivers/mfd/rz-mtu3.c > > > @@ -0,0 +1,458 @@ > > > +// 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> > > > +#include <linux/of_platform.h> > > > +#include <linux/reset.h> > > > +#include <linux/spinlock.h> > > > + > > > +static const unsigned long rz_mtu3_8bit_ch_reg_offs[][13] = { > > > + { > > > + [RZ_MTU3_TIER] = 0x4, [RZ_MTU3_NFCR] = 0x70, > > > + [RZ_MTU3_TCR] = 0x0, [RZ_MTU3_TCR2] = 0x28, > > > + [RZ_MTU3_TMDR1] = 0x1, [RZ_MTU3_TIORH] = 0x2, > > > + [RZ_MTU3_TIORL] = 0x3 > > > > Instead of having this huge, fragile (ordered) slab list (which I will > > request to be amended to a single entry per line), please consider > > implementing a (3?) few helper MACROS, stored in a header, along the > > lines of: > > > > MTU_8BIT_<BLAH>(_tier, _nfcr, ... <blah>) \ > > { > > [RZ_MTU3_TIER] = _tier, > > [RZ_MTU3_NFCR] = _nfcr, > > <blah> > > } > > > > Then in here, do: > > > > [MTU3_CHAN_x] = MTU_8BIT_<BLAH>(0x04, 0x70 ... <blah>) > > > > Notice how we're being explicit about the which values associate with > > which channel here too. This eradicates the fragility of an ordered > > structure. > > Also, have you considered: devm_regmap_field_bulk_alloc()? > > See: REG_FIELD() Yes, I have tried, but it will results in more memory usage and more code because of non-uniformity of register offset between the channels and 8/16/32 bit regs are mixed with in each channel. I have cleaned up the core code based your previous suggestion and added some improvements 1) Added struct rz_mtu3_priv specific to core driver 2) Added real offset and eliminated all channel magic in read/write function rz_mtu3_8bit_ch_{read, write}, rz_mtu3_16bit_ch_read{read,write}, rz_mtu3_32bit_ch_{read,write} rz_mtu3_start_stop_ch() and rz_mtu3_is_enabled() 3) removed channel specific ch_reg_offsets 4) Added "rz-mtu3.h" and moved the macros there. 5) Renamed the enums RZ_MTU* with RZ_MTU3_CHAN_* Now changes looks like below drivers/mfd/rz-mtu3.c | 441 +++++++++++++++--------------------- drivers/mfd/rz-mtu3.h | 147 ++++++++++++ include/linux/mfd/rz-mtu3.h | 43 ++-- 3 files changed, 352 insertions(+), 279 deletions(-) create mode 100644 drivers/mfd/rz-mtu3.h diff --git a/drivers/mfd/rz-mtu3.c b/drivers/mfd/rz-mtu3.c index ad6bb6b28694..49a57a70cd7f 100644 --- a/drivers/mfd/rz-mtu3.c +++ b/drivers/mfd/rz-mtu3.c @@ -15,341 +15,271 @@ #include <linux/reset.h> #include <linux/spinlock.h> +#include "rz-mtu3.h" + +struct rz_mtu3_priv { + void __iomem *mmio; + struct reset_control *rstc; + raw_spinlock_t lock; +}; + +/******* MTU3 registers (original offset is +0x1200) *******/ static const unsigned long rz_mtu3_8bit_ch_reg_offs[][13] = { - { - [RZ_MTU3_TIER] = 0x4, [RZ_MTU3_NFCR] = 0x70, - [RZ_MTU3_TCR] = 0x0, [RZ_MTU3_TCR2] = 0x28, - [RZ_MTU3_TMDR1] = 0x1, [RZ_MTU3_TIORH] = 0x2, - [RZ_MTU3_TIORL] = 0x3 - }, - { - [RZ_MTU3_TIER] = 0x4, [RZ_MTU3_NFCR] = 0xef, - [RZ_MTU3_TSR] = 0x5, [RZ_MTU3_TCR] = 0x0, - [RZ_MTU3_TCR2] = 0x14, [RZ_MTU3_TMDR1] = 0x1, - [RZ_MTU3_TIOR] = 0x2 - }, - { - [RZ_MTU3_TIER] = 0x4, [RZ_MTU3_NFCR] = 0x16e, - [RZ_MTU3_TSR] = 0x5, [RZ_MTU3_TCR] = 0x0, - [RZ_MTU3_TCR2] = 0xc, [RZ_MTU3_TMDR1] = 0x1, - [RZ_MTU3_TIOR] = 0x2 - }, - { - [RZ_MTU3_TIER] = 0x8, [RZ_MTU3_NFCR] = 0x93, - [RZ_MTU3_TSR] = 0x2c, [RZ_MTU3_TCR] = 0x0, - [RZ_MTU3_TCR2] = 0x4c, [RZ_MTU3_TMDR1] = 0x2, - [RZ_MTU3_TIORH] = 0x4, [RZ_MTU3_TIORL] = 0x5, - [RZ_MTU3_TBTM] = 0x38 - }, - { - [RZ_MTU3_TIER] = 0x8, [RZ_MTU3_NFCR] = 0x93, - [RZ_MTU3_TSR] = 0x2c, [RZ_MTU3_TCR] = 0x0, - [RZ_MTU3_TCR2] = 0x4c, [RZ_MTU3_TMDR1] = 0x2, - [RZ_MTU3_TIORH] = 0x5, [RZ_MTU3_TIORL] = 0x6, - [RZ_MTU3_TBTM] = 0x38 - }, - { - [RZ_MTU3_TIER] = 0x32, [RZ_MTU3_NFCR] = 0x1eb, - [RZ_MTU3_TSTR] = 0x34, [RZ_MTU3_TCNTCMPCLR] = 0x36, - [RZ_MTU3_TCRU] = 0x4, [RZ_MTU3_TCR2U] = 0x5, - [RZ_MTU3_TIORU] = 0x6, [RZ_MTU3_TCRV] = 0x14, - [RZ_MTU3_TCR2V] = 0x15, [RZ_MTU3_TIORV] = 0x16, - [RZ_MTU3_TCRW] = 0x24, [RZ_MTU3_TCR2W] = 0x25, - [RZ_MTU3_TIORW] = 0x26 - }, - { - [RZ_MTU3_TIER] = 0x8, [RZ_MTU3_NFCR] = 0x93, - [RZ_MTU3_TSR] = 0x2c, [RZ_MTU3_TCR] = 0x0, - [RZ_MTU3_TCR2] = 0x4c, [RZ_MTU3_TMDR1] = 0x2, - [RZ_MTU3_TIORH] = 0x4, [RZ_MTU3_TIORL] = 0x5, - [RZ_MTU3_TBTM] = 0x38 - }, - { - [RZ_MTU3_TIER] = 0x8, [RZ_MTU3_NFCR] = 0x93, - [RZ_MTU3_TSR] = 0x2c, [RZ_MTU3_TCR] = 0x0, - [RZ_MTU3_TCR2] = 0x4c, [RZ_MTU3_TMDR1] = 0x2, - [RZ_MTU3_TIORH] = 0x5, [RZ_MTU3_TIORL] = 0x6, - [RZ_MTU3_TBTM] = 0x38 - }, - { - [RZ_MTU3_TIER] = 0x4, [RZ_MTU3_NFCR] = 0x368, - [RZ_MTU3_TCR] = 0x0, [RZ_MTU3_TCR2] = 0x6, - [RZ_MTU3_TMDR1] = 0x1, [RZ_MTU3_TIORH] = 0x2, - [RZ_MTU3_TIORL] = 0x3 - } + [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_TCNT] = 0x6, [RZ_MTU3_TGRA] = 0x8, - [RZ_MTU3_TGRB] = 0xa, [RZ_MTU3_TGRC] = 0xc, - [RZ_MTU3_TGRD] = 0xe, [RZ_MTU3_TGRE] = 0x20, - [RZ_MTU3_TGRF] = 0x22 - }, - { - [RZ_MTU3_TCNT] = 0x6, [RZ_MTU3_TGRA] = 0x8, - [RZ_MTU3_TGRB] = 0xa - }, - { - [RZ_MTU3_TCNT] = 0x6, [RZ_MTU3_TGRA] = 0x8, - [RZ_MTU3_TGRB] = 0xa - }, - { - [RZ_MTU3_TCNT] = 0x10, [RZ_MTU3_TGRA] = 0x18, - [RZ_MTU3_TGRB] = 0x1a, [RZ_MTU3_TGRC] = 0x24, - [RZ_MTU3_TGRD] = 0x26, [RZ_MTU3_TGRE] = 0x72 - }, - { - [RZ_MTU3_TCNT] = 0x11, [RZ_MTU3_TGRA] = 0x1b, - [RZ_MTU3_TGRB] = 0x1d, [RZ_MTU3_TGRC] = 0x27, - [RZ_MTU3_TGRD] = 0x29, [RZ_MTU3_TGRE] = 0x73, - [RZ_MTU3_TGRF] = 0x75, [RZ_MTU3_TADCR] = 0x3f, - [RZ_MTU3_TADCORA] = 0x43, [RZ_MTU3_TADCORB] = 0x45, - [RZ_MTU3_TADCOBRA] = 0x47, - [RZ_MTU3_TADCOBRB] = 0x49 - }, - { - [RZ_MTU3_TCNTU] = 0x0, [RZ_MTU3_TGRU] = 0x2, - [RZ_MTU3_TCNTV] = 0x10, [RZ_MTU3_TGRV] = 0x12, - [RZ_MTU3_TCNTW] = 0x20, [RZ_MTU3_TGRW] = 0x22 - }, - { - [RZ_MTU3_TCNT] = 0x10, [RZ_MTU3_TGRA] = 0x18, - [RZ_MTU3_TGRB] = 0x1a, [RZ_MTU3_TGRC] = 0x24, - [RZ_MTU3_TGRD] = 0x26, [RZ_MTU3_TGRE] = 0x72 - }, - { - [RZ_MTU3_TCNT] = 0x11, [RZ_MTU3_TGRA] = 0x1b, - [RZ_MTU3_TGRB] = 0x1d, [RZ_MTU3_TGRC] = 0x27, - [RZ_MTU3_TGRD] = 0x29, [RZ_MTU3_TGRE] = 0x73, - [RZ_MTU3_TGRF] = 0x75, [RZ_MTU3_TADCR] = 0x3f, - [RZ_MTU3_TADCORA] = 0x43, [RZ_MTU3_TADCORB] = 0x45, - [RZ_MTU3_TADCOBRA] = 0x47, - [RZ_MTU3_TADCOBRB] = 0x49 - }, + [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, 0x01b, 0x01c, 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(0x811, 0x81c, 0x81e, 0x828, 0x82a, 0x874, 0x876, 0x840, 0x844, 0x846, 0x848, 0x84a) }; static const unsigned long rz_mtu3_32bit_ch_reg_offs[][5] = { - { - [RZ_MTU3_TCNTLW] = 0x20, [RZ_MTU3_TGRALW] = 0x24, - [RZ_MTU3_TGRBLW] = 0x28 - }, - { [RZ_MTU3_TCNT] = 0x8, [RZ_MTU3_TGRA] = 0xc, - [RZ_MTU3_TGRB] = 0x10, [RZ_MTU3_TGRC] = 0x14, - [RZ_MTU3_TGRD] = 0x18 - } + [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 off) +static bool rz_mtu3_is_16bit_shared_reg(u16 offset) { - return (off == RZ_MTU3_TDDRA || off == RZ_MTU3_TDDRB || - off == RZ_MTU3_TCDRA || off == RZ_MTU3_TCDRB || - off == RZ_MTU3_TCBRA || off == RZ_MTU3_TCBRB || - off == RZ_MTU3_TCNTSA || off == RZ_MTU3_TCNTSB); + 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 off) +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(off)) - return readw(mtu->mmio + off); + if (rz_mtu3_is_16bit_shared_reg(offset)) + return readw(priv->mmio + offset); else - return readb(mtu->mmio + off); + 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 off) +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->index][off]; - if (off != RZ_MTU3_TCR && ch_offs == 0) - return -EINVAL; - - /* - * NFCR register addresses on MTU{0,1,2,5,8} channels are smaller than - * channel's base address. - */ - if (off == RZ_MTU3_NFCR && (ch->index <= RZ_MTU2 || - ch->index == RZ_MTU5 || - ch->index == RZ_MTU8)) - return readb(ch->base - ch_offs); - else - return readb(ch->base + 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 off) +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->index == RZ_MTU8) + if (ch->channel_number == RZ_MTU3_CHAN_8) return 0; - ch_offs = rz_mtu3_16bit_ch_reg_offs[ch->index][off]; - if (ch->index != RZ_MTU5 && off != RZ_MTU3_TCNTU && ch_offs == 0) - return 0; + ch_offs = rz_mtu3_16bit_ch_reg_offs[ch->channel_number][offset]; - return readw(ch->base + ch_offs); + 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 off) +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->index == RZ_MTU1) - ch_offs = rz_mtu3_32bit_ch_reg_offs[0][off]; - else if (ch->index == RZ_MTU8) - ch_offs = rz_mtu3_32bit_ch_reg_offs[1][off]; + if (ch->channel_number != RZ_MTU3_CHAN_1 && (ch->channel_number != RZ_MTU3_CHAN_8)) + return 0; - if (!ch_offs) - return -EINVAL; + ch_offs = rz_mtu3_32bit_ch_reg_offs[ch->channel_number][offset]; - return readl(ch->base + ch_offs); + 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 off, u8 val) +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->index][off]; - if (ch->index != RZ_MTU5 && off != RZ_MTU3_TCR && ch_offs == 0) - return; - - /* - * NFCR register addresses on MTU{0,1,2,5,8} channels are smaller than - * channel's base address. - */ - if (off == RZ_MTU3_NFCR && (ch->index <= RZ_MTU2 || - ch->index == RZ_MTU5 || - ch->index == RZ_MTU8)) - writeb(val, ch->base - ch_offs); - else - writeb(val, ch->base + 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 off, u16 val) +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->index == RZ_MTU8) + if (ch->channel_number == RZ_MTU3_CHAN_8) return; - ch_offs = rz_mtu3_16bit_ch_reg_offs[ch->index][off]; - if (ch->index != RZ_MTU5 && off != RZ_MTU3_TCNTU && ch_offs == 0) - return; - - writew(val, ch->base + ch_offs); + 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 off, u32 val) +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->index == RZ_MTU1) - ch_offs = rz_mtu3_32bit_ch_reg_offs[0][off]; - else if (ch->index == RZ_MTU8) - ch_offs = rz_mtu3_32bit_ch_reg_offs[1][off]; - - if (!ch_offs) + if (ch->channel_number != RZ_MTU3_CHAN_1 && (ch->channel_number != RZ_MTU3_CHAN_8)) return; - writel(val, ch->base + ch_offs); + 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 off, u16 value) +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(off)) - writew(value, mtu->mmio + off); + if (rz_mtu3_is_16bit_shared_reg(offset)) + writew(value, priv->mmio + offset); else - writeb((u8)value, mtu->mmio + off); + 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 off, +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; - raw_spin_lock_irqsave(&mtu->lock, flags); - tmdr = rz_mtu3_shared_reg_read(ch, off); + raw_spin_lock_irqsave(&priv->lock, flags); + tmdr = rz_mtu3_shared_reg_read(ch, offset); __assign_bit(pos, &tmdr, !!val); - rz_mtu3_shared_reg_write(ch, off, tmdr); - raw_spin_unlock_irqrestore(&mtu->lock, flags); + rz_mtu3_shared_reg_write(ch, offset, tmdr); + raw_spin_unlock_irqrestore(&priv->lock, flags); } 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); - unsigned long flags, value; - u8 offs; + struct rz_mtu3_priv *priv = mtu->priv_data; + unsigned long flags, tstr; + u16 offset; + u8 bitpos; /* start stop register shared by multiple timer channels */ - raw_spin_lock_irqsave(&mtu->lock, flags); - - if (ch->index == RZ_MTU6 || ch->index == RZ_MTU7) { - value = rz_mtu3_shared_reg_read(ch, RZ_MTU3_TSTRB); - if (start) - value |= 1 << ch->index; - else - value &= ~(1 << ch->index); - rz_mtu3_shared_reg_write(ch, RZ_MTU3_TSTRB, value); - } else if (ch->index != RZ_MTU5) { - value = rz_mtu3_shared_reg_read(ch, RZ_MTU3_TSTRA); - if (ch->index == RZ_MTU8) - offs = 0x08; - else if (ch->index < RZ_MTU3) - offs = 1 << ch->index; - else - offs = 1 << (ch->index + 3); - if (start) - value |= offs; - else - value &= ~offs; - rz_mtu3_shared_reg_write(ch, RZ_MTU3_TSTRA, value); - } + raw_spin_lock_irqsave(&priv->lock, flags); + + offset = rz_mtu3_get_tstr_offset(ch); + bitpos = rz_mtu3_get_tstr_bit_pos(ch); + tstr = rz_mtu3_shared_reg_read(ch, offset); + __assign_bit(bitpos, &tstr, start); + rz_mtu3_shared_reg_write(ch, offset, tstr); - raw_spin_unlock_irqrestore(&mtu->lock, flags); + raw_spin_unlock_irqrestore(&priv->lock, flags); } bool rz_mtu3_is_enabled(struct rz_mtu3_channel *ch) { struct rz_mtu3 *mtu = dev_get_drvdata(ch->dev->parent); - unsigned long flags, value; + struct rz_mtu3_priv *priv = mtu->priv_data; + unsigned long flags, tstr; bool ret = false; - u8 offs; + u16 offset; + u8 bitpos; /* start stop register shared by multiple timer channels */ - raw_spin_lock_irqsave(&mtu->lock, flags); - - if (ch->index == RZ_MTU6 || ch->index == RZ_MTU7) { - value = rz_mtu3_shared_reg_read(ch, RZ_MTU3_TSTRB); - ret = value & (1 << ch->index); - } else if (ch->index != RZ_MTU5) { - value = rz_mtu3_shared_reg_read(ch, RZ_MTU3_TSTRA); - if (ch->index == RZ_MTU8) - offs = 0x08; - else if (ch->index < RZ_MTU3) - offs = 1 << ch->index; - else - offs = 1 << (ch->index + 3); - - ret = value & offs; - } + raw_spin_lock_irqsave(&priv->lock, flags); - raw_spin_unlock_irqrestore(&mtu->lock, flags); + offset = rz_mtu3_get_tstr_offset(ch); + bitpos = rz_mtu3_get_tstr_bit_pos(ch); + tstr = rz_mtu3_shared_reg_read(ch, offset); + ret = tstr & BIT(bitpos); + + raw_spin_unlock_irqrestore(&priv->lock, flags); return ret; } @@ -371,16 +301,13 @@ void rz_mtu3_disable(struct rz_mtu3_channel *ch) } EXPORT_SYMBOL_GPL(rz_mtu3_disable); -static const unsigned int ch_reg_offsets[] = { - 0x100, 0x180, 0x200, 0x000, 0x001, 0xa80, 0x800, 0x801, 0x400 -}; - 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(mtu->rstc); + reset_control_assert(priv->rstc); } static const struct mfd_cell rz_mtu3_devs[] = { @@ -394,6 +321,7 @@ static const struct mfd_cell rz_mtu3_devs[] = { static int rz_mtu3_probe(struct platform_device *pdev) { + struct rz_mtu3_priv *priv; struct rz_mtu3 *ddata; unsigned int i; int ret; @@ -402,26 +330,31 @@ static int rz_mtu3_probe(struct platform_device *pdev) if (!ddata) return -ENOMEM; - ddata->mmio = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(ddata->mmio)) - return PTR_ERR(ddata->mmio); + 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); - ddata->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); - if (IS_ERR(ddata->rstc)) - return PTR_ERR(ddata->rstc); + 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(ddata->rstc); - raw_spin_lock_init(&ddata->lock); + reset_control_deassert(priv->rstc); + raw_spin_lock_init(&priv->lock); platform_set_drvdata(pdev, ddata); for (i = 0; i < RZ_MTU_NUM_CHANNELS; i++) { - ddata->channels[i].index = i; + ddata->channels[i].channel_number = i; ddata->channels[i].is_busy = false; - ddata->channels[i].base = ddata->mmio + ch_reg_offsets[i]; mutex_init(&ddata->channels[i].lock); } @@ -434,7 +367,7 @@ static int rz_mtu3_probe(struct platform_device *pdev) &pdev->dev); err_assert: - reset_control_assert(ddata->rstc); + reset_control_assert(priv->rstc); return ret; } diff --git a/drivers/mfd/rz-mtu3.h b/drivers/mfd/rz-mtu3.h new file mode 100644 index 000000000000..1ff1492c2d16 --- /dev/null +++ b/drivers/mfd/rz-mtu3.h @@ -0,0 +1,147 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * MFD internals for Renesas RZ/G2L MTU3 Core driver + * + * Copyright (C) 2023 Renesas Electronics Corporation + */ + +#ifndef RZ_MTU3_MFD_H +#define RZ_MTU3_MFD_H + +#define MTU_8BIT_CH_0(_tier, _nfcr, _tcr, _tcr2, _tmdr1, _tiorh, _tiorl, _tbtm) \ + { \ + [RZ_MTU3_TIER] = _tier, \ + [RZ_MTU3_NFCR] = _nfcr, \ + [RZ_MTU3_TCR] = _tcr, \ + [RZ_MTU3_TCR2] = _tcr2, \ + [RZ_MTU3_TMDR1] = _tmdr1, \ + [RZ_MTU3_TIORH] = _tiorh, \ + [RZ_MTU3_TIORL] = _tiorl, \ + [RZ_MTU3_TBTM] = _tbtm \ + } + +#define MTU_8BIT_CH_1_2(_tier, _nfcr, _tsr, _tcr, _tcr2, _tmdr1, _tior) \ + { \ + [RZ_MTU3_TIER] = _tier, \ + [RZ_MTU3_NFCR] = _nfcr, \ + [RZ_MTU3_TSR] = _tsr, \ + [RZ_MTU3_TCR] = _tcr, \ + [RZ_MTU3_TCR2] = _tcr2, \ + [RZ_MTU3_TMDR1] = _tmdr1, \ + [RZ_MTU3_TIOR] = _tior \ + } \ + +#define MTU_8BIT_CH_3_4_6_7(_tier, _nfcr, _tsr, _tcr, _tcr2, _tmdr1, _tiorh, _tiorl, _tbtm) \ + { \ + [RZ_MTU3_TIER] = _tier, \ + [RZ_MTU3_NFCR] = _nfcr, \ + [RZ_MTU3_TSR] = _tsr, \ + [RZ_MTU3_TCR] = _tcr, \ + [RZ_MTU3_TCR2] = _tcr2, \ + [RZ_MTU3_TMDR1] = _tmdr1, \ + [RZ_MTU3_TIORH] = _tiorh, \ + [RZ_MTU3_TIORL] = _tiorl, \ + [RZ_MTU3_TBTM] = _tbtm \ + } \ + +#define MTU_8BIT_CH_5(_tier, _nfcr, _tstr, _tcntcmpclr, _tcru, _tcr2u, _tioru, \ + _tcrv, _tcr2v, _tiorv, _tcrw, _tcr2w, _tiorw) \ + { \ + [RZ_MTU3_TIER] = _tier, \ + [RZ_MTU3_NFCR] = _nfcr, \ + [RZ_MTU3_TSTR] = _tstr, \ + [RZ_MTU3_TCNTCMPCLR] = _tcntcmpclr, \ + [RZ_MTU3_TCRU] = _tcru, \ + [RZ_MTU3_TCR2U] = _tcr2u, \ + [RZ_MTU3_TIORU] = _tioru, \ + [RZ_MTU3_TCRV] = _tcrv, \ + [RZ_MTU3_TCR2V] = _tcr2v, \ + [RZ_MTU3_TIORV] = _tiorv, \ + [RZ_MTU3_TCRW] = _tcrw, \ + [RZ_MTU3_TCR2W] = _tcr2w, \ + [RZ_MTU3_TIORW] = _tiorw \ + } \ + +#define MTU_8BIT_CH_8(_tier, _nfcr, _tcr, _tcr2, _tmdr1, _tiorh, _tiorl) \ + { \ + [RZ_MTU3_TIER] = _tier, \ + [RZ_MTU3_NFCR] = _nfcr, \ + [RZ_MTU3_TCR] = _tcr, \ + [RZ_MTU3_TCR2] = _tcr2, \ + [RZ_MTU3_TMDR1] = _tmdr1, \ + [RZ_MTU3_TIORH] = _tiorh, \ + [RZ_MTU3_TIORL] = _tiorl \ + } \ + +#define MTU_16BIT_CH_0(_tcnt, _tgra, _tgrb, _tgrc, _tgrd, _tgre, _tgrf) \ + { \ + [RZ_MTU3_TCNT] = _tcnt, \ + [RZ_MTU3_TGRA] = _tgra, \ + [RZ_MTU3_TGRB] = _tgrb, \ + [RZ_MTU3_TGRC] = _tgrc, \ + [RZ_MTU3_TGRD] = _tgrd, \ + [RZ_MTU3_TGRE] = _tgre, \ + [RZ_MTU3_TGRF] = _tgrf \ + } + +#define MTU_16BIT_CH_1_2(_tcnt, _tgra, _tgrb) \ + { \ + [RZ_MTU3_TCNT] = _tcnt, \ + [RZ_MTU3_TGRA] = _tgra, \ + [RZ_MTU3_TGRB] = _tgrb \ + } + +#define MTU_16BIT_CH_3_6(_tcnt, _tgra, _tgrb, _tgrc, _tgrd, _tgre) \ + { \ + [RZ_MTU3_TCNT] = _tcnt, \ + [RZ_MTU3_TGRA] = _tgra, \ + [RZ_MTU3_TGRB] = _tgrb, \ + [RZ_MTU3_TGRC] = _tgrc, \ + [RZ_MTU3_TGRD] = _tgrd, \ + [RZ_MTU3_TGRE] = _tgre \ + } + +#define MTU_16BIT_CH_4_7(_tcnt, _tgra, _tgrb, _tgrc, _tgrd, _tgre, _tgrf, \ + _tadcr, _tadcora, _tadcorb, _tadcobra, _tadcobrb) \ + { \ + [RZ_MTU3_TCNT] = _tcnt, \ + [RZ_MTU3_TGRA] = _tgra, \ + [RZ_MTU3_TGRB] = _tgrb, \ + [RZ_MTU3_TGRC] = _tgrc, \ + [RZ_MTU3_TGRD] = _tgrd, \ + [RZ_MTU3_TGRE] = _tgre, \ + [RZ_MTU3_TGRF] = _tgrf, \ + [RZ_MTU3_TADCR] = _tadcr, \ + [RZ_MTU3_TADCORA] = _tadcora, \ + [RZ_MTU3_TADCORB] = _tadcorb, \ + [RZ_MTU3_TADCOBRA] = _tadcobra, \ + [RZ_MTU3_TADCOBRB] = _tadcobrb \ + } + +#define MTU_16BIT_CH_5(_tcntu, _tgru, _tcntv, _tgrv, _tcntw, _tgrw) \ + { \ + [RZ_MTU3_TCNTU] = _tcntu, \ + [RZ_MTU3_TGRU] = _tgru, \ + [RZ_MTU3_TCNTV] = _tcntv, \ + [RZ_MTU3_TGRV] = _tgrv, \ + [RZ_MTU3_TCNTW] = _tcntw, \ + [RZ_MTU3_TGRW] = _tgrw \ + } + +#define MTU_32BIT_CH_1(_tcntlw, _tgralw, _tgrblw) \ + { \ + [RZ_MTU3_TCNTLW] = _tcntlw, \ + [RZ_MTU3_TGRALW] = _tgralw, \ + [RZ_MTU3_TGRBLW] = _tgrblw \ + } + +#define MTU_32BIT_CH_8(_tcnt, _tgra, _tgrb, _tgrc, _tgrd) \ + { \ + [RZ_MTU3_TCNT] = _tcnt, \ + [RZ_MTU3_TGRA] = _tgra, \ + [RZ_MTU3_TGRB] = _tgrb, \ + [RZ_MTU3_TGRC] = _tgrc, \ + [RZ_MTU3_TGRD] = _tgrd \ + } + +#endif diff --git a/include/linux/mfd/rz-mtu3.h b/include/linux/mfd/rz-mtu3.h index 42e561a9603c..c9380488285d 100644 --- a/include/linux/mfd/rz-mtu3.h +++ b/include/linux/mfd/rz-mtu3.h @@ -2,10 +2,8 @@ /* * Copyright (C) 2022 Renesas Electronics Corporation */ -#ifndef __LINUX_RZ_MTU3_H__ -#define __LINUX_RZ_MTU3_H__ - -#include <linux/clk.h> +#ifndef __MFD_RZ_MTU3_H__ +#define __MFD_RZ_MTU3_H__ /* 8-bit shared register offsets macros */ #define RZ_MTU3_TSTRA 0x080 /* Timer start register A */ @@ -92,15 +90,15 @@ #define RZ_MTU3_TCR_CCLR_TGRA BIT(5) enum rz_mtu3_channels { - RZ_MTU0, - RZ_MTU1, - RZ_MTU2, - RZ_MTU3, - RZ_MTU4, - RZ_MTU5, - RZ_MTU6, - RZ_MTU7, - RZ_MTU8, + RZ_MTU3_CHAN_0, + RZ_MTU3_CHAN_1, + RZ_MTU3_CHAN_2, + RZ_MTU3_CHAN_3, + RZ_MTU3_CHAN_4, + RZ_MTU3_CHAN_5, + RZ_MTU3_CHAN_6, + RZ_MTU3_CHAN_7, + RZ_MTU3_CHAN_8, RZ_MTU_NUM_CHANNELS }; @@ -108,16 +106,14 @@ enum rz_mtu3_channels { * struct rz_mtu3_channel - MTU3 channel private data * * @dev: device handle - * @index: channel index - * @base: channel base address + * @channel_number: channel number * @lock: Lock to protect channel state * @is_busy: channel state */ struct rz_mtu3_channel { struct device *dev; - unsigned int index; - void __iomem *base; - struct mutex lock; /* Protect channel state */ + unsigned int channel_number; + struct mutex lock; bool is_busy; }; @@ -125,17 +121,14 @@ struct rz_mtu3_channel { * struct rz_mtu3 - MTU3 core private data * * @clk: MTU3 module clock - * @mmio: MTU3 module clock - * @lock: Lock to protect shared register access * @rz_mtu3_channel: HW channels + * @priv_data: MTU3 core driver private data */ struct rz_mtu3 { - void *priv_rz_mtu3; - void __iomem *mmio; struct clk *clk; - struct reset_control *rstc; - raw_spinlock_t lock; /* Protect the shared registers */ struct rz_mtu3_channel channels[RZ_MTU_NUM_CHANNELS]; + + void *priv_data; }; #if IS_ENABLED(CONFIG_RZ_MTU3) @@ -240,4 +233,4 @@ static inline void rz_mtu3_shared_reg_update_bit(struct rz_mtu3_channel *ch, } #endif -#endif /* __LINUX_RZ_MTU3_H__ */ +#endif /* __MFD_RZ_MTU3_H__ */ Cheers, Biju