| /****************************************************************************** |
| * |
| * (C)Copyright 2022 ASRMicro. All Rights Reserved. |
| * |
| * THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF ASRMicro. |
| * The copyright notice above does not evidence any actual or intended |
| * publication of such source code. |
| * This Module contains Proprietary Information of ASRMicro and should be |
| * treated as Confidential. |
| * The information in this file is provided for the exclusive use of the |
| * licensees of ASRMicro. |
| * Such users have the right to use, modify, and incorporate this code into |
| * products for purposes authorized by the license agreement provided they |
| * include this notice and the associated copyright notice with any such |
| * product. |
| * The information in this file is provided "AS IS" without warranty. |
| * |
| ******************************************************************************/ |
| |
| |
| /****************************************************************************** |
| * Include files |
| ******************************************************************************/ |
| #include <linux/kernel.h> |
| #include <linux/module.h> |
| #include <linux/slab.h> |
| #include <linux/delay.h> |
| #include <linux/of_device.h> |
| #include <linux/uaccess.h> |
| #include <linux/proc_fs.h> |
| #include <linux/fs.h> |
| #include <linux/debugfs.h> |
| #include <linux/gpio.h> |
| #include <linux/regulator/consumer.h> |
| #include <linux/pinctrl/consumer.h> |
| #include <linux/platform_device.h> |
| #include <linux/mutex.h> |
| #include <linux/of.h> |
| #include <linux/of_gpio.h> |
| #include <asm/io.h> |
| #include <linux/interrupt.h> |
| #include <linux/cputype.h> |
| #include <linux/cdev.h> |
| #include <linux/device.h> |
| #include <linux/ioctl.h> |
| #include <linux/mfd/88pm80x.h> |
| #include <linux/mfd/pm813.h> |
| |
| |
| /****************************************************************************** |
| * Macro definitions |
| ******************************************************************************/ |
| #define AUDIO_PA_MAGIC 'y' |
| #define PA_ENABLE _IO(AUDIO_PA_MAGIC, 'a') |
| #define PA_DISABLE _IO(AUDIO_PA_MAGIC, 'b') |
| |
| |
| /****************************************************************************** |
| * Local variable definitions |
| ******************************************************************************/ |
| static struct dentry *pm813pa_control = NULL; |
| static char msg[10]; |
| |
| static dev_t pm813pa_dev = 0; |
| static struct class *pm813pa_class; |
| static struct cdev pm813pa_cdev; |
| |
| |
| /****************************************************************************** |
| * function definitions |
| ******************************************************************************/ |
| extern int pm813_extern_read(int page, int reg); |
| extern int pm813_extern_setbits(int page, int reg, unsigned char mask, unsigned char val); |
| |
| int PMIC_is_PM813(void) |
| { |
| int ret = 0; |
| unsigned int chip_id = 0; |
| unsigned int val_30 = 0; |
| unsigned int val_31 = 0; |
| |
| chip_id = pm813_extern_read(PM813_BASE_PAGE, 0x00); |
| |
| if ((CHIP_PM813_ID == chip_id) || (CHIP_PM813S_A1_ID == chip_id) || CHIP_PM813S_A0_ID == chip_id) { |
| val_30 = pm813_extern_read(PM813_BASE_PAGE, 0x30); |
| val_31 = pm813_extern_read(PM813_BASE_PAGE, 0x31); |
| printk(KERN_INFO "%s: chip_id=0x%0x, val_30=0x%0x, val_31=0x%0x\n", __FUNCTION__, chip_id, val_30, val_31); |
| ret = 1; |
| } |
| return ret; |
| } |
| |
| void enable_PM813_A1_classD(void) |
| { |
| if (PMIC_is_PM813()) { |
| pm813_extern_setbits(PM813_BASE_PAGE, 0x30, 0x03, 0x02); |
| pm813_extern_setbits(PM813_BASE_PAGE, 0x31, 0x02, 0x00); |
| |
| pm813_extern_setbits(PM813_BASE_PAGE, 0x31, 0x01, 0x01); |
| //pm813_extern_setbits(PM813_BASE_PAGE, 0x31, 0x01, 0x00); |
| |
| pm813_extern_setbits(PM813_BASE_PAGE, 0x31, 0x40, 0x00); |
| } |
| |
| return; |
| } |
| |
| void disable_PM813_A1_classD(void) |
| { |
| if (PMIC_is_PM813()) { |
| pm813_extern_setbits(PM813_BASE_PAGE, 0x31, 0x40, 0x40); |
| |
| pm813_extern_setbits(PM813_BASE_PAGE, 0x31, 0x01, 0x00); |
| //pm813_extern_setbits(PM813_BASE_PAGE, 0x31, 0x01, 0x01); |
| } |
| return; |
| } |
| |
| void enable_PM813_classD(void) |
| { |
| if (PMIC_is_PM813()) { |
| pm813_extern_setbits(PM813_BASE_PAGE, 0x30, 0x03, 0x02); |
| pm813_extern_setbits(PM813_BASE_PAGE, 0x31, 0x02, 0x00); |
| |
| //pm813_extern_setbits(PM813_BASE_PAGE, 0x31, 0x01, 0x01); |
| pm813_extern_setbits(PM813_BASE_PAGE, 0x31, 0x01, 0x00); |
| |
| pm813_extern_setbits(PM813_BASE_PAGE, 0x31, 0x40, 0x00); |
| } |
| |
| return; |
| } |
| |
| void disable_PM813_classD(void) |
| { |
| if (PMIC_is_PM813()) { |
| pm813_extern_setbits(PM813_BASE_PAGE, 0x31, 0x40, 0x40); |
| |
| //pm813_extern_setbits(PM813_BASE_PAGE, 0x31, 0x01, 0x00); |
| pm813_extern_setbits(PM813_BASE_PAGE, 0x31, 0x01, 0x01); |
| } |
| return; |
| } |
| |
| |
| /****************************************************************************** |
| * functions for debugfs |
| ******************************************************************************/ |
| static ssize_t debugfs_read(struct file *file, char __user *user_buf, |
| size_t count, loff_t *ppos) |
| { |
| |
| printk(KERN_INFO "%s/L%d.\n", __FUNCTION__, __LINE__); |
| |
| return 0; |
| |
| } |
| |
| static ssize_t debugfs_write(struct file *file, |
| const char __user *user_buf, |
| size_t count, loff_t *ppos) |
| { |
| int ret = 0; |
| size_t tmp_count = 0; |
| |
| memset(msg, 0x00, sizeof(msg)); |
| tmp_count = count; |
| |
| if (tmp_count >= sizeof(msg)) { |
| tmp_count = sizeof(msg) - 1; |
| } |
| |
| /* copy the content from user space to kernel space */ |
| ret = copy_from_user(msg, user_buf, tmp_count); |
| if (ret) { |
| printk(KERN_ALERT "copy from user fail \n"); |
| return -EFAULT; |
| } |
| |
| switch (msg[0]) { |
| case 'c':/* input command# echo c > /sys/kernel/debug/pm813pa */ |
| printk(KERN_INFO "input %c. \n", msg[0]); |
| disable_PM813_A1_classD(); |
| break; |
| |
| case 'd':/* input command# echo d > /sys/kernel/debug/pm813pa */ |
| printk(KERN_INFO "input %c. \n", msg[0]); |
| enable_PM813_A1_classD(); |
| break; |
| |
| case '0':/* input command# echo 0 > /sys/kernel/debug/pm813pa */ |
| printk(KERN_INFO "input %c to disable pa pm813pa\n", msg[0]); |
| disable_PM813_classD(); |
| break; |
| |
| case '1':/* input command# echo 1 > /sys/kernel/debug/pm813pa */ |
| printk(KERN_INFO "input %c to enable pa pm813pa\n", msg[0]); |
| enable_PM813_classD(); |
| break; |
| |
| default:/* input command# */ |
| printk(KERN_INFO "input invalid.\n"); |
| break; |
| } |
| |
| return tmp_count; |
| } |
| |
| static const struct file_operations debugfs_ops = { |
| .owner = THIS_MODULE, |
| .open = simple_open, |
| .read = debugfs_read, |
| .write = debugfs_write, |
| }; |
| |
| static inline int pm813pa_debugfs_init(void) |
| { |
| |
| pm813pa_control = debugfs_create_file("pm813pa", S_IRUGO | S_IFREG, NULL, NULL, &debugfs_ops); |
| |
| if (pm813pa_control == NULL) { |
| pr_err("create pm813pa debugfs error!\n"); |
| return -ENOENT; |
| } |
| else if (pm813pa_control == ERR_PTR(-ENODEV)) { |
| pr_err("CONFIG_DEBUG_FS is not enabled!\n"); |
| return -ENOENT; |
| } |
| |
| return 0; |
| } |
| |
| static void pm813pa_debugfs_remove(void) |
| { |
| if (NULL != pm813pa_control) { |
| debugfs_remove_recursive(pm813pa_control); |
| } |
| |
| return; |
| } |
| |
| |
| /****************************************************************************** |
| * functions for cdev |
| ******************************************************************************/ |
| static long cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
| { |
| printk(KERN_INFO "%s/L%d, cmd = %d", __FUNCTION__, __LINE__, cmd); |
| |
| switch (cmd) { |
| case PA_ENABLE: |
| printk(KERN_INFO "enable pa pm813pa\n"); |
| enable_PM813_classD(); |
| break; |
| case PA_DISABLE: |
| printk(KERN_INFO "disable pa pm813pa\n"); |
| disable_PM813_classD(); |
| break; |
| default: |
| pr_info("Default\n"); |
| break; |
| } |
| return 0; |
| } |
| |
| static struct file_operations cdev_fops = |
| { |
| .owner = THIS_MODULE, |
| .open = simple_open, |
| .unlocked_ioctl = cdev_ioctl, |
| }; |
| |
| static int pm813pa_cdev_init(void) |
| { |
| /*Allocating Major number*/ |
| if ((alloc_chrdev_region(&pm813pa_dev, 0, 1, "pm813pa_Dev")) < 0) { |
| pr_err("Cannot allocate major number\n"); |
| return -1; |
| } |
| printk(KERN_INFO "%s/L%d, Major = %d, Minor = %d\n", __FUNCTION__, __LINE__, MAJOR(pm813pa_dev), MINOR(pm813pa_dev)); |
| |
| /*Creating cdev structure*/ |
| cdev_init(&pm813pa_cdev, &cdev_fops); |
| |
| /*Adding character device to the system*/ |
| if ((cdev_add(&pm813pa_cdev, pm813pa_dev, 1)) < 0) { |
| pr_err("Cannot add the device to the system\n"); |
| unregister_chrdev_region(pm813pa_dev, 1); |
| return -1; |
| } |
| |
| /*Creating struct class*/ |
| if ((pm813pa_class = class_create(THIS_MODULE, "pm813pa_class")) == NULL) { |
| pr_err("Cannot create the struct class\n"); |
| unregister_chrdev_region(pm813pa_dev, 1); |
| return -2; |
| } |
| |
| /*Creating device*/ |
| if ((device_create(pm813pa_class, NULL, pm813pa_dev, NULL, "audio_pa")) == NULL) { |
| pr_err("Cannot create the Device 1\n"); |
| class_destroy(pm813pa_class); |
| unregister_chrdev_region(pm813pa_dev, 1); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| static void pm813pa_cdev_exit(void) |
| { |
| device_destroy(pm813pa_class, pm813pa_dev); |
| class_destroy(pm813pa_class); |
| cdev_del(&pm813pa_cdev); |
| unregister_chrdev_region(pm813pa_dev, 1); |
| pr_info("Device Driver Remove...Done!!!\n"); |
| } |
| |
| |
| /****************************************************************************** |
| * functions for module |
| ******************************************************************************/ |
| static int pm813pa_init(void) |
| { |
| printk(KERN_INFO "%s/L%d.\n", __FUNCTION__, __LINE__); |
| |
| pm813pa_cdev_init(); |
| pm813pa_debugfs_init(); |
| |
| return 0; |
| } |
| |
| static void pm813pa_exit(void) |
| { |
| pm813pa_cdev_exit(); |
| pm813pa_debugfs_remove(); |
| } |
| |
| module_init(pm813pa_init); |
| module_exit(pm813pa_exit); |
| |
| MODULE_DESCRIPTION("Driver for PM813PA"); |
| MODULE_AUTHOR("yjgwang@asrmicro.com"); |
| MODULE_LICENSE("GPL"); |