289 lines
6.3 KiB
C
289 lines
6.3 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2017 MediaTek Inc.
|
|
*/
|
|
/*
|
|
* @file mtk_udi.c
|
|
* @brief Driver for UDI interface
|
|
*
|
|
*/
|
|
/* system includes */
|
|
#ifdef __KERNEL__
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/init.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/seq_file.h>
|
|
#include <linux/jiffies.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/platform_device.h>
|
|
#ifdef CONFIG_OF
|
|
#include <linux/of.h>
|
|
#include <linux/of_irq.h>
|
|
#include <linux/of_address.h>
|
|
#endif
|
|
#else
|
|
#include <common.h> /* for printf */
|
|
#endif
|
|
|
|
/* local includes */
|
|
#include <sync_write.h>
|
|
#include "mtk_udi_internal.h"
|
|
|
|
/*-----------------------------------------*/
|
|
/* Reused code start */
|
|
/*-----------------------------------------*/
|
|
|
|
#ifdef __KERNEL__
|
|
#define udi_read(addr) readl(addr)
|
|
#define udi_write(addr, val) mt_reg_sync_writel((val), ((void *)addr))
|
|
#endif
|
|
|
|
/*
|
|
* LOG
|
|
*/
|
|
#define UDI_TAG "[mt_udi] "
|
|
#ifdef __KERNEL__
|
|
#ifdef USING_XLOG
|
|
#include <linux/xlog.h>
|
|
#define udi_info(fmt, args...) \
|
|
pr_info(ANDROID_LOG_INFO, UDI_TAG, fmt, ##args)
|
|
#else
|
|
#define udi_info(fmt, args...) \
|
|
pr_info(UDI_TAG fmt, ##args)
|
|
#endif
|
|
#else
|
|
#define udi_info(fmt, args...) \
|
|
printf(UDI_TAG fmt, ##args)
|
|
#endif
|
|
|
|
#ifdef __KERNEL__
|
|
/* Device infrastructure */
|
|
static int udi_remove(struct platform_device *pdev)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int udi_probe(struct platform_device *pdev)
|
|
{
|
|
udi_info("UDI Initial.\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int udi_suspend(struct platform_device *pdev, pm_message_t state)
|
|
{
|
|
udi_info("UDI suspend\n");
|
|
return 0;
|
|
}
|
|
|
|
static int udi_resume(struct platform_device *pdev)
|
|
{
|
|
udi_info("UDI resume\n");
|
|
return 0;
|
|
}
|
|
|
|
struct platform_device udi_pdev = {
|
|
.name = "mt_udi",
|
|
.id = -1,
|
|
};
|
|
|
|
static struct platform_driver udi_pdrv = {
|
|
.remove = udi_remove,
|
|
.shutdown = NULL,
|
|
.probe = udi_probe,
|
|
.suspend = udi_suspend,
|
|
.resume = udi_resume,
|
|
.driver = {
|
|
.name = "mt_udi",
|
|
},
|
|
};
|
|
|
|
|
|
#ifdef CONFIG_PROC_FS
|
|
|
|
static char *_copy_from_user_for_proc(const char __user *buffer, size_t count)
|
|
{
|
|
char *buf = (char *)__get_free_page(GFP_USER);
|
|
|
|
if (buf == NULL)
|
|
return NULL;
|
|
if (count >= PAGE_SIZE)
|
|
goto out0;
|
|
if (copy_from_user(buf, buffer, count) != 0U)
|
|
goto out0;
|
|
|
|
buf[count] = '\0';
|
|
return buf;
|
|
|
|
out0:
|
|
free_page((unsigned long)buf);
|
|
return NULL;
|
|
}
|
|
|
|
/* udi_pinmux_switch */
|
|
static int udi_pinmux_proc_show(struct seq_file *m, void *v)
|
|
{
|
|
seq_printf(m, "CPU UDI pinmux reg[0x%p] = 0x%x.\n",
|
|
UDIPIN_UDI_MUX1, udi_read(UDIPIN_UDI_MUX1));
|
|
seq_printf(m, "CPU UDI pinmux reg[0x%p] = 0x%x.\n",
|
|
UDIPIN_UDI_MUX2, udi_read(UDIPIN_UDI_MUX2));
|
|
seq_printf(m, "APU UDI pinmux reg[0x%p] = 0x%x.\n",
|
|
UDIPIN_UDI_MUX3, udi_read(UDIPIN_UDI_MUX3));
|
|
seq_printf(m, "APU UDI pinmux reg[0x%p] = 0x%x.\n",
|
|
UDIPIN_UDI_MUX4, udi_read(UDIPIN_UDI_MUX4));
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t udi_pinmux_proc_write(struct file *file,
|
|
const char __user *buffer, size_t count, loff_t *pos)
|
|
{
|
|
char *buf = _copy_from_user_for_proc(buffer, count);
|
|
unsigned int pin_switch = 0U;
|
|
|
|
if (buf == NULL)
|
|
return -EINVAL;
|
|
|
|
if (kstrtoint(buf, 10, &pin_switch) == 0) {
|
|
if (pin_switch == 1U) {
|
|
udi_write(UDIPIN_UDI_MUX1, UDIPIN_UDI_MUX1_VALUE);
|
|
udi_write(UDIPIN_UDI_MUX2, UDIPIN_UDI_MUX2_VALUE);
|
|
}
|
|
if (pin_switch == 2U) {
|
|
udi_write(UDIPIN_UDI_MUX3, UDIPIN_UDI_MUX3_VALUE);
|
|
udi_write(UDIPIN_UDI_MUX4, UDIPIN_UDI_MUX4_VALUE);
|
|
}
|
|
} else
|
|
udi_info("echo dbg_lv (dec) > /proc/udi/udi_debug\n");
|
|
|
|
free_page((unsigned long)buf);
|
|
return (long)count;
|
|
}
|
|
|
|
#define PROC_FOPS_RW(name) \
|
|
static int name ## _proc_open(struct inode *inode, \
|
|
struct file *file) \
|
|
{ \
|
|
return single_open(file, name ## _proc_show, \
|
|
PDE_DATA(inode)); \
|
|
} \
|
|
static const struct file_operations name ## _proc_fops = { \
|
|
.owner = THIS_MODULE, \
|
|
.open = name ## _proc_open, \
|
|
.read = seq_read, \
|
|
.llseek = seq_lseek, \
|
|
.release = single_release, \
|
|
.write = name ## _proc_write, \
|
|
}
|
|
|
|
#define PROC_FOPS_RO(name) \
|
|
static int name ## _proc_open(struct inode *inode, \
|
|
struct file *file) \
|
|
{ \
|
|
return single_open(file, name ## _proc_show, \
|
|
PDE_DATA(inode));\
|
|
} \
|
|
static const struct file_operations name ## _proc_fops = { \
|
|
.owner = THIS_MODULE, \
|
|
.open = name ## _proc_open, \
|
|
.read = seq_read, \
|
|
.llseek = seq_lseek, \
|
|
.release = single_release, \
|
|
}
|
|
|
|
#define PROC_ENTRY(name) {__stringify(name), &name ## _proc_fops}
|
|
|
|
PROC_FOPS_RW(udi_pinmux); /* for udi pinmux switch */
|
|
|
|
static int _create_procfs(void)
|
|
{
|
|
struct proc_dir_entry *dir = NULL;
|
|
|
|
struct pentry {
|
|
const char *name;
|
|
const struct file_operations *fops;
|
|
};
|
|
|
|
const struct pentry entries[] = {
|
|
PROC_ENTRY(udi_pinmux),
|
|
};
|
|
|
|
dir = proc_mkdir("udi", NULL);
|
|
|
|
if (dir == NULL) {
|
|
udi_info("fail to create /proc/udi @ %s()\n", __func__);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
if (proc_create(entries[0].name, 0664, dir, entries[0].fops) == NULL)
|
|
udi_info("create /proc/udi/udi_pinmux failed\n");
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif /* CONFIG_PROC_FS */
|
|
|
|
/*
|
|
* Module driver
|
|
*/
|
|
static int __init udi_init(void)
|
|
{
|
|
int err = 0;
|
|
struct device_node *node = NULL;
|
|
|
|
node = of_find_compatible_node(NULL, NULL, DEVICE_GPIO);
|
|
if (node == NULL)
|
|
udi_info("error: cannot find node UDI_NODE!\n");
|
|
|
|
/* Setup IO addresses and printf */
|
|
udipin_base = of_iomap(node, 0); /* UDI pinmux reg */
|
|
udi_info("udipin_base = 0x%lx.\n", (unsigned long)udipin_base);
|
|
if (udipin_base == NULL)
|
|
udi_info("udi pinmux get some base NULL.\n");
|
|
|
|
|
|
/* register platform device/driver */
|
|
err = platform_device_register(&udi_pdev);
|
|
|
|
if (err != 0) {
|
|
udi_info("fail to register UDI device @ %s()\n", __func__);
|
|
goto out2;
|
|
}
|
|
|
|
err = platform_driver_register(&udi_pdrv);
|
|
if (err != 0) {
|
|
udi_info("%s(), UDI driver callback register failed..\n",
|
|
__func__);
|
|
return err;
|
|
}
|
|
|
|
#ifdef CONFIG_PROC_FS
|
|
/* init proc */
|
|
if (_create_procfs() != 0) {
|
|
err = -ENOMEM;
|
|
goto out2;
|
|
}
|
|
#endif /* CONFIG_PROC_FS */
|
|
|
|
out2:
|
|
return err;
|
|
}
|
|
|
|
static void __exit udi_exit(void)
|
|
{
|
|
udi_info("UDI de-initialization\n");
|
|
platform_driver_unregister(&udi_pdrv);
|
|
platform_device_unregister(&udi_pdev);
|
|
}
|
|
|
|
module_init(udi_init);
|
|
module_exit(udi_exit);
|
|
|
|
MODULE_DESCRIPTION("MediaTek UDI Driver v0.1");
|
|
MODULE_LICENSE("GPL");
|
|
#endif /* __KERNEL__ */
|