|
@@ -0,0 +1,255 @@
|
|
|
|
|
+#include <linux/module.h>
|
|
|
|
|
+#include <linux/fs.h>
|
|
|
|
|
+#include <linux/cdev.h>
|
|
|
|
|
+#include <linux/device.h>
|
|
|
|
|
+#include <linux/uaccess.h>
|
|
|
|
|
+#include <linux/slab.h>
|
|
|
|
|
+
|
|
|
|
|
+#include <linux/sched.h>
|
|
|
|
|
+#include <linux/io.h>
|
|
|
|
|
+
|
|
|
|
|
+#define DEVICE_NAME "com12v"
|
|
|
|
|
+#define CLASS_NAME "com12v_class"
|
|
|
|
|
+
|
|
|
|
|
+#define MAJOR_NUM 56
|
|
|
|
|
+#define MINOR_NUM 20
|
|
|
|
|
+#define DEVICE_COUNT 1
|
|
|
|
|
+
|
|
|
|
|
+#define NUM_COMPORTS 6
|
|
|
|
|
+#define A_LONG_TIME 100 // 10.0 seconds
|
|
|
|
|
+#define DEFAULT_DELAY 20 // 2.0 second
|
|
|
|
|
+
|
|
|
|
|
+typedef enum
|
|
|
|
|
+{
|
|
|
|
|
+ PPS_OFF = 0,
|
|
|
|
|
+ PPS_AWAITING_ON,
|
|
|
|
|
+ PPS_ON,
|
|
|
|
|
+ NUM_POWER_PORT_STATES
|
|
|
|
|
+} ENUM_POWER_PORT_STATES;
|
|
|
|
|
+
|
|
|
|
|
+static struct class *com12v_class = NULL;
|
|
|
|
|
+static struct device *com12v_device = NULL;
|
|
|
|
|
+static struct cdev com12v_cdev;
|
|
|
|
|
+static dev_t dev_num;
|
|
|
|
|
+
|
|
|
|
|
+static unsigned int muiPowerOnDelayCount = DEFAULT_DELAY;
|
|
|
|
|
+static unsigned int mauiPowerPortState[NUM_COMPORTS];
|
|
|
|
|
+
|
|
|
|
|
+static struct delayed_work delay_work1;
|
|
|
|
|
+
|
|
|
|
|
+static unsigned int com12v_ctl_phy_addr[NUM_COMPORTS] = {0xFD6E0750, 0xFD6E0760, 0xFD6E0770, 0xFD6E0780, 0xFD6D0920, 0xFD6D0930};
|
|
|
|
|
+static void __iomem *com12v_ctl_vir_addr[NUM_COMPORTS];
|
|
|
|
|
+static void com12v_delay_work_func(struct work_struct *work)
|
|
|
|
|
+{
|
|
|
|
|
+ static unsigned int uiTimeSinceLastPowerOn = A_LONG_TIME;
|
|
|
|
|
+ int i;
|
|
|
|
|
+ unsigned int value = 0;
|
|
|
|
|
+
|
|
|
|
|
+ // Keep incrementing the uiTimeSinceLastPowerOn until it is large enough
|
|
|
|
|
+ // that we can safely assume a long time has passed since the last port
|
|
|
|
|
+ // transitioned on.
|
|
|
|
|
+ if (uiTimeSinceLastPowerOn < A_LONG_TIME)
|
|
|
|
|
+ {
|
|
|
|
|
+ ++uiTimeSinceLastPowerOn;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // If a port is waiting to power on and enough time has passed since the
|
|
|
|
|
+ // last port powered on then go ahead and power on the waiting port. The
|
|
|
|
|
+ // break statement insure we don't power on more than one port at a time.
|
|
|
|
|
+ for (i=0; i < NUM_COMPORTS; ++i)
|
|
|
|
|
+ {
|
|
|
|
|
+ // printk("%d", mauiPowerPortState[i]);
|
|
|
|
|
+ if ((mauiPowerPortState[i] == PPS_AWAITING_ON) && (uiTimeSinceLastPowerOn >= muiPowerOnDelayCount))
|
|
|
|
|
+ {
|
|
|
|
|
+ mauiPowerPortState[i] = PPS_ON;
|
|
|
|
|
+ uiTimeSinceLastPowerOn = 0;
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ // printk("\n");
|
|
|
|
|
+
|
|
|
|
|
+ // Turn on/off hw power supply pins based on current states.
|
|
|
|
|
+ for (i=0; i < NUM_COMPORTS; ++i)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (mauiPowerPortState[i] == PPS_ON)
|
|
|
|
|
+ {
|
|
|
|
|
+ value = readl(com12v_ctl_vir_addr[i]);
|
|
|
|
|
+ value &= 0xfffffffe;
|
|
|
|
|
+ writel(value, com12v_ctl_vir_addr[i]);
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (mauiPowerPortState[i] == PPS_OFF)
|
|
|
|
|
+ {
|
|
|
|
|
+ value = readl(com12v_ctl_vir_addr[i]);
|
|
|
|
|
+ value |= 0x1;
|
|
|
|
|
+ writel(value, com12v_ctl_vir_addr[i]);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ schedule_delayed_work(&delay_work1, msecs_to_jiffies(100));
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+static int com12v_open(struct inode *inode, struct file *file)
|
|
|
|
|
+{
|
|
|
|
|
+ // pr_info("com12v: device opened\n");
|
|
|
|
|
+ return 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+static int com12v_release(struct inode *inode, struct file *file)
|
|
|
|
|
+{
|
|
|
|
|
+ // pr_info("com12v: device closed\n");
|
|
|
|
|
+ return 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+static ssize_t com12v_read(struct file *file, char __user *user_buf,
|
|
|
|
|
+ size_t len, loff_t *offset)
|
|
|
|
|
+{
|
|
|
|
|
+ size_t read_len;
|
|
|
|
|
+ return read_len;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+static ssize_t com12v_write(struct file *file, const char __user *user_buf,
|
|
|
|
|
+ size_t len, loff_t *offset)
|
|
|
|
|
+{
|
|
|
|
|
+ int i = 0;
|
|
|
|
|
+ int iPortIndex;
|
|
|
|
|
+ char buff[1024];
|
|
|
|
|
+ char c;
|
|
|
|
|
+ size_t write_len;
|
|
|
|
|
+
|
|
|
|
|
+ if (len > sizeof(buff))
|
|
|
|
|
+ {
|
|
|
|
|
+ write_len = sizeof(buff);
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ write_len = len;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (copy_from_user(buff, user_buf, write_len))
|
|
|
|
|
+ {
|
|
|
|
|
+ return -EFAULT;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ for (i = 0; i < write_len; i++)
|
|
|
|
|
+ {
|
|
|
|
|
+ c = buff[i];
|
|
|
|
|
+ if ((c >= 'A') && (c <= 'F'))
|
|
|
|
|
+ {
|
|
|
|
|
+ iPortIndex = c - 'A';
|
|
|
|
|
+
|
|
|
|
|
+ if (muiPowerOnDelayCount == 0)
|
|
|
|
|
+ {
|
|
|
|
|
+ mauiPowerPortState[iPortIndex] = PPS_ON;
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ if (mauiPowerPortState[iPortIndex] == PPS_OFF)
|
|
|
|
|
+ {
|
|
|
|
|
+ mauiPowerPortState[iPortIndex] = PPS_AWAITING_ON;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ else if ((c >= 'a') && (c <= 'f'))
|
|
|
|
|
+ {
|
|
|
|
|
+ iPortIndex = c - 'a';
|
|
|
|
|
+
|
|
|
|
|
+ mauiPowerPortState[iPortIndex] = PPS_OFF;
|
|
|
|
|
+ }
|
|
|
|
|
+ else if ((c >= '0') && (c <= '9'))
|
|
|
|
|
+ {
|
|
|
|
|
+ muiPowerOnDelayCount = (c - '0') * 10;
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ printk("com12v: invalid character %c\n", c);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return write_len;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+static struct file_operations com12v_fops = {
|
|
|
|
|
+ .owner = THIS_MODULE,
|
|
|
|
|
+ .open = com12v_open,
|
|
|
|
|
+ .release = com12v_release,
|
|
|
|
|
+ .read = com12v_read,
|
|
|
|
|
+ .write = com12v_write,
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+int com12v_init(void)
|
|
|
|
|
+{
|
|
|
|
|
+ int ret;
|
|
|
|
|
+ int i = 0;
|
|
|
|
|
+
|
|
|
|
|
+ for(i = 0; i < NUM_COMPORTS; i++)
|
|
|
|
|
+ {
|
|
|
|
|
+ com12v_ctl_vir_addr[i] = ioremap(com12v_ctl_phy_addr[i], 4);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ pr_info("com12v: initializing driver\n");
|
|
|
|
|
+
|
|
|
|
|
+ memset(mauiPowerPortState, 0, sizeof(mauiPowerPortState));
|
|
|
|
|
+ INIT_DELAYED_WORK(&delay_work1, com12v_delay_work_func);
|
|
|
|
|
+ schedule_delayed_work(&delay_work1, msecs_to_jiffies(100));
|
|
|
|
|
+
|
|
|
|
|
+ dev_num = MKDEV(MAJOR_NUM, MINOR_NUM);
|
|
|
|
|
+ ret = register_chrdev_region(dev_num, DEVICE_COUNT, DEVICE_NAME);
|
|
|
|
|
+ if (ret < 0)
|
|
|
|
|
+ {
|
|
|
|
|
+ pr_err("com12v: Failed to register device number %d:%d\n",
|
|
|
|
|
+ MAJOR_NUM, MINOR_NUM);
|
|
|
|
|
+ return ret;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ cdev_init(&com12v_cdev, &com12v_fops);
|
|
|
|
|
+ com12v_cdev.owner = THIS_MODULE;
|
|
|
|
|
+ ret = cdev_add(&com12v_cdev, dev_num, DEVICE_COUNT);
|
|
|
|
|
+ if (ret < 0)
|
|
|
|
|
+ {
|
|
|
|
|
+ pr_err("com12v: Failed to add cdev\n");
|
|
|
|
|
+ goto err_unregister_region;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ com12v_class = class_create(THIS_MODULE, CLASS_NAME);
|
|
|
|
|
+ if (IS_ERR(com12v_class))
|
|
|
|
|
+ {
|
|
|
|
|
+ pr_err("com12v: Failed to create class\n");
|
|
|
|
|
+ ret = PTR_ERR(com12v_class);
|
|
|
|
|
+ goto err_cdev_del;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ com12v_device = device_create(com12v_class, NULL, dev_num,
|
|
|
|
|
+ NULL, DEVICE_NAME);
|
|
|
|
|
+ if (IS_ERR(com12v_device))
|
|
|
|
|
+ {
|
|
|
|
|
+ pr_err("com12v: Failed to create device\n");
|
|
|
|
|
+ ret = PTR_ERR(com12v_device);
|
|
|
|
|
+ goto err_class_destroy;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ pr_info("com12v: driver initialized successfully, device node /dev/%s\n",
|
|
|
|
|
+ DEVICE_NAME);
|
|
|
|
|
+ return 0;
|
|
|
|
|
+
|
|
|
|
|
+err_class_destroy:
|
|
|
|
|
+ class_destroy(com12v_class);
|
|
|
|
|
+err_cdev_del:
|
|
|
|
|
+ cdev_del(&com12v_cdev);
|
|
|
|
|
+err_unregister_region:
|
|
|
|
|
+ unregister_chrdev_region(dev_num, DEVICE_COUNT);
|
|
|
|
|
+ return ret;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void com12v_exit(void)
|
|
|
|
|
+{
|
|
|
|
|
+ int i = 0;
|
|
|
|
|
+ cancel_delayed_work_sync(&delay_work1);
|
|
|
|
|
+ device_destroy(com12v_class, dev_num);
|
|
|
|
|
+ class_destroy(com12v_class);
|
|
|
|
|
+ cdev_del(&com12v_cdev);
|
|
|
|
|
+ unregister_chrdev_region(dev_num, DEVICE_COUNT);
|
|
|
|
|
+
|
|
|
|
|
+ for(i = 0; i < NUM_COMPORTS; i++)
|
|
|
|
|
+ {
|
|
|
|
|
+ iounmap(com12v_ctl_vir_addr[i]);
|
|
|
|
|
+ }
|
|
|
|
|
+ pr_info("com12v: driver unloaded\n");
|
|
|
|
|
+}
|
|
|
|
|
+
|