// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2019 MediaTek Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define RTC_BBPU 0x0000 #define RTC_BBPU_CBUSY BIT(6) #define RTC_WRTGR 0x003c #define RTC_AL_HOU 0x001c #define RTC_PDN1 0x002c #define RTC_PDN2 0x002e #define RTC_SPAR0 0x0030 #define RTC_CON 0x003e #define RTC_BBPU_KEY (0x43 << 8) #define RTC_BBPU_AUTO BIT(3) #define RTC_BBPU_PWREN BIT(0) #define RTC_CON_F32KOB BIT(5) #define RTC_GPIO_USER_MASK 0x1f00 static const u16 rtc_spare_reg[][3] = { {RTC_AL_HOU, 0x7f, 8}, {RTC_PDN1, 0xf, 0}, {RTC_PDN1, 0x3, 4}, {RTC_PDN1, 0x1, 6}, {RTC_PDN1, 0x1, 7}, {RTC_PDN1, 0x1, 13}, {RTC_PDN1, 0x1, 14}, {RTC_PDN1, 0x1, 15}, {RTC_PDN2, 0x1, 4}, {RTC_PDN2, 0x3, 5}, {RTC_PDN2, 0x1, 7}, {RTC_PDN2, 0x1, 15}, {RTC_SPAR0, 0x1, 6}, {RTC_SPAR0, 0x1, 7} }; enum rtc_spare_enum { RTC_FGSOC = 0, RTC_ANDROID, RTC_FAC_RESET, RTC_BYPASS_PWR, RTC_PWRON_TIME, RTC_FAST_BOOT, RTC_KPOC, RTC_DEBUG, RTC_PWRON_AL, RTC_UART, RTC_AUTOBOOT, RTC_PWRON_LOGO, RTC_32K_LESS, RTC_LP_DET, RTC_SPAR_NUM }; enum rtc_reg_set { RTC_REG, RTC_MASK, RTC_SHIFT }; struct mt6397_misc { struct device *dev; struct mutex lock; struct regmap *regmap; u32 addr_base; }; static struct mt6397_misc *rtc_misc; static int mtk_rtc_write_trigger(void) { unsigned long timeout = jiffies + HZ; int ret; u32 data; ret = regmap_write(rtc_misc->regmap, rtc_misc->addr_base + RTC_WRTGR, 1); if (ret < 0) return ret; while (1) { ret = regmap_read(rtc_misc->regmap, rtc_misc->addr_base + RTC_BBPU, &data); if (ret < 0) break; if (!(data & RTC_BBPU_CBUSY)) break; if (time_after(jiffies, timeout)) { ret = -ETIMEDOUT; break; } cpu_relax(); } return ret; } static u32 __mtk_misc_get_spare_register(enum rtc_spare_enum cmd) { int ret; u32 data; if (cmd >= 0 && cmd < RTC_SPAR_NUM) { ret = regmap_read(rtc_misc->regmap, rtc_misc->addr_base + rtc_spare_reg[cmd][RTC_REG], &data); data = (data >> rtc_spare_reg[cmd][RTC_SHIFT]) & rtc_spare_reg[cmd][RTC_MASK]; return data; } return -EINVAL; } static void __mtk_misc_set_spare_register(enum rtc_spare_enum cmd, u32 val) { int ret; u32 data, mask; if (cmd >= 0 && cmd < RTC_SPAR_NUM) { data = val << rtc_spare_reg[cmd][RTC_SHIFT]; mask = rtc_spare_reg[cmd][RTC_MASK] << rtc_spare_reg[cmd][RTC_SHIFT]; ret = regmap_update_bits(rtc_misc->regmap, rtc_misc->addr_base + rtc_spare_reg[cmd][RTC_REG], mask, data); if (ret < 0) dev_err(rtc_misc->dev, "regmap write error!!!\n"); mtk_rtc_write_trigger(); } } u32 mtk_misc_get_spare_fg_value(void) { u32 data; mutex_lock(&rtc_misc->lock); data = __mtk_misc_get_spare_register(RTC_FGSOC); mutex_unlock(&rtc_misc->lock); return data; } int mtk_misc_set_spare_fg_value(u32 val) { if (val > 100) return 1; mutex_lock(&rtc_misc->lock); __mtk_misc_set_spare_register(RTC_FGSOC, val); mutex_unlock(&rtc_misc->lock); return 0; } bool mtk_misc_crystal_exist_status(void) { u32 ret; mutex_lock(&rtc_misc->lock); ret = __mtk_misc_get_spare_register(RTC_32K_LESS); mutex_unlock(&rtc_misc->lock); return !!ret; } EXPORT_SYMBOL(mtk_misc_crystal_exist_status); static void mtk_misc_set_gpio_32k_status(enum rtc_gpio_user_t user, bool enable) { u32 pdn1, temp, con; int ret; ret = regmap_read(rtc_misc->regmap, rtc_misc->addr_base + RTC_PDN1, &pdn1); if (ret < 0) goto exit; ret = regmap_read(rtc_misc->regmap, rtc_misc->addr_base + RTC_CON, &con); if (ret < 0) goto exit; if (!enable) { temp = pdn1 & ~(1 << user); ret = regmap_write(rtc_misc->regmap, rtc_misc->addr_base + RTC_PDN1, temp); if (ret < 0) goto exit; mtk_rtc_write_trigger(); if (!(pdn1 & RTC_GPIO_USER_MASK)) con |= RTC_CON_F32KOB; } else { con &= ~RTC_CON_F32KOB; pdn1 |= (1 << user); ret = regmap_write(rtc_misc->regmap, rtc_misc->addr_base + RTC_PDN1, pdn1); if (ret < 0) goto exit; mtk_rtc_write_trigger(); } ret = regmap_write(rtc_misc->regmap, rtc_misc->addr_base + RTC_CON, con); if (ret < 0) goto exit; mtk_rtc_write_trigger(); return; exit: dev_err(rtc_misc->dev, "regmap write/read error!!!\n"); } void rtc_gpio_enable_32k(enum rtc_gpio_user_t user) { if (user < RTC_GPIO_USER_WIFI || user > RTC_GPIO_USER_PMIC) return; dev_info(rtc_misc->dev, "enable 32k clock output!!!\n"); mutex_lock(&rtc_misc->lock); mtk_misc_set_gpio_32k_status(user, true); mutex_unlock(&rtc_misc->lock); } EXPORT_SYMBOL(rtc_gpio_enable_32k); void rtc_gpio_disable_32k(enum rtc_gpio_user_t user) { if (user < RTC_GPIO_USER_WIFI || user > RTC_GPIO_USER_PMIC) return; mutex_lock(&rtc_misc->lock); mtk_misc_set_gpio_32k_status(user, false); mutex_unlock(&rtc_misc->lock); } EXPORT_SYMBOL(rtc_gpio_disable_32k); bool mtk_misc_low_power_detected(void) { u32 ret; mutex_lock(&rtc_misc->lock); ret = __mtk_misc_get_spare_register(RTC_LP_DET); mutex_unlock(&rtc_misc->lock); return !!ret; } EXPORT_SYMBOL(mtk_misc_low_power_detected); void mtk_misc_mark_recovery(void) { mutex_lock(&rtc_misc->lock); __mtk_misc_set_spare_register(RTC_FAC_RESET, 0x1); mutex_unlock(&rtc_misc->lock); } void mtk_misc_mark_fast(void) { mutex_lock(&rtc_misc->lock); __mtk_misc_set_spare_register(RTC_FAST_BOOT, 0x1); mutex_unlock(&rtc_misc->lock); } static void mt_power_off(void) { u32 bbpu; int ret; mutex_lock(&rtc_misc->lock); bbpu = RTC_BBPU_KEY | RTC_BBPU_AUTO | RTC_BBPU_PWREN; ret = regmap_write(rtc_misc->regmap, rtc_misc->addr_base + RTC_BBPU, bbpu); if (ret < 0) dev_err(rtc_misc->dev, "regmap write error!!!\n"); mtk_rtc_write_trigger(); mutex_unlock(&rtc_misc->lock); } static int mt6397_misc_probe(struct platform_device *pdev) { struct resource *res; struct device_node *np = pdev->dev.parent->of_node; struct mt6397_chip *mt6397_chip = dev_get_drvdata(pdev->dev.parent); struct mt6397_misc *misc; int pm_off = 0; if (!np) return -EINVAL; misc = devm_kzalloc(&pdev->dev, sizeof(struct mt6397_misc), GFP_KERNEL); if (!misc) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); misc->addr_base = res->start; misc->regmap = mt6397_chip->regmap; misc->dev = &pdev->dev; mutex_init(&misc->lock); rtc_misc = misc; platform_set_drvdata(pdev, misc); pm_off = of_property_read_bool(np, "mediatek,system-power-controller"); if (pm_off && !pm_power_off) pm_power_off = mt_power_off; return 0; } static const struct of_device_id mt6397_misc_of_match[] = { { .compatible = "mediatek,mt6397-misc", }, { .compatible = "mediatek,mt6323-misc", }, { .compatible = "mediatek,mt6392-misc", }, { } }; MODULE_DEVICE_TABLE(of, mt6397_misc_of_match); static struct platform_driver mt6397_misc_driver = { .driver = { .name = "mt6397-misc", .of_match_table = mt6397_misc_of_match, }, .probe = mt6397_misc_probe, }; module_platform_driver(mt6397_misc_driver); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Tianping Fang "); MODULE_DESCRIPTION("Misc Driver for MediaTek MT6323 PMIC"); MODULE_ALIAS("platform:mt6397-misc");