brintos

brintos / linux-shallow public Read only

0
0
Text · 8.2 KiB · 59bab76 Raw
339 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright 2018-2020 Broadcom.4 */5 6#include <linux/tty.h>7#include <linux/tty_driver.h>8#include <linux/tty_flip.h>9 10#include "bcm_vk.h"11 12/* TTYVK base offset is 0x30000 into BAR1 */13#define BAR1_TTYVK_BASE_OFFSET	0x30000014/* Each TTYVK channel (TO or FROM) is 0x10000 */15#define BAR1_TTYVK_CHAN_OFFSET	0x10000016/* Each TTYVK channel has TO and FROM, hence the * 2 */17#define BAR1_TTYVK_BASE(index)	(BAR1_TTYVK_BASE_OFFSET + \18				 ((index) * BAR1_TTYVK_CHAN_OFFSET * 2))19/* TO TTYVK channel base comes before FROM for each index */20#define TO_TTYK_BASE(index)	BAR1_TTYVK_BASE(index)21#define FROM_TTYK_BASE(index)	(BAR1_TTYVK_BASE(index) + \22				 BAR1_TTYVK_CHAN_OFFSET)23 24struct bcm_vk_tty_chan {25	u32 reserved;26	u32 size;27	u32 wr;28	u32 rd;29	u32 *data;30};31 32#define VK_BAR_CHAN(v, DIR, e)	((v)->DIR##_offset \33				 + offsetof(struct bcm_vk_tty_chan, e))34#define VK_BAR_CHAN_SIZE(v, DIR)	VK_BAR_CHAN(v, DIR, size)35#define VK_BAR_CHAN_WR(v, DIR)		VK_BAR_CHAN(v, DIR, wr)36#define VK_BAR_CHAN_RD(v, DIR)		VK_BAR_CHAN(v, DIR, rd)37#define VK_BAR_CHAN_DATA(v, DIR, off)	(VK_BAR_CHAN(v, DIR, data) + (off))38 39#define VK_BAR0_REGSEG_TTY_DB_OFFSET	0x86c40 41/* Poll every 1/10 of second - temp hack till we use MSI interrupt */42#define SERIAL_TIMER_VALUE (HZ / 10)43 44static void bcm_vk_tty_poll(struct timer_list *t)45{46	struct bcm_vk *vk = from_timer(vk, t, serial_timer);47 48	queue_work(vk->tty_wq_thread, &vk->tty_wq_work);49	mod_timer(&vk->serial_timer, jiffies + SERIAL_TIMER_VALUE);50}51 52irqreturn_t bcm_vk_tty_irqhandler(int irq, void *dev_id)53{54	struct bcm_vk *vk = dev_id;55 56	queue_work(vk->tty_wq_thread, &vk->tty_wq_work);57 58	return IRQ_HANDLED;59}60 61static void bcm_vk_tty_wq_handler(struct work_struct *work)62{63	struct bcm_vk *vk = container_of(work, struct bcm_vk, tty_wq_work);64	struct bcm_vk_tty *vktty;65	int card_status;66	int count;67	int i;68	int wr;69	u8 c;70 71	card_status = vkread32(vk, BAR_0, BAR_CARD_STATUS);72	if (BCM_VK_INTF_IS_DOWN(card_status))73		return;74 75	for (i = 0; i < BCM_VK_NUM_TTY; i++) {76		count = 0;77		/* Check the card status that the tty channel is ready */78		if ((card_status & BIT(i)) == 0)79			continue;80 81		vktty = &vk->tty[i];82 83		/* Don't increment read index if tty app is closed */84		if (!vktty->is_opened)85			continue;86 87		/* Fetch the wr offset in buffer from VK */88		wr = vkread32(vk, BAR_1, VK_BAR_CHAN_WR(vktty, from));89 90		/* safe to ignore until bar read gives proper size */91		if (vktty->from_size == 0)92			continue;93 94		if (wr >= vktty->from_size) {95			dev_err(&vk->pdev->dev,96				"ERROR: wq handler ttyVK%d wr:0x%x > 0x%x\n",97				i, wr, vktty->from_size);98			/* Need to signal and close device in this case */99			continue;100		}101 102		/*103		 * Simple read of circular buffer and104		 * insert into tty flip buffer105		 */106		while (vk->tty[i].rd != wr) {107			c = vkread8(vk, BAR_1,108				    VK_BAR_CHAN_DATA(vktty, from, vktty->rd));109			vktty->rd++;110			if (vktty->rd >= vktty->from_size)111				vktty->rd = 0;112			tty_insert_flip_char(&vktty->port, c, TTY_NORMAL);113			count++;114		}115 116		if (count) {117			tty_flip_buffer_push(&vktty->port);118 119			/* Update read offset from shadow register to card */120			vkwrite32(vk, vktty->rd, BAR_1,121				  VK_BAR_CHAN_RD(vktty, from));122		}123	}124}125 126static int bcm_vk_tty_open(struct tty_struct *tty, struct file *file)127{128	int card_status;129	struct bcm_vk *vk;130	struct bcm_vk_tty *vktty;131	int index;132 133	/* initialize the pointer in case something fails */134	tty->driver_data = NULL;135 136	vk = (struct bcm_vk *)dev_get_drvdata(tty->dev);137	index = tty->index;138 139	if (index >= BCM_VK_NUM_TTY)140		return -EINVAL;141 142	vktty = &vk->tty[index];143 144	vktty->pid = task_pid_nr(current);145	vktty->to_offset = TO_TTYK_BASE(index);146	vktty->from_offset = FROM_TTYK_BASE(index);147 148	/* Do not allow tty device to be opened if tty on card not ready */149	card_status = vkread32(vk, BAR_0, BAR_CARD_STATUS);150	if (BCM_VK_INTF_IS_DOWN(card_status) || ((card_status & BIT(index)) == 0))151		return -EBUSY;152 153	/*154	 * Get shadow registers of the buffer sizes and the "to" write offset155	 * and "from" read offset156	 */157	vktty->to_size = vkread32(vk, BAR_1, VK_BAR_CHAN_SIZE(vktty, to));158	vktty->wr = vkread32(vk, BAR_1,  VK_BAR_CHAN_WR(vktty, to));159	vktty->from_size = vkread32(vk, BAR_1, VK_BAR_CHAN_SIZE(vktty, from));160	vktty->rd = vkread32(vk, BAR_1,  VK_BAR_CHAN_RD(vktty, from));161	vktty->is_opened = true;162 163	if (tty->count == 1 && !vktty->irq_enabled) {164		timer_setup(&vk->serial_timer, bcm_vk_tty_poll, 0);165		mod_timer(&vk->serial_timer, jiffies + SERIAL_TIMER_VALUE);166	}167	return 0;168}169 170static void bcm_vk_tty_close(struct tty_struct *tty, struct file *file)171{172	struct bcm_vk *vk = dev_get_drvdata(tty->dev);173 174	if (tty->index >= BCM_VK_NUM_TTY)175		return;176 177	vk->tty[tty->index].is_opened = false;178 179	if (tty->count == 1)180		del_timer_sync(&vk->serial_timer);181}182 183static void bcm_vk_tty_doorbell(struct bcm_vk *vk, u32 db_val)184{185	vkwrite32(vk, db_val, BAR_0,186		  VK_BAR0_REGSEG_DB_BASE + VK_BAR0_REGSEG_TTY_DB_OFFSET);187}188 189static ssize_t bcm_vk_tty_write(struct tty_struct *tty, const u8 *buffer,190				size_t count)191{192	int index;193	struct bcm_vk *vk;194	struct bcm_vk_tty *vktty;195	size_t i;196 197	index = tty->index;198	vk = dev_get_drvdata(tty->dev);199	vktty = &vk->tty[index];200 201	/* Simple write each byte to circular buffer */202	for (i = 0; i < count; i++) {203		vkwrite8(vk, buffer[i], BAR_1,204			 VK_BAR_CHAN_DATA(vktty, to, vktty->wr));205		vktty->wr++;206		if (vktty->wr >= vktty->to_size)207			vktty->wr = 0;208	}209	/* Update write offset from shadow register to card */210	vkwrite32(vk, vktty->wr, BAR_1, VK_BAR_CHAN_WR(vktty, to));211	bcm_vk_tty_doorbell(vk, 0);212 213	return count;214}215 216static unsigned int bcm_vk_tty_write_room(struct tty_struct *tty)217{218	struct bcm_vk *vk = dev_get_drvdata(tty->dev);219 220	return vk->tty[tty->index].to_size - 1;221}222 223static const struct tty_operations serial_ops = {224	.open = bcm_vk_tty_open,225	.close = bcm_vk_tty_close,226	.write = bcm_vk_tty_write,227	.write_room = bcm_vk_tty_write_room,228};229 230int bcm_vk_tty_init(struct bcm_vk *vk, char *name)231{232	int i;233	int err;234	struct tty_driver *tty_drv;235	struct device *dev = &vk->pdev->dev;236 237	tty_drv = tty_alloc_driver238				(BCM_VK_NUM_TTY,239				 TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV);240	if (IS_ERR(tty_drv))241		return PTR_ERR(tty_drv);242 243	/* Save struct tty_driver for uninstalling the device */244	vk->tty_drv = tty_drv;245 246	/* initialize the tty driver */247	tty_drv->driver_name = KBUILD_MODNAME;248	tty_drv->name = kstrdup(name, GFP_KERNEL);249	if (!tty_drv->name) {250		err = -ENOMEM;251		goto err_tty_driver_kref_put;252	}253	tty_drv->type = TTY_DRIVER_TYPE_SERIAL;254	tty_drv->subtype = SERIAL_TYPE_NORMAL;255	tty_drv->init_termios = tty_std_termios;256	tty_set_operations(tty_drv, &serial_ops);257 258	/* register the tty driver */259	err = tty_register_driver(tty_drv);260	if (err) {261		dev_err(dev, "tty_register_driver failed\n");262		goto err_kfree_tty_name;263	}264 265	for (i = 0; i < BCM_VK_NUM_TTY; i++) {266		struct device *tty_dev;267 268		tty_port_init(&vk->tty[i].port);269		tty_dev = tty_port_register_device_attr(&vk->tty[i].port,270							tty_drv, i, dev, vk,271							NULL);272		if (IS_ERR(tty_dev)) {273			err = PTR_ERR(tty_dev);274			goto unwind;275		}276		vk->tty[i].is_opened = false;277	}278 279	INIT_WORK(&vk->tty_wq_work, bcm_vk_tty_wq_handler);280	vk->tty_wq_thread = create_singlethread_workqueue("tty");281	if (!vk->tty_wq_thread) {282		dev_err(dev, "Fail to create tty workqueue thread\n");283		err = -ENOMEM;284		goto unwind;285	}286	return 0;287 288unwind:289	while (--i >= 0)290		tty_port_unregister_device(&vk->tty[i].port, tty_drv, i);291	tty_unregister_driver(tty_drv);292 293err_kfree_tty_name:294	kfree(tty_drv->name);295	tty_drv->name = NULL;296 297err_tty_driver_kref_put:298	tty_driver_kref_put(tty_drv);299 300	return err;301}302 303void bcm_vk_tty_exit(struct bcm_vk *vk)304{305	int i;306 307	del_timer_sync(&vk->serial_timer);308	for (i = 0; i < BCM_VK_NUM_TTY; ++i) {309		tty_port_unregister_device(&vk->tty[i].port,310					   vk->tty_drv,311					   i);312		tty_port_destroy(&vk->tty[i].port);313	}314	tty_unregister_driver(vk->tty_drv);315 316	kfree(vk->tty_drv->name);317	vk->tty_drv->name = NULL;318 319	tty_driver_kref_put(vk->tty_drv);320}321 322void bcm_vk_tty_terminate_tty_user(struct bcm_vk *vk)323{324	struct bcm_vk_tty *vktty;325	int i;326 327	for (i = 0; i < BCM_VK_NUM_TTY; ++i) {328		vktty = &vk->tty[i];329		if (vktty->pid)330			kill_pid(find_vpid(vktty->pid), SIGKILL, 1);331	}332}333 334void bcm_vk_tty_wq_exit(struct bcm_vk *vk)335{336	cancel_work_sync(&vk->tty_wq_work);337	destroy_workqueue(vk->tty_wq_thread);338}339