669 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Driver for Feature Integration Technology Inc. (aka Fintek) LPC CIR4 *5 * Copyright (C) 2011 Jarod Wilson <jarod@redhat.com>6 *7 * Special thanks to Fintek for providing hardware and spec sheets.8 * This driver is based upon the nuvoton, ite and ene drivers for9 * similar hardware.10 */11 12#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt13 14#include <linux/kernel.h>15#include <linux/module.h>16#include <linux/pnp.h>17#include <linux/io.h>18#include <linux/interrupt.h>19#include <linux/sched.h>20#include <linux/slab.h>21#include <media/rc-core.h>22 23#include "fintek-cir.h"24 25/* write val to config reg */26static inline void fintek_cr_write(struct fintek_dev *fintek, u8 val, u8 reg)27{28 fit_dbg("%s: reg 0x%02x, val 0x%02x (ip/dp: %02x/%02x)",29 __func__, reg, val, fintek->cr_ip, fintek->cr_dp);30 outb(reg, fintek->cr_ip);31 outb(val, fintek->cr_dp);32}33 34/* read val from config reg */35static inline u8 fintek_cr_read(struct fintek_dev *fintek, u8 reg)36{37 u8 val;38 39 outb(reg, fintek->cr_ip);40 val = inb(fintek->cr_dp);41 42 fit_dbg("%s: reg 0x%02x, val 0x%02x (ip/dp: %02x/%02x)",43 __func__, reg, val, fintek->cr_ip, fintek->cr_dp);44 return val;45}46 47/* update config register bit without changing other bits */48static inline void fintek_set_reg_bit(struct fintek_dev *fintek, u8 val, u8 reg)49{50 u8 tmp = fintek_cr_read(fintek, reg) | val;51 fintek_cr_write(fintek, tmp, reg);52}53 54/* enter config mode */55static inline void fintek_config_mode_enable(struct fintek_dev *fintek)56{57 /* Enabling Config Mode explicitly requires writing 2x */58 outb(CONFIG_REG_ENABLE, fintek->cr_ip);59 outb(CONFIG_REG_ENABLE, fintek->cr_ip);60}61 62/* exit config mode */63static inline void fintek_config_mode_disable(struct fintek_dev *fintek)64{65 outb(CONFIG_REG_DISABLE, fintek->cr_ip);66}67 68/*69 * When you want to address a specific logical device, write its logical70 * device number to GCR_LOGICAL_DEV_NO71 */72static inline void fintek_select_logical_dev(struct fintek_dev *fintek, u8 ldev)73{74 fintek_cr_write(fintek, ldev, GCR_LOGICAL_DEV_NO);75}76 77/* write val to cir config register */78static inline void fintek_cir_reg_write(struct fintek_dev *fintek, u8 val, u8 offset)79{80 outb(val, fintek->cir_addr + offset);81}82 83/* read val from cir config register */84static u8 fintek_cir_reg_read(struct fintek_dev *fintek, u8 offset)85{86 return inb(fintek->cir_addr + offset);87}88 89/* dump current cir register contents */90static void cir_dump_regs(struct fintek_dev *fintek)91{92 fintek_config_mode_enable(fintek);93 fintek_select_logical_dev(fintek, fintek->logical_dev_cir);94 95 pr_info("%s: Dump CIR logical device registers:\n", FINTEK_DRIVER_NAME);96 pr_info(" * CR CIR BASE ADDR: 0x%x\n",97 (fintek_cr_read(fintek, CIR_CR_BASE_ADDR_HI) << 8) |98 fintek_cr_read(fintek, CIR_CR_BASE_ADDR_LO));99 pr_info(" * CR CIR IRQ NUM: 0x%x\n",100 fintek_cr_read(fintek, CIR_CR_IRQ_SEL));101 102 fintek_config_mode_disable(fintek);103 104 pr_info("%s: Dump CIR registers:\n", FINTEK_DRIVER_NAME);105 pr_info(" * STATUS: 0x%x\n",106 fintek_cir_reg_read(fintek, CIR_STATUS));107 pr_info(" * CONTROL: 0x%x\n",108 fintek_cir_reg_read(fintek, CIR_CONTROL));109 pr_info(" * RX_DATA: 0x%x\n",110 fintek_cir_reg_read(fintek, CIR_RX_DATA));111 pr_info(" * TX_CONTROL: 0x%x\n",112 fintek_cir_reg_read(fintek, CIR_TX_CONTROL));113 pr_info(" * TX_DATA: 0x%x\n",114 fintek_cir_reg_read(fintek, CIR_TX_DATA));115}116 117/* detect hardware features */118static int fintek_hw_detect(struct fintek_dev *fintek)119{120 unsigned long flags;121 u8 chip_major, chip_minor;122 u8 vendor_major, vendor_minor;123 u8 portsel, ir_class;124 u16 vendor, chip;125 126 fintek_config_mode_enable(fintek);127 128 /* Check if we're using config port 0x4e or 0x2e */129 portsel = fintek_cr_read(fintek, GCR_CONFIG_PORT_SEL);130 if (portsel == 0xff) {131 fit_pr(KERN_INFO, "first portsel read was bunk, trying alt");132 fintek_config_mode_disable(fintek);133 fintek->cr_ip = CR_INDEX_PORT2;134 fintek->cr_dp = CR_DATA_PORT2;135 fintek_config_mode_enable(fintek);136 portsel = fintek_cr_read(fintek, GCR_CONFIG_PORT_SEL);137 }138 fit_dbg("portsel reg: 0x%02x", portsel);139 140 ir_class = fintek_cir_reg_read(fintek, CIR_CR_CLASS);141 fit_dbg("ir_class reg: 0x%02x", ir_class);142 143 switch (ir_class) {144 case CLASS_RX_2TX:145 case CLASS_RX_1TX:146 fintek->hw_tx_capable = true;147 break;148 case CLASS_RX_ONLY:149 default:150 fintek->hw_tx_capable = false;151 break;152 }153 154 chip_major = fintek_cr_read(fintek, GCR_CHIP_ID_HI);155 chip_minor = fintek_cr_read(fintek, GCR_CHIP_ID_LO);156 chip = chip_major << 8 | chip_minor;157 158 vendor_major = fintek_cr_read(fintek, GCR_VENDOR_ID_HI);159 vendor_minor = fintek_cr_read(fintek, GCR_VENDOR_ID_LO);160 vendor = vendor_major << 8 | vendor_minor;161 162 if (vendor != VENDOR_ID_FINTEK)163 fit_pr(KERN_WARNING, "Unknown vendor ID: 0x%04x", vendor);164 else165 fit_dbg("Read Fintek vendor ID from chip");166 167 fintek_config_mode_disable(fintek);168 169 spin_lock_irqsave(&fintek->fintek_lock, flags);170 fintek->chip_major = chip_major;171 fintek->chip_minor = chip_minor;172 fintek->chip_vendor = vendor;173 174 /*175 * Newer reviews of this chipset uses port 8 instead of 5176 */177 if ((chip != 0x0408) && (chip != 0x0804))178 fintek->logical_dev_cir = LOGICAL_DEV_CIR_REV2;179 else180 fintek->logical_dev_cir = LOGICAL_DEV_CIR_REV1;181 182 spin_unlock_irqrestore(&fintek->fintek_lock, flags);183 184 return 0;185}186 187static void fintek_cir_ldev_init(struct fintek_dev *fintek)188{189 /* Select CIR logical device and enable */190 fintek_select_logical_dev(fintek, fintek->logical_dev_cir);191 fintek_cr_write(fintek, LOGICAL_DEV_ENABLE, CIR_CR_DEV_EN);192 193 /* Write allocated CIR address and IRQ information to hardware */194 fintek_cr_write(fintek, fintek->cir_addr >> 8, CIR_CR_BASE_ADDR_HI);195 fintek_cr_write(fintek, fintek->cir_addr & 0xff, CIR_CR_BASE_ADDR_LO);196 197 fintek_cr_write(fintek, fintek->cir_irq, CIR_CR_IRQ_SEL);198 199 fit_dbg("CIR initialized, base io address: 0x%lx, irq: %d (len: %d)",200 fintek->cir_addr, fintek->cir_irq, fintek->cir_port_len);201}202 203/* enable CIR interrupts */204static void fintek_enable_cir_irq(struct fintek_dev *fintek)205{206 fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_EN, CIR_STATUS);207}208 209static void fintek_cir_regs_init(struct fintek_dev *fintek)210{211 /* clear any and all stray interrupts */212 fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS);213 214 /* and finally, enable interrupts */215 fintek_enable_cir_irq(fintek);216}217 218static void fintek_enable_wake(struct fintek_dev *fintek)219{220 fintek_config_mode_enable(fintek);221 fintek_select_logical_dev(fintek, LOGICAL_DEV_ACPI);222 223 /* Allow CIR PME's to wake system */224 fintek_set_reg_bit(fintek, ACPI_WAKE_EN_CIR_BIT, LDEV_ACPI_WAKE_EN_REG);225 /* Enable CIR PME's */226 fintek_set_reg_bit(fintek, ACPI_PME_CIR_BIT, LDEV_ACPI_PME_EN_REG);227 /* Clear CIR PME status register */228 fintek_set_reg_bit(fintek, ACPI_PME_CIR_BIT, LDEV_ACPI_PME_CLR_REG);229 /* Save state */230 fintek_set_reg_bit(fintek, ACPI_STATE_CIR_BIT, LDEV_ACPI_STATE_REG);231 232 fintek_config_mode_disable(fintek);233}234 235static int fintek_cmdsize(u8 cmd, u8 subcmd)236{237 int datasize = 0;238 239 switch (cmd) {240 case BUF_COMMAND_NULL:241 if (subcmd == BUF_HW_CMD_HEADER)242 datasize = 1;243 break;244 case BUF_HW_CMD_HEADER:245 if (subcmd == BUF_CMD_G_REVISION)246 datasize = 2;247 break;248 case BUF_COMMAND_HEADER:249 switch (subcmd) {250 case BUF_CMD_S_CARRIER:251 case BUF_CMD_S_TIMEOUT:252 case BUF_RSP_PULSE_COUNT:253 datasize = 2;254 break;255 case BUF_CMD_SIG_END:256 case BUF_CMD_S_TXMASK:257 case BUF_CMD_S_RXSENSOR:258 datasize = 1;259 break;260 }261 }262 263 return datasize;264}265 266/* process ir data stored in driver buffer */267static void fintek_process_rx_ir_data(struct fintek_dev *fintek)268{269 struct ir_raw_event rawir = {};270 u8 sample;271 bool event = false;272 int i;273 274 for (i = 0; i < fintek->pkts; i++) {275 sample = fintek->buf[i];276 switch (fintek->parser_state) {277 case CMD_HEADER:278 fintek->cmd = sample;279 if ((fintek->cmd == BUF_COMMAND_HEADER) ||280 ((fintek->cmd & BUF_COMMAND_MASK) !=281 BUF_PULSE_BIT)) {282 fintek->parser_state = SUBCMD;283 continue;284 }285 fintek->rem = (fintek->cmd & BUF_LEN_MASK);286 fit_dbg("%s: rem: 0x%02x", __func__, fintek->rem);287 if (fintek->rem)288 fintek->parser_state = PARSE_IRDATA;289 else290 ir_raw_event_overflow(fintek->rdev);291 break;292 case SUBCMD:293 fintek->rem = fintek_cmdsize(fintek->cmd, sample);294 fintek->parser_state = CMD_DATA;295 break;296 case CMD_DATA:297 fintek->rem--;298 break;299 case PARSE_IRDATA:300 fintek->rem--;301 rawir.pulse = ((sample & BUF_PULSE_BIT) != 0);302 rawir.duration = (sample & BUF_SAMPLE_MASK)303 * CIR_SAMPLE_PERIOD;304 305 fit_dbg("Storing %s with duration %d",306 rawir.pulse ? "pulse" : "space",307 rawir.duration);308 if (ir_raw_event_store_with_filter(fintek->rdev,309 &rawir))310 event = true;311 break;312 }313 314 if ((fintek->parser_state != CMD_HEADER) && !fintek->rem)315 fintek->parser_state = CMD_HEADER;316 }317 318 fintek->pkts = 0;319 320 if (event) {321 fit_dbg("Calling ir_raw_event_handle");322 ir_raw_event_handle(fintek->rdev);323 }324}325 326/* copy data from hardware rx register into driver buffer */327static void fintek_get_rx_ir_data(struct fintek_dev *fintek, u8 rx_irqs)328{329 unsigned long flags;330 u8 sample, status;331 332 spin_lock_irqsave(&fintek->fintek_lock, flags);333 334 /*335 * We must read data from CIR_RX_DATA until the hardware IR buffer336 * is empty and clears the RX_TIMEOUT and/or RX_RECEIVE flags in337 * the CIR_STATUS register338 */339 do {340 sample = fintek_cir_reg_read(fintek, CIR_RX_DATA);341 fit_dbg("%s: sample: 0x%02x", __func__, sample);342 343 fintek->buf[fintek->pkts] = sample;344 fintek->pkts++;345 346 status = fintek_cir_reg_read(fintek, CIR_STATUS);347 if (!(status & CIR_STATUS_IRQ_EN))348 break;349 } while (status & rx_irqs);350 351 fintek_process_rx_ir_data(fintek);352 353 spin_unlock_irqrestore(&fintek->fintek_lock, flags);354}355 356static void fintek_cir_log_irqs(u8 status)357{358 fit_pr(KERN_INFO, "IRQ 0x%02x:%s%s%s%s%s", status,359 status & CIR_STATUS_IRQ_EN ? " IRQEN" : "",360 status & CIR_STATUS_TX_FINISH ? " TXF" : "",361 status & CIR_STATUS_TX_UNDERRUN ? " TXU" : "",362 status & CIR_STATUS_RX_TIMEOUT ? " RXTO" : "",363 status & CIR_STATUS_RX_RECEIVE ? " RXOK" : "");364}365 366/* interrupt service routine for incoming and outgoing CIR data */367static irqreturn_t fintek_cir_isr(int irq, void *data)368{369 struct fintek_dev *fintek = data;370 u8 status, rx_irqs;371 372 fit_dbg_verbose("%s firing", __func__);373 374 fintek_config_mode_enable(fintek);375 fintek_select_logical_dev(fintek, fintek->logical_dev_cir);376 fintek_config_mode_disable(fintek);377 378 /*379 * Get IR Status register contents. Write 1 to ack/clear380 *381 * bit: reg name - description382 * 3: TX_FINISH - TX is finished383 * 2: TX_UNDERRUN - TX underrun384 * 1: RX_TIMEOUT - RX data timeout385 * 0: RX_RECEIVE - RX data received386 */387 status = fintek_cir_reg_read(fintek, CIR_STATUS);388 if (!(status & CIR_STATUS_IRQ_MASK) || status == 0xff) {389 fit_dbg_verbose("%s exiting, IRSTS 0x%02x", __func__, status);390 fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS);391 return IRQ_RETVAL(IRQ_NONE);392 }393 394 if (debug)395 fintek_cir_log_irqs(status);396 397 rx_irqs = status & (CIR_STATUS_RX_RECEIVE | CIR_STATUS_RX_TIMEOUT);398 if (rx_irqs)399 fintek_get_rx_ir_data(fintek, rx_irqs);400 401 /* ack/clear all irq flags we've got */402 fintek_cir_reg_write(fintek, status, CIR_STATUS);403 404 fit_dbg_verbose("%s done", __func__);405 return IRQ_RETVAL(IRQ_HANDLED);406}407 408static void fintek_enable_cir(struct fintek_dev *fintek)409{410 /* set IRQ enabled */411 fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_EN, CIR_STATUS);412 413 fintek_config_mode_enable(fintek);414 415 /* enable the CIR logical device */416 fintek_select_logical_dev(fintek, fintek->logical_dev_cir);417 fintek_cr_write(fintek, LOGICAL_DEV_ENABLE, CIR_CR_DEV_EN);418 419 fintek_config_mode_disable(fintek);420 421 /* clear all pending interrupts */422 fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS);423 424 /* enable interrupts */425 fintek_enable_cir_irq(fintek);426}427 428static void fintek_disable_cir(struct fintek_dev *fintek)429{430 fintek_config_mode_enable(fintek);431 432 /* disable the CIR logical device */433 fintek_select_logical_dev(fintek, fintek->logical_dev_cir);434 fintek_cr_write(fintek, LOGICAL_DEV_DISABLE, CIR_CR_DEV_EN);435 436 fintek_config_mode_disable(fintek);437}438 439static int fintek_open(struct rc_dev *dev)440{441 struct fintek_dev *fintek = dev->priv;442 unsigned long flags;443 444 spin_lock_irqsave(&fintek->fintek_lock, flags);445 fintek_enable_cir(fintek);446 spin_unlock_irqrestore(&fintek->fintek_lock, flags);447 448 return 0;449}450 451static void fintek_close(struct rc_dev *dev)452{453 struct fintek_dev *fintek = dev->priv;454 unsigned long flags;455 456 spin_lock_irqsave(&fintek->fintek_lock, flags);457 fintek_disable_cir(fintek);458 spin_unlock_irqrestore(&fintek->fintek_lock, flags);459}460 461/* Allocate memory, probe hardware, and initialize everything */462static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)463{464 struct fintek_dev *fintek;465 struct rc_dev *rdev;466 int ret = -ENOMEM;467 468 fintek = kzalloc(sizeof(struct fintek_dev), GFP_KERNEL);469 if (!fintek)470 return ret;471 472 /* input device for IR remote (and tx) */473 rdev = rc_allocate_device(RC_DRIVER_IR_RAW);474 if (!rdev)475 goto exit_free_dev_rdev;476 477 ret = -ENODEV;478 /* validate pnp resources */479 if (!pnp_port_valid(pdev, 0)) {480 dev_err(&pdev->dev, "IR PNP Port not valid!\n");481 goto exit_free_dev_rdev;482 }483 484 if (!pnp_irq_valid(pdev, 0)) {485 dev_err(&pdev->dev, "IR PNP IRQ not valid!\n");486 goto exit_free_dev_rdev;487 }488 489 fintek->cir_addr = pnp_port_start(pdev, 0);490 fintek->cir_irq = pnp_irq(pdev, 0);491 fintek->cir_port_len = pnp_port_len(pdev, 0);492 493 fintek->cr_ip = CR_INDEX_PORT;494 fintek->cr_dp = CR_DATA_PORT;495 496 spin_lock_init(&fintek->fintek_lock);497 498 pnp_set_drvdata(pdev, fintek);499 fintek->pdev = pdev;500 501 ret = fintek_hw_detect(fintek);502 if (ret)503 goto exit_free_dev_rdev;504 505 /* Initialize CIR & CIR Wake Logical Devices */506 fintek_config_mode_enable(fintek);507 fintek_cir_ldev_init(fintek);508 fintek_config_mode_disable(fintek);509 510 /* Initialize CIR & CIR Wake Config Registers */511 fintek_cir_regs_init(fintek);512 513 /* Set up the rc device */514 rdev->priv = fintek;515 rdev->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER;516 rdev->open = fintek_open;517 rdev->close = fintek_close;518 rdev->device_name = FINTEK_DESCRIPTION;519 rdev->input_phys = "fintek/cir0";520 rdev->input_id.bustype = BUS_HOST;521 rdev->input_id.vendor = VENDOR_ID_FINTEK;522 rdev->input_id.product = fintek->chip_major;523 rdev->input_id.version = fintek->chip_minor;524 rdev->dev.parent = &pdev->dev;525 rdev->driver_name = FINTEK_DRIVER_NAME;526 rdev->map_name = RC_MAP_RC6_MCE;527 rdev->timeout = 1000;528 /* rx resolution is hardwired to 50us atm, 1, 25, 100 also possible */529 rdev->rx_resolution = CIR_SAMPLE_PERIOD;530 531 fintek->rdev = rdev;532 533 ret = -EBUSY;534 /* now claim resources */535 if (!request_region(fintek->cir_addr,536 fintek->cir_port_len, FINTEK_DRIVER_NAME))537 goto exit_free_dev_rdev;538 539 if (request_irq(fintek->cir_irq, fintek_cir_isr, IRQF_SHARED,540 FINTEK_DRIVER_NAME, (void *)fintek))541 goto exit_free_cir_addr;542 543 ret = rc_register_device(rdev);544 if (ret)545 goto exit_free_irq;546 547 device_init_wakeup(&pdev->dev, true);548 549 fit_pr(KERN_NOTICE, "driver has been successfully loaded\n");550 if (debug)551 cir_dump_regs(fintek);552 553 return 0;554 555exit_free_irq:556 free_irq(fintek->cir_irq, fintek);557exit_free_cir_addr:558 release_region(fintek->cir_addr, fintek->cir_port_len);559exit_free_dev_rdev:560 rc_free_device(rdev);561 kfree(fintek);562 563 return ret;564}565 566static void fintek_remove(struct pnp_dev *pdev)567{568 struct fintek_dev *fintek = pnp_get_drvdata(pdev);569 unsigned long flags;570 571 spin_lock_irqsave(&fintek->fintek_lock, flags);572 /* disable CIR */573 fintek_disable_cir(fintek);574 fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS);575 /* enable CIR Wake (for IR power-on) */576 fintek_enable_wake(fintek);577 spin_unlock_irqrestore(&fintek->fintek_lock, flags);578 579 /* free resources */580 free_irq(fintek->cir_irq, fintek);581 release_region(fintek->cir_addr, fintek->cir_port_len);582 583 rc_unregister_device(fintek->rdev);584 585 kfree(fintek);586}587 588static int fintek_suspend(struct pnp_dev *pdev, pm_message_t state)589{590 struct fintek_dev *fintek = pnp_get_drvdata(pdev);591 unsigned long flags;592 593 fit_dbg("%s called", __func__);594 595 spin_lock_irqsave(&fintek->fintek_lock, flags);596 597 /* disable all CIR interrupts */598 fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS);599 600 spin_unlock_irqrestore(&fintek->fintek_lock, flags);601 602 fintek_config_mode_enable(fintek);603 604 /* disable cir logical dev */605 fintek_select_logical_dev(fintek, fintek->logical_dev_cir);606 fintek_cr_write(fintek, LOGICAL_DEV_DISABLE, CIR_CR_DEV_EN);607 608 fintek_config_mode_disable(fintek);609 610 /* make sure wake is enabled */611 fintek_enable_wake(fintek);612 613 return 0;614}615 616static int fintek_resume(struct pnp_dev *pdev)617{618 struct fintek_dev *fintek = pnp_get_drvdata(pdev);619 620 fit_dbg("%s called", __func__);621 622 /* open interrupt */623 fintek_enable_cir_irq(fintek);624 625 /* Enable CIR logical device */626 fintek_config_mode_enable(fintek);627 fintek_select_logical_dev(fintek, fintek->logical_dev_cir);628 fintek_cr_write(fintek, LOGICAL_DEV_ENABLE, CIR_CR_DEV_EN);629 630 fintek_config_mode_disable(fintek);631 632 fintek_cir_regs_init(fintek);633 634 return 0;635}636 637static void fintek_shutdown(struct pnp_dev *pdev)638{639 struct fintek_dev *fintek = pnp_get_drvdata(pdev);640 fintek_enable_wake(fintek);641}642 643static const struct pnp_device_id fintek_ids[] = {644 { "FIT0002", 0 }, /* CIR */645 { "", 0 },646};647 648static struct pnp_driver fintek_driver = {649 .name = FINTEK_DRIVER_NAME,650 .id_table = fintek_ids,651 .flags = PNP_DRIVER_RES_DO_NOT_CHANGE,652 .probe = fintek_probe,653 .remove = fintek_remove,654 .suspend = fintek_suspend,655 .resume = fintek_resume,656 .shutdown = fintek_shutdown,657};658 659module_param(debug, int, S_IRUGO | S_IWUSR);660MODULE_PARM_DESC(debug, "Enable debugging output");661 662MODULE_DEVICE_TABLE(pnp, fintek_ids);663MODULE_DESCRIPTION(FINTEK_DESCRIPTION " driver");664 665MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>");666MODULE_LICENSE("GPL");667 668module_pnp_driver(fintek_driver);669