// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
/*
 * Copyright (c) 2020 MediaTek Inc.
 * Author Lili Zhao <lili.zhao@mediatek.com>
 */

#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>


#define MTK_PERIPH_LOG_MIN			(0)
#define MTK_PERIPH_LOG_MUTE			MTK_PERIPH_LOG_MIN	/* set to no log mode */
#define MTK_PERIPH_LOG_ALERT		(1)
#define MTK_PERIPH_LOG_CRIT			(2)
#define MTK_PERIPH_LOG_ERR			(3)
#define MTK_PERIPH_LOG_WARN			(4)
#define MTK_PERIPH_LOG_NOTICE		(5)
#define MTK_PERIPH_LOG_INFO			(6)
#define MTK_PERIPH_LOG_MAX			MTK_PERIPH_LOG_INFO

/* set loglevel control to be useless, use dmesg can see log info */
#define MTK_PERIPH_LOG_NOT_CTRL	(255)

static int periph_init_log_level = MTK_PERIPH_LOG_MUTE; //MTK_PERIPH_LOG_NOT_CTRL;
static int periph_log_level;

#define periph_log(level, fmt, args...) \
do { \
	if (periph_log_level == MTK_PERIPH_LOG_NOT_CTRL) { \
		if (level == MTK_PERIPH_LOG_INFO) \
			pr_info(fmt, ##args); \
		else if (level == MTK_PERIPH_LOG_NOTICE) \
			pr_notice(fmt, ##args); \
		else if (level == MTK_PERIPH_LOG_WARN) \
			pr_warn(fmt, ##args); \
		else if (level == MTK_PERIPH_LOG_ERR) \
			pr_err(fmt, ##args); \
		else if (level == MTK_PERIPH_LOG_CRIT) \
			pr_crit(fmt, ##args); \
		else if (level == MTK_PERIPH_LOG_ALERT) \
			pr_alert(fmt, ##args); \
	} else if (periph_log_level >= level) \
		pr_emerg(fmt, ##args); \
} while (0)

static ssize_t log_level_show(struct device *dev, struct device_attribute *attr,
			      char *buf)
{
	struct mtk_scanpwm *mdp = dev_get_drvdata(dev);
	char *str = buf;
	char *end = buf + PAGE_SIZE;

	str +=
	    scnprintf(str, end - str,
		      "not_ctrl:\t\t%d\nmute:\t\t%d\nalert:\t\t%d\ncritical:\t\t%d\nerror:\t\t%d\n"
		      "warning:\t\t%d\nnotice:\t\t%d\ninfo:\t\t%d\nCurrent:\t%d\n",
		      MTK_PERIPH_LOG_NOT_CTRL, MTK_PERIPH_LOG_MUTE,
		      MTK_PERIPH_LOG_ALERT, MTK_PERIPH_LOG_CRIT,
		      MTK_PERIPH_LOG_ERR, MTK_PERIPH_LOG_WARN,
		      MTK_PERIPH_LOG_NOTICE, MTK_PERIPH_LOG_INFO,
		      periph_log_level);

	return (str - buf);
}

static ssize_t log_level_store(struct device *dev,
			       struct device_attribute *attr,
			       const char *buf, size_t count)
{
	struct mtk_scanpwm *mdp = dev_get_drvdata(dev);
	ssize_t retval;
	int level;

	retval = kstrtoint(buf, 10, &level);
	if (retval == 0) {
		if (((level >= MTK_PERIPH_LOG_MIN) && (level <= MTK_PERIPH_LOG_MAX)) ||
			(level == MTK_PERIPH_LOG_NOT_CTRL))
			periph_log_level = level;
		else
			retval = -EINVAL;
	}

	return (retval < 0) ? retval : count;
}

static DEVICE_ATTR_RW(log_level);

static struct attribute *periph_extend_attrs[] = {
	&dev_attr_log_level.attr,
	NULL,
};
static const struct attribute_group periph_extend_attr_group = {
	.name = "periph_extend",
	.attrs = periph_extend_attrs
};

static const struct of_device_id mtk_peripheral_ctrl_match[] = {
	{ .compatible = "mediatek,mtk-peripheral-ctrl" },
	{},
};
MODULE_DEVICE_TABLE(of, mtk_peripheral_ctrl_match);

static int mtk_peripheral_ctrl_probe(struct platform_device *pdev)
{
	int err;

	periph_log_level = periph_init_log_level;

	dev_info(&pdev->dev, "%s: start support ctrl cmd.\n", __func__);

	err = sysfs_create_group(&pdev->dev.kobj, &periph_extend_attr_group);
	if (err < 0)
		dev_err(&pdev->dev, "ctrl sysfs_create_group failed: %d\n", err);

	periph_log(MTK_PERIPH_LOG_INFO, "[periph][%s] done.\n", __func__);

	return 0;
}

static int mtk_peripheral_ctrl_remove(struct platform_device *pdev)
{
	return 0;
}

static struct platform_driver mtk_peripheral_ctrl_driver = {
	.probe	= mtk_peripheral_ctrl_probe,
	.remove	= mtk_peripheral_ctrl_remove,
	.driver = {
		.name = " mtk-peripheral-ctrl",
		.of_match_table = mtk_peripheral_ctrl_match,
	},
};

module_platform_driver(mtk_peripheral_ctrl_driver);

MODULE_DESCRIPTION("MediaTek DTV SoC based peripheral control");
MODULE_AUTHOR("Lili Zhao <lili.zhao@mediatek.com>");
MODULE_LICENSE("GPL");
