0001
0002
0003
0004
0005
0006 #include <linux/tty.h>
0007 #include <linux/tty_driver.h>
0008 #include <linux/tty_flip.h>
0009
0010 #include "bcm_vk.h"
0011
0012
0013 #define BAR1_TTYVK_BASE_OFFSET 0x300000
0014
0015 #define BAR1_TTYVK_CHAN_OFFSET 0x100000
0016
0017 #define BAR1_TTYVK_BASE(index) (BAR1_TTYVK_BASE_OFFSET + \
0018 ((index) * BAR1_TTYVK_CHAN_OFFSET * 2))
0019
0020 #define TO_TTYK_BASE(index) BAR1_TTYVK_BASE(index)
0021 #define FROM_TTYK_BASE(index) (BAR1_TTYVK_BASE(index) + \
0022 BAR1_TTYVK_CHAN_OFFSET)
0023
0024 struct bcm_vk_tty_chan {
0025 u32 reserved;
0026 u32 size;
0027 u32 wr;
0028 u32 rd;
0029 u32 *data;
0030 };
0031
0032 #define VK_BAR_CHAN(v, DIR, e) ((v)->DIR##_offset \
0033 + offsetof(struct bcm_vk_tty_chan, e))
0034 #define VK_BAR_CHAN_SIZE(v, DIR) VK_BAR_CHAN(v, DIR, size)
0035 #define VK_BAR_CHAN_WR(v, DIR) VK_BAR_CHAN(v, DIR, wr)
0036 #define VK_BAR_CHAN_RD(v, DIR) VK_BAR_CHAN(v, DIR, rd)
0037 #define VK_BAR_CHAN_DATA(v, DIR, off) (VK_BAR_CHAN(v, DIR, data) + (off))
0038
0039 #define VK_BAR0_REGSEG_TTY_DB_OFFSET 0x86c
0040
0041
0042 #define SERIAL_TIMER_VALUE (HZ / 10)
0043
0044 static void bcm_vk_tty_poll(struct timer_list *t)
0045 {
0046 struct bcm_vk *vk = from_timer(vk, t, serial_timer);
0047
0048 queue_work(vk->tty_wq_thread, &vk->tty_wq_work);
0049 mod_timer(&vk->serial_timer, jiffies + SERIAL_TIMER_VALUE);
0050 }
0051
0052 irqreturn_t bcm_vk_tty_irqhandler(int irq, void *dev_id)
0053 {
0054 struct bcm_vk *vk = dev_id;
0055
0056 queue_work(vk->tty_wq_thread, &vk->tty_wq_work);
0057
0058 return IRQ_HANDLED;
0059 }
0060
0061 static void bcm_vk_tty_wq_handler(struct work_struct *work)
0062 {
0063 struct bcm_vk *vk = container_of(work, struct bcm_vk, tty_wq_work);
0064 struct bcm_vk_tty *vktty;
0065 int card_status;
0066 int count;
0067 unsigned char c;
0068 int i;
0069 int wr;
0070
0071 card_status = vkread32(vk, BAR_0, BAR_CARD_STATUS);
0072 if (BCM_VK_INTF_IS_DOWN(card_status))
0073 return;
0074
0075 for (i = 0; i < BCM_VK_NUM_TTY; i++) {
0076 count = 0;
0077
0078 if ((card_status & BIT(i)) == 0)
0079 continue;
0080
0081 vktty = &vk->tty[i];
0082
0083
0084 if (!vktty->is_opened)
0085 continue;
0086
0087
0088 wr = vkread32(vk, BAR_1, VK_BAR_CHAN_WR(vktty, from));
0089
0090
0091 if (vktty->from_size == 0)
0092 continue;
0093
0094 if (wr >= vktty->from_size) {
0095 dev_err(&vk->pdev->dev,
0096 "ERROR: wq handler ttyVK%d wr:0x%x > 0x%x\n",
0097 i, wr, vktty->from_size);
0098
0099 continue;
0100 }
0101
0102
0103
0104
0105
0106 while (vk->tty[i].rd != wr) {
0107 c = vkread8(vk, BAR_1,
0108 VK_BAR_CHAN_DATA(vktty, from, vktty->rd));
0109 vktty->rd++;
0110 if (vktty->rd >= vktty->from_size)
0111 vktty->rd = 0;
0112 tty_insert_flip_char(&vktty->port, c, TTY_NORMAL);
0113 count++;
0114 }
0115
0116 if (count) {
0117 tty_flip_buffer_push(&vktty->port);
0118
0119
0120 vkwrite32(vk, vktty->rd, BAR_1,
0121 VK_BAR_CHAN_RD(vktty, from));
0122 }
0123 }
0124 }
0125
0126 static int bcm_vk_tty_open(struct tty_struct *tty, struct file *file)
0127 {
0128 int card_status;
0129 struct bcm_vk *vk;
0130 struct bcm_vk_tty *vktty;
0131 int index;
0132
0133
0134 tty->driver_data = NULL;
0135
0136 vk = (struct bcm_vk *)dev_get_drvdata(tty->dev);
0137 index = tty->index;
0138
0139 if (index >= BCM_VK_NUM_TTY)
0140 return -EINVAL;
0141
0142 vktty = &vk->tty[index];
0143
0144 vktty->pid = task_pid_nr(current);
0145 vktty->to_offset = TO_TTYK_BASE(index);
0146 vktty->from_offset = FROM_TTYK_BASE(index);
0147
0148
0149 card_status = vkread32(vk, BAR_0, BAR_CARD_STATUS);
0150 if (BCM_VK_INTF_IS_DOWN(card_status) || ((card_status & BIT(index)) == 0))
0151 return -EBUSY;
0152
0153
0154
0155
0156
0157 vktty->to_size = vkread32(vk, BAR_1, VK_BAR_CHAN_SIZE(vktty, to));
0158 vktty->wr = vkread32(vk, BAR_1, VK_BAR_CHAN_WR(vktty, to));
0159 vktty->from_size = vkread32(vk, BAR_1, VK_BAR_CHAN_SIZE(vktty, from));
0160 vktty->rd = vkread32(vk, BAR_1, VK_BAR_CHAN_RD(vktty, from));
0161 vktty->is_opened = true;
0162
0163 if (tty->count == 1 && !vktty->irq_enabled) {
0164 timer_setup(&vk->serial_timer, bcm_vk_tty_poll, 0);
0165 mod_timer(&vk->serial_timer, jiffies + SERIAL_TIMER_VALUE);
0166 }
0167 return 0;
0168 }
0169
0170 static void bcm_vk_tty_close(struct tty_struct *tty, struct file *file)
0171 {
0172 struct bcm_vk *vk = dev_get_drvdata(tty->dev);
0173
0174 if (tty->index >= BCM_VK_NUM_TTY)
0175 return;
0176
0177 vk->tty[tty->index].is_opened = false;
0178
0179 if (tty->count == 1)
0180 del_timer_sync(&vk->serial_timer);
0181 }
0182
0183 static void bcm_vk_tty_doorbell(struct bcm_vk *vk, u32 db_val)
0184 {
0185 vkwrite32(vk, db_val, BAR_0,
0186 VK_BAR0_REGSEG_DB_BASE + VK_BAR0_REGSEG_TTY_DB_OFFSET);
0187 }
0188
0189 static int bcm_vk_tty_write(struct tty_struct *tty,
0190 const unsigned char *buffer,
0191 int count)
0192 {
0193 int index;
0194 struct bcm_vk *vk;
0195 struct bcm_vk_tty *vktty;
0196 int i;
0197
0198 index = tty->index;
0199 vk = dev_get_drvdata(tty->dev);
0200 vktty = &vk->tty[index];
0201
0202
0203 for (i = 0; i < count; i++) {
0204 vkwrite8(vk, buffer[i], BAR_1,
0205 VK_BAR_CHAN_DATA(vktty, to, vktty->wr));
0206 vktty->wr++;
0207 if (vktty->wr >= vktty->to_size)
0208 vktty->wr = 0;
0209 }
0210
0211 vkwrite32(vk, vktty->wr, BAR_1, VK_BAR_CHAN_WR(vktty, to));
0212 bcm_vk_tty_doorbell(vk, 0);
0213
0214 return count;
0215 }
0216
0217 static unsigned int bcm_vk_tty_write_room(struct tty_struct *tty)
0218 {
0219 struct bcm_vk *vk = dev_get_drvdata(tty->dev);
0220
0221 return vk->tty[tty->index].to_size - 1;
0222 }
0223
0224 static const struct tty_operations serial_ops = {
0225 .open = bcm_vk_tty_open,
0226 .close = bcm_vk_tty_close,
0227 .write = bcm_vk_tty_write,
0228 .write_room = bcm_vk_tty_write_room,
0229 };
0230
0231 int bcm_vk_tty_init(struct bcm_vk *vk, char *name)
0232 {
0233 int i;
0234 int err;
0235 struct tty_driver *tty_drv;
0236 struct device *dev = &vk->pdev->dev;
0237
0238 tty_drv = tty_alloc_driver
0239 (BCM_VK_NUM_TTY,
0240 TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV);
0241 if (IS_ERR(tty_drv))
0242 return PTR_ERR(tty_drv);
0243
0244
0245 vk->tty_drv = tty_drv;
0246
0247
0248 tty_drv->driver_name = KBUILD_MODNAME;
0249 tty_drv->name = kstrdup(name, GFP_KERNEL);
0250 if (!tty_drv->name) {
0251 err = -ENOMEM;
0252 goto err_tty_driver_kref_put;
0253 }
0254 tty_drv->type = TTY_DRIVER_TYPE_SERIAL;
0255 tty_drv->subtype = SERIAL_TYPE_NORMAL;
0256 tty_drv->init_termios = tty_std_termios;
0257 tty_set_operations(tty_drv, &serial_ops);
0258
0259
0260 err = tty_register_driver(tty_drv);
0261 if (err) {
0262 dev_err(dev, "tty_register_driver failed\n");
0263 goto err_kfree_tty_name;
0264 }
0265
0266 for (i = 0; i < BCM_VK_NUM_TTY; i++) {
0267 struct device *tty_dev;
0268
0269 tty_port_init(&vk->tty[i].port);
0270 tty_dev = tty_port_register_device_attr(&vk->tty[i].port,
0271 tty_drv, i, dev, vk,
0272 NULL);
0273 if (IS_ERR(tty_dev)) {
0274 err = PTR_ERR(tty_dev);
0275 goto unwind;
0276 }
0277 vk->tty[i].is_opened = false;
0278 }
0279
0280 INIT_WORK(&vk->tty_wq_work, bcm_vk_tty_wq_handler);
0281 vk->tty_wq_thread = create_singlethread_workqueue("tty");
0282 if (!vk->tty_wq_thread) {
0283 dev_err(dev, "Fail to create tty workqueue thread\n");
0284 err = -ENOMEM;
0285 goto unwind;
0286 }
0287 return 0;
0288
0289 unwind:
0290 while (--i >= 0)
0291 tty_port_unregister_device(&vk->tty[i].port, tty_drv, i);
0292 tty_unregister_driver(tty_drv);
0293
0294 err_kfree_tty_name:
0295 kfree(tty_drv->name);
0296 tty_drv->name = NULL;
0297
0298 err_tty_driver_kref_put:
0299 tty_driver_kref_put(tty_drv);
0300
0301 return err;
0302 }
0303
0304 void bcm_vk_tty_exit(struct bcm_vk *vk)
0305 {
0306 int i;
0307
0308 del_timer_sync(&vk->serial_timer);
0309 for (i = 0; i < BCM_VK_NUM_TTY; ++i) {
0310 tty_port_unregister_device(&vk->tty[i].port,
0311 vk->tty_drv,
0312 i);
0313 tty_port_destroy(&vk->tty[i].port);
0314 }
0315 tty_unregister_driver(vk->tty_drv);
0316
0317 kfree(vk->tty_drv->name);
0318 vk->tty_drv->name = NULL;
0319
0320 tty_driver_kref_put(vk->tty_drv);
0321 }
0322
0323 void bcm_vk_tty_terminate_tty_user(struct bcm_vk *vk)
0324 {
0325 struct bcm_vk_tty *vktty;
0326 int i;
0327
0328 for (i = 0; i < BCM_VK_NUM_TTY; ++i) {
0329 vktty = &vk->tty[i];
0330 if (vktty->pid)
0331 kill_pid(find_vpid(vktty->pid), SIGKILL, 1);
0332 }
0333 }
0334
0335 void bcm_vk_tty_wq_exit(struct bcm_vk *vk)
0336 {
0337 cancel_work_sync(&vk->tty_wq_work);
0338 destroy_workqueue(vk->tty_wq_thread);
0339 }