/* * Arkmicro mcu serial driver * * Licensed under GPLv2 or later. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define RX_BUF_SIZE PAGE_SIZE struct mcu_serial_info { int rx_head; int rx_tail; unsigned char *rx_buf; spinlock_t lock; struct work_struct rx_task; }; extern int mcu_serial_send(const unsigned char *buf, int len); extern void mcu_serial_register_rev_handler(void (*handler)(int ch), struct work_struct *task); extern void mcu_serial_unregister_rev_handler(void); static struct mcu_serial_info *msinfo; static void mcu_serial_get_ch(int ch) { struct mcu_serial_info *info = msinfo; unsigned long flags; spin_lock_irqsave(&info->lock, flags); info->rx_buf[info->rx_head] = ch; info->rx_head = (info->rx_head + 1) & (RX_BUF_SIZE - 1); if (info->rx_head == info->rx_tail) { printk("rev buf is full, lost ch.\n"); if (--info->rx_head < 0) info->rx_head = RX_BUF_SIZE - 1; } spin_unlock_irqrestore(&info->lock, flags); } static void mcu_serial_rx_task(struct work_struct *work) { struct mcu_serial_info *info = container_of(work, struct mcu_serial_info, rx_task); int count; unsigned long flags; spin_lock_irqsave(&info->lock, flags); count = info->rx_head - info->rx_tail; spin_unlock_irqrestore(&info->lock, flags); if (count < 0) count += RX_BUF_SIZE; printk("rx count=%d.\n", count); } static int ark_mcu_serial_probe(struct platform_device *pdev) { struct mcu_serial_info *info = devm_kzalloc(&pdev->dev, sizeof(struct mcu_serial_info), GFP_KERNEL); if (!info) return -ENOMEM; info->rx_buf = devm_kzalloc(&pdev->dev, RX_BUF_SIZE, GFP_KERNEL); if (!info->rx_buf) return -ENOMEM; msinfo = info; INIT_WORK(&info->rx_task, mcu_serial_rx_task); mcu_serial_register_rev_handler(mcu_serial_get_ch, &info->rx_task); platform_set_drvdata(pdev, info); return 0; } static int ark_mcu_serial_remove(struct platform_device *pdev) { struct mcu_serial_info *info = platform_get_drvdata(pdev); cancel_work_sync(&info->rx_task); mcu_serial_unregister_rev_handler(); return 0; } static const struct of_device_id ark_mcu_serial_of_match[] = { { .compatible = "arkmicro,ark-mcu-serial", }, {}, }; static struct platform_driver ark_mcu_serial_platform_driver = { .probe = ark_mcu_serial_probe, .remove = ark_mcu_serial_remove, .driver = { .name = "ark-mcu-serial", .of_match_table = of_match_ptr(ark_mcu_serial_of_match), }, }; static int __init ark_mcu_serial_init(void) { return platform_driver_register(&ark_mcu_serial_platform_driver); } static void __exit ark_mcu_serial_exit(void) { platform_driver_unregister(&ark_mcu_serial_platform_driver); } /* * While this can be a module, if builtin it's most likely the console * So let's leave module_exit but move module_init to an earlier place */ arch_initcall(ark_mcu_serial_init); module_exit(ark_mcu_serial_exit); MODULE_AUTHOR("Sim"); MODULE_DESCRIPTION("Arkmicro mcu serial driver"); MODULE_LICENSE("GPL v2");