Message ID | 20210505163905.1199923-2-git@xen0n.name (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | rtc: ls2x: Add support for the Loongson-2K/LS7A RTC | expand |
Hi WANG, Thank you for the patch! Perhaps something to improve: [auto build test WARNING on linus/master] [also build test WARNING on next-20210505] [cannot apply to abelloni/rtc-next robh/for-next v5.12] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch] url: https://github.com/0day-ci/linux/commits/WANG-Xuerui/rtc-ls2x-Add-support-for-the-Loongson-2K-LS7A-RTC/20210506-013703 base: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git d665ea6ea86c785760ee4bad4543dab3267ad074 config: parisc-allyesconfig (attached as .config) compiler: hppa-linux-gcc (GCC) 9.3.0 reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # https://github.com/0day-ci/linux/commit/a4a12242f17c2ad92025b724458a31eb088e1893 git remote add linux-review https://github.com/0day-ci/linux git fetch --no-tags linux-review WANG-Xuerui/rtc-ls2x-Add-support-for-the-Loongson-2K-LS7A-RTC/20210506-013703 git checkout a4a12242f17c2ad92025b724458a31eb088e1893 # save the attached .config to linux build tree COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross W=1 ARCH=parisc If you fix the issue, kindly add following tag as appropriate Reported-by: kernel test robot <lkp@intel.com> All warnings (new ones prefixed by >>): drivers/rtc/rtc-ls2x.c: In function 'ls2x_rtc_probe': drivers/rtc/rtc-ls2x.c:202:9: error: implicit declaration of function 'rtc_register_device'; did you mean 'devm_rtc_register_device'? [-Werror=implicit-function-declaration] 202 | return rtc_register_device(rtc); | ^~~~~~~~~~~~~~~~~~~ | devm_rtc_register_device drivers/rtc/rtc-ls2x.c: At top level: drivers/rtc/rtc-ls2x.c:215:21: error: implicit declaration of function 'of_match_ptr' [-Werror=implicit-function-declaration] 215 | .of_match_table = of_match_ptr(ls2x_rtc_of_match), | ^~~~~~~~~~~~ >> drivers/rtc/rtc-ls2x.c:215:21: warning: initialization of 'const struct of_device_id *' from 'int' makes pointer from integer without a cast [-Wint-conversion] drivers/rtc/rtc-ls2x.c:215:21: note: (near initialization for 'ls2x_rtc_driver.driver.of_match_table') drivers/rtc/rtc-ls2x.c:215:21: error: initializer element is not constant drivers/rtc/rtc-ls2x.c:215:21: note: (near initialization for 'ls2x_rtc_driver.driver.of_match_table') cc1: some warnings being treated as errors vim +215 drivers/rtc/rtc-ls2x.c 210 211 static struct platform_driver ls2x_rtc_driver = { 212 .probe = ls2x_rtc_probe, 213 .driver = { 214 .name = "ls2x-rtc", > 215 .of_match_table = of_match_ptr(ls2x_rtc_of_match), 216 }, 217 }; 218 --- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
Hi WANG, Thank you for the patch! Yet something to improve: [auto build test ERROR on linus/master] [also build test ERROR on next-20210505] [cannot apply to abelloni/rtc-next robh/for-next v5.12] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch] url: https://github.com/0day-ci/linux/commits/WANG-Xuerui/rtc-ls2x-Add-support-for-the-Loongson-2K-LS7A-RTC/20210506-013703 base: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git d665ea6ea86c785760ee4bad4543dab3267ad074 config: arc-allyesconfig (attached as .config) compiler: arceb-elf-gcc (GCC) 9.3.0 reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # https://github.com/0day-ci/linux/commit/a4a12242f17c2ad92025b724458a31eb088e1893 git remote add linux-review https://github.com/0day-ci/linux git fetch --no-tags linux-review WANG-Xuerui/rtc-ls2x-Add-support-for-the-Loongson-2K-LS7A-RTC/20210506-013703 git checkout a4a12242f17c2ad92025b724458a31eb088e1893 # save the attached .config to linux build tree COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross W=1 ARCH=arc If you fix the issue, kindly add following tag as appropriate Reported-by: kernel test robot <lkp@intel.com> All errors (new ones prefixed by >>): drivers/rtc/rtc-ls2x.c: In function 'ls2x_rtc_probe': >> drivers/rtc/rtc-ls2x.c:202:9: error: implicit declaration of function 'rtc_register_device'; did you mean 'devm_rtc_register_device'? [-Werror=implicit-function-declaration] 202 | return rtc_register_device(rtc); | ^~~~~~~~~~~~~~~~~~~ | devm_rtc_register_device drivers/rtc/rtc-ls2x.c: At top level: >> drivers/rtc/rtc-ls2x.c:205:34: error: array type has incomplete element type 'struct of_device_id' 205 | static const struct of_device_id ls2x_rtc_of_match[] = { | ^~~~~~~~~~~~~~~~~ >> drivers/rtc/rtc-ls2x.c:206:4: error: field name not in record or union initializer 206 | { .compatible = "loongson,ls2x-rtc" }, | ^ drivers/rtc/rtc-ls2x.c:206:4: note: (near initialization for 'ls2x_rtc_of_match') >> drivers/rtc/rtc-ls2x.c:215:21: error: implicit declaration of function 'of_match_ptr' [-Werror=implicit-function-declaration] 215 | .of_match_table = of_match_ptr(ls2x_rtc_of_match), | ^~~~~~~~~~~~ drivers/rtc/rtc-ls2x.c:205:34: warning: 'ls2x_rtc_of_match' defined but not used [-Wunused-variable] 205 | static const struct of_device_id ls2x_rtc_of_match[] = { | ^~~~~~~~~~~~~~~~~ cc1: some warnings being treated as errors vim +202 drivers/rtc/rtc-ls2x.c 152 153 static int ls2x_rtc_probe(struct platform_device *pdev) 154 { 155 struct device *dev = &pdev->dev; 156 struct rtc_device *rtc; 157 struct ls2x_rtc_priv *priv; 158 void __iomem *regs; 159 int ret; 160 161 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 162 if (unlikely(!priv)) 163 return -ENOMEM; 164 165 spin_lock_init(&priv->lock); 166 platform_set_drvdata(pdev, priv); 167 168 regs = devm_platform_ioremap_resource(pdev, 0); 169 if (IS_ERR(regs)) { 170 ret = PTR_ERR(regs); 171 dev_err(dev, "Failed to map rtc registers: %d\n", ret); 172 return ret; 173 } 174 175 priv->regmap = devm_regmap_init_mmio(dev, regs, 176 &ls2x_rtc_regmap_config); 177 if (IS_ERR(priv->regmap)) { 178 ret = PTR_ERR(priv->regmap); 179 dev_err(dev, "Failed to init regmap: %d\n", ret); 180 return ret; 181 } 182 183 rtc = devm_rtc_allocate_device(dev); 184 if (IS_ERR(rtc)) { 185 ret = PTR_ERR(rtc); 186 dev_err(dev, "Failed to allocate rtc device: %d\n", ret); 187 return ret; 188 } 189 190 rtc->ops = &ls2x_rtc_ops; 191 192 /* Due to hardware erratum, all years multiple of 4 are considered 193 * leap year, so only years 2000 through 2099 are usable. 194 * 195 * Previous out-of-tree versions of this driver wrote tm_year directly 196 * into the year register, so epoch 2000 must be used to preserve 197 * semantics on shipped systems. 198 */ 199 rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; 200 rtc->range_max = RTC_TIMESTAMP_END_2099; 201 > 202 return rtc_register_device(rtc); 203 } 204 > 205 static const struct of_device_id ls2x_rtc_of_match[] = { > 206 { .compatible = "loongson,ls2x-rtc" }, 207 { /* sentinel */ }, 208 }; 209 MODULE_DEVICE_TABLE(of, ls2x_rtc_of_match); 210 211 static struct platform_driver ls2x_rtc_driver = { 212 .probe = ls2x_rtc_probe, 213 .driver = { 214 .name = "ls2x-rtc", > 215 .of_match_table = of_match_ptr(ls2x_rtc_of_match), 216 }, 217 }; 218 --- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
Sorry for the noise, there's one compile error not caught after rebase. It was late in the night and I didn't watch the test compile finish before sending the series. Will resend very soon. (/me yawns) On 5/6/21 00:39, WANG Xuerui wrote: > This RTC module is integrated into the Loongson-2K SoC and the LS7A > bridge chip. This version is almost entirely rewritten to make use of > current kernel API. > > Signed-off-by: Huacai Chen <chenhuacai@kernel.org> > Signed-off-by: WANG Xuerui <git@xen0n.name> > Tested-by: Tiezhu Yang <yangtiezhu@loongson.cn> > --- > drivers/rtc/Kconfig | 11 ++ > drivers/rtc/Makefile | 1 + > drivers/rtc/rtc-ls2x.c | 225 +++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 237 insertions(+) > create mode 100644 drivers/rtc/rtc-ls2x.c > > diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig > index d8c13fded164..55beede68829 100644 > --- a/drivers/rtc/Kconfig > +++ b/drivers/rtc/Kconfig > @@ -1304,6 +1304,17 @@ config RTC_DRV_NTXEC > embedded controller found in certain e-book readers designed by the > original design manufacturer Netronix. > > +config RTC_DRV_LS2X > + tristate "Loongson LS2X RTC" > + depends on MACH_LOONGSON64 || COMPILE_TEST > + select REGMAP_MMIO > + help > + If you say yes here you get support for the RTC on the Loongson-2K > + SoC and LS7A bridge, which first appeared on the Loongson-2H. > + > + This driver can also be built as a module. If so, the module > + will be called rtc-ls2x. > + > comment "on-CPU RTC drivers" > > config RTC_DRV_ASM9260 > diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile > index 2dd0dd956b0e..6042ff1e73b7 100644 > --- a/drivers/rtc/Makefile > +++ b/drivers/rtc/Makefile > @@ -81,6 +81,7 @@ obj-$(CONFIG_RTC_DRV_LOONGSON1) += rtc-ls1x.o > obj-$(CONFIG_RTC_DRV_LP8788) += rtc-lp8788.o > obj-$(CONFIG_RTC_DRV_LPC24XX) += rtc-lpc24xx.o > obj-$(CONFIG_RTC_DRV_LPC32XX) += rtc-lpc32xx.o > +obj-$(CONFIG_RTC_DRV_LS2X) += rtc-ls2x.o > obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o > obj-$(CONFIG_RTC_DRV_M41T93) += rtc-m41t93.o > obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o > diff --git a/drivers/rtc/rtc-ls2x.c b/drivers/rtc/rtc-ls2x.c > new file mode 100644 > index 000000000000..8eddb0316b3b > --- /dev/null > +++ b/drivers/rtc/rtc-ls2x.c > @@ -0,0 +1,225 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Loongson-2K/7A RTC driver > + * > + * Based on the out-of-tree Loongson-2H RTC driver for Linux 2.6.32, by > + * Shaozong Liu <liushaozong@loongson.cn>. > + * > + * Maintained out-of-tree by Huacai Chen <chenhuacai@kernel.org>. > + * > + * Rewritten for mainline by WANG Xuerui <git@xen0n.name>. > + */ > + > +#include <linux/module.h> > +#include <linux/platform_device.h> > +#include <linux/regmap.h> > +#include <linux/rtc.h> > +#include <linux/spinlock.h> > + > +#define TOY_TRIM_REG 0x20 > +#define TOY_WRITE0_REG 0x24 > +#define TOY_WRITE1_REG 0x28 > +#define TOY_READ0_REG 0x2c > +#define TOY_READ1_REG 0x30 > +#define TOY_MATCH0_REG 0x34 > +#define TOY_MATCH1_REG 0x38 > +#define TOY_MATCH2_REG 0x3c > +#define RTC_CTRL_REG 0x40 > +#define RTC_TRIM_REG 0x60 > +#define RTC_WRITE0_REG 0x64 > +#define RTC_READ0_REG 0x68 > +#define RTC_MATCH0_REG 0x6c > +#define RTC_MATCH1_REG 0x70 > +#define RTC_MATCH2_REG 0x74 > + > +#define TOY_MON GENMASK(31, 26) > +#define TOY_MON_SHIFT 26 > +#define TOY_DAY GENMASK(25, 21) > +#define TOY_DAY_SHIFT 21 > +#define TOY_HOUR GENMASK(20, 16) > +#define TOY_HOUR_SHIFT 16 > +#define TOY_MIN GENMASK(15, 10) > +#define TOY_MIN_SHIFT 10 > +#define TOY_SEC GENMASK(9, 4) > +#define TOY_SEC_SHIFT 4 > +#define TOY_MSEC GENMASK(3, 0) > +#define TOY_MSEC_SHIFT 0 > + > +struct ls2x_rtc_priv { > + struct regmap *regmap; > + spinlock_t lock; > +}; > + > +static const struct regmap_config ls2x_rtc_regmap_config = { > + .reg_bits = 32, > + .val_bits = 32, > + .reg_stride = 4, > +}; > + > +struct ls2x_rtc_regs { > + u32 reg0; > + u32 reg1; > +}; > + > +static inline void ls2x_rtc_regs_to_time(struct ls2x_rtc_regs *regs, > + struct rtc_time *tm) > +{ > + tm->tm_year = regs->reg1; > + tm->tm_sec = (regs->reg0 & TOY_SEC) >> TOY_SEC_SHIFT; > + tm->tm_min = (regs->reg0 & TOY_MIN) >> TOY_MIN_SHIFT; > + tm->tm_hour = (regs->reg0 & TOY_HOUR) >> TOY_HOUR_SHIFT; > + tm->tm_mday = (regs->reg0 & TOY_DAY) >> TOY_DAY_SHIFT; > + tm->tm_mon = ((regs->reg0 & TOY_MON) >> TOY_MON_SHIFT) - 1; > +} > + > +static inline void ls2x_rtc_time_to_regs(struct rtc_time *tm, > + struct ls2x_rtc_regs *regs) > +{ > + regs->reg0 = (tm->tm_sec << TOY_SEC_SHIFT) & TOY_SEC; > + regs->reg0 |= (tm->tm_min << TOY_MIN_SHIFT) & TOY_MIN; > + regs->reg0 |= (tm->tm_hour << TOY_HOUR_SHIFT) & TOY_HOUR; > + regs->reg0 |= (tm->tm_mday << TOY_DAY_SHIFT) & TOY_DAY; > + regs->reg0 |= ((tm->tm_mon + 1) << TOY_MON_SHIFT) & TOY_MON; > + regs->reg1 = tm->tm_year; > +} > + > +static int ls2x_rtc_read_time(struct device *dev, struct rtc_time *tm) > +{ > + struct ls2x_rtc_priv *priv = dev_get_drvdata(dev); > + struct ls2x_rtc_regs regs; > + int ret; > + > + spin_lock_irq(&priv->lock); > + > + ret = regmap_read(priv->regmap, TOY_READ1_REG, ®s.reg1); > + if (unlikely(ret)) { > + dev_err(dev, "Failed to read time: %d\n", ret); > + goto fail; > + } > + > + ret = regmap_read(priv->regmap, TOY_READ0_REG, ®s.reg0); > + if (unlikely(ret)) { > + dev_err(dev, "Failed to read time: %d\n", ret); > + goto fail; > + } > + > + spin_unlock_irq(&priv->lock); > + > + ls2x_rtc_regs_to_time(®s, tm); > + > + return 0; > + > +fail: > + spin_unlock_irq(&priv->lock); > + return ret; > +} > + > +static int ls2x_rtc_set_time(struct device *dev, struct rtc_time *tm) > +{ > + struct ls2x_rtc_priv *priv = dev_get_drvdata(dev); > + struct ls2x_rtc_regs regs; > + int ret; > + > + ls2x_rtc_time_to_regs(tm, ®s); > + > + spin_lock_irq(&priv->lock); > + > + ret = regmap_write(priv->regmap, TOY_WRITE0_REG, regs.reg0); > + if (unlikely(ret)) { > + dev_err(dev, "Failed to set time: %d\n", ret); > + goto fail; > + } > + > + ret = regmap_write(priv->regmap, TOY_WRITE1_REG, regs.reg1); > + if (unlikely(ret)) { > + dev_err(dev, "Failed to set time: %d\n", ret); > + goto fail; > + } > + > + spin_unlock_irq(&priv->lock); > + > + return 0; > + > +fail: > + spin_unlock_irq(&priv->lock); > + return ret; > +} > + > +static struct rtc_class_ops ls2x_rtc_ops = { > + .read_time = ls2x_rtc_read_time, > + .set_time = ls2x_rtc_set_time, > +}; > + > +static int ls2x_rtc_probe(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct rtc_device *rtc; > + struct ls2x_rtc_priv *priv; > + void __iomem *regs; > + int ret; > + > + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); > + if (unlikely(!priv)) > + return -ENOMEM; > + > + spin_lock_init(&priv->lock); > + platform_set_drvdata(pdev, priv); > + > + regs = devm_platform_ioremap_resource(pdev, 0); > + if (IS_ERR(regs)) { > + ret = PTR_ERR(regs); > + dev_err(dev, "Failed to map rtc registers: %d\n", ret); > + return ret; > + } > + > + priv->regmap = devm_regmap_init_mmio(dev, regs, > + &ls2x_rtc_regmap_config); > + if (IS_ERR(priv->regmap)) { > + ret = PTR_ERR(priv->regmap); > + dev_err(dev, "Failed to init regmap: %d\n", ret); > + return ret; > + } > + > + rtc = devm_rtc_allocate_device(dev); > + if (IS_ERR(rtc)) { > + ret = PTR_ERR(rtc); > + dev_err(dev, "Failed to allocate rtc device: %d\n", ret); > + return ret; > + } > + > + rtc->ops = &ls2x_rtc_ops; > + > + /* Due to hardware erratum, all years multiple of 4 are considered > + * leap year, so only years 2000 through 2099 are usable. > + * > + * Previous out-of-tree versions of this driver wrote tm_year directly > + * into the year register, so epoch 2000 must be used to preserve > + * semantics on shipped systems. > + */ > + rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; > + rtc->range_max = RTC_TIMESTAMP_END_2099; > + > + return rtc_register_device(rtc); > +} > + > +static const struct of_device_id ls2x_rtc_of_match[] = { > + { .compatible = "loongson,ls2x-rtc" }, > + { /* sentinel */ }, > +}; > +MODULE_DEVICE_TABLE(of, ls2x_rtc_of_match); > + > +static struct platform_driver ls2x_rtc_driver = { > + .probe = ls2x_rtc_probe, > + .driver = { > + .name = "ls2x-rtc", > + .of_match_table = of_match_ptr(ls2x_rtc_of_match), > + }, > +}; > + > +module_platform_driver(ls2x_rtc_driver); > + > +MODULE_DESCRIPTION("LS2X RTC driver"); > +MODULE_AUTHOR("WANG Xuerui"); > +MODULE_AUTHOR("Huacai Chen"); > +MODULE_LICENSE("GPL"); > +MODULE_ALIAS("platform:ls2x-rtc");
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index d8c13fded164..55beede68829 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1304,6 +1304,17 @@ config RTC_DRV_NTXEC embedded controller found in certain e-book readers designed by the original design manufacturer Netronix. +config RTC_DRV_LS2X + tristate "Loongson LS2X RTC" + depends on MACH_LOONGSON64 || COMPILE_TEST + select REGMAP_MMIO + help + If you say yes here you get support for the RTC on the Loongson-2K + SoC and LS7A bridge, which first appeared on the Loongson-2H. + + This driver can also be built as a module. If so, the module + will be called rtc-ls2x. + comment "on-CPU RTC drivers" config RTC_DRV_ASM9260 diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 2dd0dd956b0e..6042ff1e73b7 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -81,6 +81,7 @@ obj-$(CONFIG_RTC_DRV_LOONGSON1) += rtc-ls1x.o obj-$(CONFIG_RTC_DRV_LP8788) += rtc-lp8788.o obj-$(CONFIG_RTC_DRV_LPC24XX) += rtc-lpc24xx.o obj-$(CONFIG_RTC_DRV_LPC32XX) += rtc-lpc32xx.o +obj-$(CONFIG_RTC_DRV_LS2X) += rtc-ls2x.o obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o obj-$(CONFIG_RTC_DRV_M41T93) += rtc-m41t93.o obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o diff --git a/drivers/rtc/rtc-ls2x.c b/drivers/rtc/rtc-ls2x.c new file mode 100644 index 000000000000..8eddb0316b3b --- /dev/null +++ b/drivers/rtc/rtc-ls2x.c @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Loongson-2K/7A RTC driver + * + * Based on the out-of-tree Loongson-2H RTC driver for Linux 2.6.32, by + * Shaozong Liu <liushaozong@loongson.cn>. + * + * Maintained out-of-tree by Huacai Chen <chenhuacai@kernel.org>. + * + * Rewritten for mainline by WANG Xuerui <git@xen0n.name>. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/rtc.h> +#include <linux/spinlock.h> + +#define TOY_TRIM_REG 0x20 +#define TOY_WRITE0_REG 0x24 +#define TOY_WRITE1_REG 0x28 +#define TOY_READ0_REG 0x2c +#define TOY_READ1_REG 0x30 +#define TOY_MATCH0_REG 0x34 +#define TOY_MATCH1_REG 0x38 +#define TOY_MATCH2_REG 0x3c +#define RTC_CTRL_REG 0x40 +#define RTC_TRIM_REG 0x60 +#define RTC_WRITE0_REG 0x64 +#define RTC_READ0_REG 0x68 +#define RTC_MATCH0_REG 0x6c +#define RTC_MATCH1_REG 0x70 +#define RTC_MATCH2_REG 0x74 + +#define TOY_MON GENMASK(31, 26) +#define TOY_MON_SHIFT 26 +#define TOY_DAY GENMASK(25, 21) +#define TOY_DAY_SHIFT 21 +#define TOY_HOUR GENMASK(20, 16) +#define TOY_HOUR_SHIFT 16 +#define TOY_MIN GENMASK(15, 10) +#define TOY_MIN_SHIFT 10 +#define TOY_SEC GENMASK(9, 4) +#define TOY_SEC_SHIFT 4 +#define TOY_MSEC GENMASK(3, 0) +#define TOY_MSEC_SHIFT 0 + +struct ls2x_rtc_priv { + struct regmap *regmap; + spinlock_t lock; +}; + +static const struct regmap_config ls2x_rtc_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, +}; + +struct ls2x_rtc_regs { + u32 reg0; + u32 reg1; +}; + +static inline void ls2x_rtc_regs_to_time(struct ls2x_rtc_regs *regs, + struct rtc_time *tm) +{ + tm->tm_year = regs->reg1; + tm->tm_sec = (regs->reg0 & TOY_SEC) >> TOY_SEC_SHIFT; + tm->tm_min = (regs->reg0 & TOY_MIN) >> TOY_MIN_SHIFT; + tm->tm_hour = (regs->reg0 & TOY_HOUR) >> TOY_HOUR_SHIFT; + tm->tm_mday = (regs->reg0 & TOY_DAY) >> TOY_DAY_SHIFT; + tm->tm_mon = ((regs->reg0 & TOY_MON) >> TOY_MON_SHIFT) - 1; +} + +static inline void ls2x_rtc_time_to_regs(struct rtc_time *tm, + struct ls2x_rtc_regs *regs) +{ + regs->reg0 = (tm->tm_sec << TOY_SEC_SHIFT) & TOY_SEC; + regs->reg0 |= (tm->tm_min << TOY_MIN_SHIFT) & TOY_MIN; + regs->reg0 |= (tm->tm_hour << TOY_HOUR_SHIFT) & TOY_HOUR; + regs->reg0 |= (tm->tm_mday << TOY_DAY_SHIFT) & TOY_DAY; + regs->reg0 |= ((tm->tm_mon + 1) << TOY_MON_SHIFT) & TOY_MON; + regs->reg1 = tm->tm_year; +} + +static int ls2x_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct ls2x_rtc_priv *priv = dev_get_drvdata(dev); + struct ls2x_rtc_regs regs; + int ret; + + spin_lock_irq(&priv->lock); + + ret = regmap_read(priv->regmap, TOY_READ1_REG, ®s.reg1); + if (unlikely(ret)) { + dev_err(dev, "Failed to read time: %d\n", ret); + goto fail; + } + + ret = regmap_read(priv->regmap, TOY_READ0_REG, ®s.reg0); + if (unlikely(ret)) { + dev_err(dev, "Failed to read time: %d\n", ret); + goto fail; + } + + spin_unlock_irq(&priv->lock); + + ls2x_rtc_regs_to_time(®s, tm); + + return 0; + +fail: + spin_unlock_irq(&priv->lock); + return ret; +} + +static int ls2x_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct ls2x_rtc_priv *priv = dev_get_drvdata(dev); + struct ls2x_rtc_regs regs; + int ret; + + ls2x_rtc_time_to_regs(tm, ®s); + + spin_lock_irq(&priv->lock); + + ret = regmap_write(priv->regmap, TOY_WRITE0_REG, regs.reg0); + if (unlikely(ret)) { + dev_err(dev, "Failed to set time: %d\n", ret); + goto fail; + } + + ret = regmap_write(priv->regmap, TOY_WRITE1_REG, regs.reg1); + if (unlikely(ret)) { + dev_err(dev, "Failed to set time: %d\n", ret); + goto fail; + } + + spin_unlock_irq(&priv->lock); + + return 0; + +fail: + spin_unlock_irq(&priv->lock); + return ret; +} + +static struct rtc_class_ops ls2x_rtc_ops = { + .read_time = ls2x_rtc_read_time, + .set_time = ls2x_rtc_set_time, +}; + +static int ls2x_rtc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rtc_device *rtc; + struct ls2x_rtc_priv *priv; + void __iomem *regs; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (unlikely(!priv)) + return -ENOMEM; + + spin_lock_init(&priv->lock); + platform_set_drvdata(pdev, priv); + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) { + ret = PTR_ERR(regs); + dev_err(dev, "Failed to map rtc registers: %d\n", ret); + return ret; + } + + priv->regmap = devm_regmap_init_mmio(dev, regs, + &ls2x_rtc_regmap_config); + if (IS_ERR(priv->regmap)) { + ret = PTR_ERR(priv->regmap); + dev_err(dev, "Failed to init regmap: %d\n", ret); + return ret; + } + + rtc = devm_rtc_allocate_device(dev); + if (IS_ERR(rtc)) { + ret = PTR_ERR(rtc); + dev_err(dev, "Failed to allocate rtc device: %d\n", ret); + return ret; + } + + rtc->ops = &ls2x_rtc_ops; + + /* Due to hardware erratum, all years multiple of 4 are considered + * leap year, so only years 2000 through 2099 are usable. + * + * Previous out-of-tree versions of this driver wrote tm_year directly + * into the year register, so epoch 2000 must be used to preserve + * semantics on shipped systems. + */ + rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + rtc->range_max = RTC_TIMESTAMP_END_2099; + + return rtc_register_device(rtc); +} + +static const struct of_device_id ls2x_rtc_of_match[] = { + { .compatible = "loongson,ls2x-rtc" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, ls2x_rtc_of_match); + +static struct platform_driver ls2x_rtc_driver = { + .probe = ls2x_rtc_probe, + .driver = { + .name = "ls2x-rtc", + .of_match_table = of_match_ptr(ls2x_rtc_of_match), + }, +}; + +module_platform_driver(ls2x_rtc_driver); + +MODULE_DESCRIPTION("LS2X RTC driver"); +MODULE_AUTHOR("WANG Xuerui"); +MODULE_AUTHOR("Huacai Chen"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ls2x-rtc");