/* detect hardware features */ static int fintek_hw_detect(struct fintek_dev *fintek) { unsigned long flags; u8 chip_major, chip_minor; u8 vendor_major, vendor_minor; u8 portsel, ir_class; u16 vendor; int ret = 0; fintek_config_mode_enable(fintek); /* Check if we're using config port 0x4e or 0x2e */ portsel = fintek_cr_read(fintek, GCR_CONFIG_PORT_SEL); if (portsel == 0xff) { fit_pr(KERN_INFO, "first portsel read was bunk, trying alt"); fintek_config_mode_disable(fintek); fintek->cr_ip = CR_INDEX_PORT2; fintek->cr_dp = CR_DATA_PORT2; fintek_config_mode_enable(fintek); portsel = fintek_cr_read(fintek, GCR_CONFIG_PORT_SEL); } fit_dbg("portsel reg: 0x%02x", portsel); ir_class = fintek_cir_reg_read(fintek, CIR_CR_CLASS); fit_dbg("ir_class reg: 0x%02x", ir_class); switch (ir_class) { case CLASS_RX_2TX: case CLASS_RX_1TX: fintek->hw_tx_capable = true; break; case CLASS_RX_ONLY: default: fintek->hw_tx_capable = false; break; } chip_major = fintek_cr_read(fintek, GCR_CHIP_ID_HI); chip_minor = fintek_cr_read(fintek, GCR_CHIP_ID_LO); vendor_major = fintek_cr_read(fintek, GCR_VENDOR_ID_HI); vendor_minor = fintek_cr_read(fintek, GCR_VENDOR_ID_LO); vendor = vendor_major << 8 | vendor_minor; if (vendor != VENDOR_ID_FINTEK) fit_pr(KERN_WARNING, "Unknown vendor ID: 0x%04x", vendor); else fit_dbg("Read Fintek vendor ID from chip"); fintek_config_mode_disable(fintek); spin_lock_irqsave(&fintek->fintek_lock, flags); fintek->chip_major = chip_major; fintek->chip_minor = chip_minor; fintek->chip_vendor = vendor; spin_unlock_irqrestore(&fintek->fintek_lock, flags); return ret; }
static int fintek_suspend(struct pnp_dev *pdev, pm_message_t state) { struct fintek_dev *fintek = pnp_get_drvdata(pdev); unsigned long flags; fit_dbg("%s called", __func__); spin_lock_irqsave(&fintek->fintek_lock, flags); /* disable all CIR interrupts */ fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS); spin_unlock_irqrestore(&fintek->fintek_lock, flags); fintek_config_mode_enable(fintek); /* disable cir logical dev */ fintek_select_logical_dev(fintek, fintek->logical_dev_cir); fintek_cr_write(fintek, LOGICAL_DEV_DISABLE, CIR_CR_DEV_EN); fintek_config_mode_disable(fintek); /* make sure wake is enabled */ fintek_enable_wake(fintek); return 0; }
/* dump current cir register contents */ static void cir_dump_regs(struct fintek_dev *fintek) { fintek_config_mode_enable(fintek); fintek_select_logical_dev(fintek, fintek->logical_dev_cir); pr_info("%s: Dump CIR logical device registers:\n", FINTEK_DRIVER_NAME); pr_info(" * CR CIR BASE ADDR: 0x%x\n", (fintek_cr_read(fintek, CIR_CR_BASE_ADDR_HI) << 8) | fintek_cr_read(fintek, CIR_CR_BASE_ADDR_LO)); pr_info(" * CR CIR IRQ NUM: 0x%x\n", fintek_cr_read(fintek, CIR_CR_IRQ_SEL)); fintek_config_mode_disable(fintek); pr_info("%s: Dump CIR registers:\n", FINTEK_DRIVER_NAME); pr_info(" * STATUS: 0x%x\n", fintek_cir_reg_read(fintek, CIR_STATUS)); pr_info(" * CONTROL: 0x%x\n", fintek_cir_reg_read(fintek, CIR_CONTROL)); pr_info(" * RX_DATA: 0x%x\n", fintek_cir_reg_read(fintek, CIR_RX_DATA)); pr_info(" * TX_CONTROL: 0x%x\n", fintek_cir_reg_read(fintek, CIR_TX_CONTROL)); pr_info(" * TX_DATA: 0x%x\n", fintek_cir_reg_read(fintek, CIR_TX_DATA)); }
static void fintek_disable_cir(struct fintek_dev *fintek) { fintek_config_mode_enable(fintek); /* disable the CIR logical device */ fintek_select_logical_dev(fintek, fintek->logical_dev_cir); fintek_cr_write(fintek, LOGICAL_DEV_DISABLE, CIR_CR_DEV_EN); fintek_config_mode_disable(fintek); }
static void fintek_enable_wake(struct fintek_dev *fintek) { fintek_config_mode_enable(fintek); fintek_select_logical_dev(fintek, LOGICAL_DEV_ACPI); /* Allow CIR PME's to wake system */ fintek_set_reg_bit(fintek, ACPI_WAKE_EN_CIR_BIT, LDEV_ACPI_WAKE_EN_REG); /* Enable CIR PME's */ fintek_set_reg_bit(fintek, ACPI_PME_CIR_BIT, LDEV_ACPI_PME_EN_REG); /* Clear CIR PME status register */ fintek_set_reg_bit(fintek, ACPI_PME_CIR_BIT, LDEV_ACPI_PME_CLR_REG); /* Save state */ fintek_set_reg_bit(fintek, ACPI_STATE_CIR_BIT, LDEV_ACPI_STATE_REG); fintek_config_mode_disable(fintek); }
static void fintek_enable_cir(struct fintek_dev *fintek) { /* set IRQ enabled */ fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_EN, CIR_STATUS); fintek_config_mode_enable(fintek); /* enable the CIR logical device */ fintek_select_logical_dev(fintek, fintek->logical_dev_cir); fintek_cr_write(fintek, LOGICAL_DEV_ENABLE, CIR_CR_DEV_EN); fintek_config_mode_disable(fintek); /* clear all pending interrupts */ fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS); /* enable interrupts */ fintek_enable_cir_irq(fintek); }
static int fintek_resume(struct pnp_dev *pdev) { struct fintek_dev *fintek = pnp_get_drvdata(pdev); fit_dbg("%s called", __func__); /* open interrupt */ fintek_enable_cir_irq(fintek); /* Enable CIR logical device */ fintek_config_mode_enable(fintek); fintek_select_logical_dev(fintek, fintek->logical_dev_cir); fintek_cr_write(fintek, LOGICAL_DEV_ENABLE, CIR_CR_DEV_EN); fintek_config_mode_disable(fintek); fintek_cir_regs_init(fintek); return 0; }
/* interrupt service routine for incoming and outgoing CIR data */ static irqreturn_t fintek_cir_isr(int irq, void *data) { struct fintek_dev *fintek = data; u8 status, rx_irqs; fit_dbg_verbose("%s firing", __func__); fintek_config_mode_enable(fintek); fintek_select_logical_dev(fintek, fintek->logical_dev_cir); fintek_config_mode_disable(fintek); /* * Get IR Status register contents. Write 1 to ack/clear * * bit: reg name - description * 3: TX_FINISH - TX is finished * 2: TX_UNDERRUN - TX underrun * 1: RX_TIMEOUT - RX data timeout * 0: RX_RECEIVE - RX data received */ status = fintek_cir_reg_read(fintek, CIR_STATUS); if (!(status & CIR_STATUS_IRQ_MASK) || status == 0xff) { fit_dbg_verbose("%s exiting, IRSTS 0x%02x", __func__, status); fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS); return IRQ_RETVAL(IRQ_NONE); } if (debug) fintek_cir_log_irqs(status); rx_irqs = status & (CIR_STATUS_RX_RECEIVE | CIR_STATUS_RX_TIMEOUT); if (rx_irqs) fintek_get_rx_ir_data(fintek, rx_irqs); /* ack/clear all irq flags we've got */ fintek_cir_reg_write(fintek, status, CIR_STATUS); fit_dbg_verbose("%s done", __func__); return IRQ_RETVAL(IRQ_HANDLED); }
/* Allocate memory, probe hardware, and initialize everything */ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) { struct fintek_dev *fintek; struct rc_dev *rdev; int ret = -ENOMEM; fintek = kzalloc(sizeof(struct fintek_dev), GFP_KERNEL); if (!fintek) return ret; /* input device for IR remote (and tx) */ rdev = rc_allocate_device(); if (!rdev) goto exit_free_dev_rdev; ret = -ENODEV; /* validate pnp resources */ if (!pnp_port_valid(pdev, 0)) { dev_err(&pdev->dev, "IR PNP Port not valid!\n"); goto exit_free_dev_rdev; } if (!pnp_irq_valid(pdev, 0)) { dev_err(&pdev->dev, "IR PNP IRQ not valid!\n"); goto exit_free_dev_rdev; } fintek->cir_addr = pnp_port_start(pdev, 0); fintek->cir_irq = pnp_irq(pdev, 0); fintek->cir_port_len = pnp_port_len(pdev, 0); fintek->cr_ip = CR_INDEX_PORT; fintek->cr_dp = CR_DATA_PORT; spin_lock_init(&fintek->fintek_lock); pnp_set_drvdata(pdev, fintek); fintek->pdev = pdev; ret = fintek_hw_detect(fintek); if (ret) goto exit_free_dev_rdev; /* Initialize CIR & CIR Wake Logical Devices */ fintek_config_mode_enable(fintek); fintek_cir_ldev_init(fintek); fintek_config_mode_disable(fintek); /* Initialize CIR & CIR Wake Config Registers */ fintek_cir_regs_init(fintek); /* Set up the rc device */ rdev->priv = fintek; rdev->driver_type = RC_DRIVER_IR_RAW; rdev->allowed_protocols = RC_BIT_ALL; rdev->open = fintek_open; rdev->close = fintek_close; rdev->input_name = FINTEK_DESCRIPTION; rdev->input_phys = "fintek/cir0"; rdev->input_id.bustype = BUS_HOST; rdev->input_id.vendor = VENDOR_ID_FINTEK; rdev->input_id.product = fintek->chip_major; rdev->input_id.version = fintek->chip_minor; rdev->dev.parent = &pdev->dev; rdev->driver_name = FINTEK_DRIVER_NAME; rdev->map_name = RC_MAP_RC6_MCE; rdev->timeout = US_TO_NS(1000); /* rx resolution is hardwired to 50us atm, 1, 25, 100 also possible */ rdev->rx_resolution = US_TO_NS(CIR_SAMPLE_PERIOD); fintek->rdev = rdev; ret = -EBUSY; /* now claim resources */ if (!request_region(fintek->cir_addr, fintek->cir_port_len, FINTEK_DRIVER_NAME)) goto exit_free_dev_rdev; if (request_irq(fintek->cir_irq, fintek_cir_isr, IRQF_SHARED, FINTEK_DRIVER_NAME, (void *)fintek)) goto exit_free_cir_addr; ret = rc_register_device(rdev); if (ret) goto exit_free_irq; device_init_wakeup(&pdev->dev, true); fit_pr(KERN_NOTICE, "driver has been successfully loaded\n"); if (debug) cir_dump_regs(fintek); return 0; exit_free_irq: free_irq(fintek->cir_irq, fintek); exit_free_cir_addr: release_region(fintek->cir_addr, fintek->cir_port_len); exit_free_dev_rdev: rc_free_device(rdev); kfree(fintek); return ret; }