/** * quicki2c_acpi_get_dsm_property - Query device ACPI DSM parameter * @adev: Point to ACPI device * @guid: ACPI method's guid * @rev: ACPI method's revision * @func: ACPI method's function number * @type: ACPI parameter's data type * @prop_buf: Point to return buffer * * This is a helper function for device to query its ACPI DSM parameters. * * Return: 0 if success or ENODEV on failure.
*/ staticint quicki2c_acpi_get_dsm_property(struct acpi_device *adev, const guid_t *guid,
u64 rev, u64 func, acpi_object_type type, void *prop_buf)
{
acpi_handle handle = acpi_device_handle(adev); union acpi_object *obj;
/** * quicki2c_acpi_get_dsd_property - Query device ACPI DSD parameter * @adev: Point to ACPI device * @dsd_method_name: ACPI method's property name * @type: ACPI parameter's data type * @prop_buf: Point to return buffer * * This is a helper function for device to query its ACPI DSD parameters. * * Return: 0 if success or ENODEV on failed.
*/ staticint quicki2c_acpi_get_dsd_property(struct acpi_device *adev, acpi_string dsd_method_name,
acpi_object_type type, void *prop_buf)
{
acpi_handle handle = acpi_device_handle(adev); struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; union acpi_object *ret_obj;
acpi_status status;
status = acpi_evaluate_object(handle, dsd_method_name, NULL, &buffer); if (ACPI_FAILURE(status)) {
acpi_handle_err(handle, "Can't evaluate %s method: %d\n", dsd_method_name, status); return -ENODEV;
}
/** * quicki2c_irq_quick_handler - The ISR of the QuickI2C driver * @irq: The irq number * @dev_id: Pointer to the quicki2c_device structure * * Return: IRQ_WAKE_THREAD if further process needed.
*/ static irqreturn_t quicki2c_irq_quick_handler(int irq, void *dev_id)
{ struct quicki2c_device *qcdev = dev_id;
if (qcdev->state == QUICKI2C_DISABLED) return IRQ_HANDLED;
/* Disable THC interrupt before current interrupt be handled */
thc_interrupt_enable(qcdev->thc_hw, false);
return IRQ_WAKE_THREAD;
}
/** * try_recover - Try to recovery THC and Device * @qcdev: Pointer to quicki2c_device structure * * This function is an error handler, called when fatal error happens. * It try to reset touch device and re-configure THC to recovery * communication between touch device and THC. * * Return: 0 if successful or error code on failure
*/ staticint try_recover(struct quicki2c_device *qcdev)
{ int ret;
thc_dma_unconfigure(qcdev->thc_hw);
ret = thc_dma_configure(qcdev->thc_hw); if (ret) {
dev_err(qcdev->dev, "Reconfig DMA failed\n"); return ret;
}
return 0;
}
staticint handle_input_report(struct quicki2c_device *qcdev)
{ struct hidi2c_report_packet *pkt = (struct hidi2c_report_packet *)qcdev->input_buf; int rx_dma_finished = 0;
size_t report_len; int ret;
while (!rx_dma_finished) {
ret = thc_rxdma_read(qcdev->thc_hw, THC_RXDMA2,
(u8 *)pkt, &report_len,
&rx_dma_finished); if (ret) return ret;
if (!pkt->len) { if (qcdev->state == QUICKI2C_RESETING) {
qcdev->reset_ack = true;
wake_up(&qcdev->reset_ack_wq);
/* THC hardware init */
qcdev->thc_hw = thc_dev_init(qcdev->dev, qcdev->mem_addr); if (IS_ERR(qcdev->thc_hw)) {
ret = PTR_ERR(qcdev->thc_hw);
dev_err_once(dev, "Failed to initialize THC device context, ret = %d.\n", ret); return ERR_PTR(ret);
}
ret = quicki2c_get_acpi_resources(qcdev); if (ret) {
dev_err_once(dev, "Get ACPI resources failed, ret = %d\n", ret); return ERR_PTR(ret);
}
ret = thc_interrupt_quiesce(qcdev->thc_hw, true); if (ret) return ERR_PTR(ret);
ret = thc_port_select(qcdev->thc_hw, THC_PORT_TYPE_I2C); if (ret) {
dev_err_once(dev, "Failed to select THC port, ret = %d.\n", ret); return ERR_PTR(ret);
}
ret = thc_i2c_subip_init(qcdev->thc_hw, qcdev->i2c_slave_addr,
qcdev->i2c_speed_mode,
qcdev->i2c_clock_hcnt,
qcdev->i2c_clock_lcnt); if (ret) return ERR_PTR(ret);
/** * quicki2c_dma_adv_enable - Configure and enable DMA advanced features * @qcdev: Pointer to the quicki2c_device structure * * If platform supports THC DMA advanced features, such as max input size * control or interrupt delay, configures and enables them.
*/ staticvoid quicki2c_dma_adv_enable(struct quicki2c_device *qcdev)
{ /* * If platform supports max input size control feature and touch device * max input length <= THC detect capability, enable the feature with device * max input length.
*/ if (qcdev->ddata->max_detect_size >=
le16_to_cpu(qcdev->dev_desc.max_input_len)) {
thc_i2c_set_rx_max_size(qcdev->thc_hw,
le16_to_cpu(qcdev->dev_desc.max_input_len));
thc_i2c_rx_max_size_enable(qcdev->thc_hw, true);
}
/* If platform supports interrupt delay feature, enable it with given delay */ if (qcdev->ddata->interrupt_delay) {
thc_i2c_set_rx_int_delay(qcdev->thc_hw,
qcdev->ddata->interrupt_delay);
thc_i2c_rx_int_delay_enable(qcdev->thc_hw, true);
}
}
/** * quicki2c_dma_adv_disable - Disable DMA advanced features * @qcdev: Pointer to the quicki2c device structure * * Disable all DMA advanced features if platform supports.
*/ staticvoid quicki2c_dma_adv_disable(struct quicki2c_device *qcdev)
{ if (qcdev->ddata->max_detect_size)
thc_i2c_rx_max_size_enable(qcdev->thc_hw, false);
if (qcdev->ddata->interrupt_delay)
thc_i2c_rx_int_delay_enable(qcdev->thc_hw, false);
}
/** * quicki2c_dma_init - Configure THC DMA for QuickI2C device * @qcdev: Pointer to the quicki2c_device structure * * This function uses TIC's parameters(such as max input length, max output * length) to allocate THC DMA buffers and configure THC DMA engines. * * Return: 0 if success or error code on failure.
*/ staticint quicki2c_dma_init(struct quicki2c_device *qcdev)
{
size_t swdma_max_len; int ret;
ret = thc_dma_set_max_packet_sizes(qcdev->thc_hw, 0,
le16_to_cpu(qcdev->dev_desc.max_input_len),
le16_to_cpu(qcdev->dev_desc.max_output_len),
swdma_max_len); if (ret) return ret;
ret = thc_dma_allocate(qcdev->thc_hw); if (ret) {
dev_err(qcdev->dev, "Allocate THC DMA buffer failed, ret = %d\n", ret); return ret;
}
/* Enable RxDMA */
ret = thc_dma_configure(qcdev->thc_hw); if (ret) {
dev_err(qcdev->dev, "Configure THC DMA failed, ret = %d\n", ret);
thc_dma_unconfigure(qcdev->thc_hw);
thc_dma_release(qcdev->thc_hw); return ret;
}
if (qcdev->ddata)
quicki2c_dma_adv_enable(qcdev);
return 0;
}
/** * quicki2c_dma_deinit - Release THC DMA for QuickI2C device * @qcdev: Pointer to the quicki2c_device structure * * Stop THC DMA engines and release all DMA buffers. *
*/ staticvoid quicki2c_dma_deinit(struct quicki2c_device *qcdev)
{
thc_dma_unconfigure(qcdev->thc_hw);
thc_dma_release(qcdev->thc_hw);
if (qcdev->ddata)
quicki2c_dma_adv_disable(qcdev);
}
/** * quicki2c_alloc_report_buf - Alloc report buffers * @qcdev: Pointer to the quicki2c_device structure * * Allocate report descriptor buffer, it will be used for restore TIC HID * report descriptor. * * Allocate input report buffer, it will be used for receive HID input report * data from TIC. * * Allocate output report buffer, it will be used for store HID output report, * such as set feature. * * Return: 0 if success or error code on failure.
*/ staticint quicki2c_alloc_report_buf(struct quicki2c_device *qcdev)
{
size_t max_report_len;
qcdev->report_descriptor = devm_kzalloc(qcdev->dev,
le16_to_cpu(qcdev->dev_desc.report_desc_len),
GFP_KERNEL); if (!qcdev->report_descriptor) return -ENOMEM;
/* * Some HIDI2C devices don't declare input/output max length correctly, * give default 4K buffer to avoid DMA buffer overrun.
*/
max_report_len = max(le16_to_cpu(qcdev->dev_desc.max_input_len), SZ_4K);
qcdev->input_buf = devm_kzalloc(qcdev->dev, max_report_len, GFP_KERNEL); if (!qcdev->input_buf) return -ENOMEM;
if (!le16_to_cpu(qcdev->dev_desc.max_output_len))
qcdev->dev_desc.max_output_len = cpu_to_le16(SZ_4K);
qcdev->report_buf = devm_kzalloc(qcdev->dev, max_report_len, GFP_KERNEL); if (!qcdev->report_buf) return -ENOMEM;
qcdev->report_len = max_report_len;
return 0;
}
/* * quicki2c_probe: QuickI2C driver probe function * @pdev: Point to PCI device * @id: Point to pci_device_id structure * * This function initializes THC and HIDI2C device, the flow is: * - Do THC pci device initialization * - Query HIDI2C ACPI parameters * - Configure THC to HIDI2C mode * - Go through HIDI2C enumeration flow * |- Read device descriptor * |- Reset HIDI2C device * - Enable THC interrupt and DMA * - Read report descriptor * - Register HID device * - Enable runtime power management * * Return 0 if success or error code on failure.
*/ staticint quicki2c_probe(struct pci_dev *pdev, conststruct pci_device_id *id)
{ conststruct quicki2c_ddata *ddata = (conststruct quicki2c_ddata *)id->driver_data; struct quicki2c_device *qcdev; void __iomem *mem_addr; int ret;
ret = pcim_enable_device(pdev); if (ret) {
dev_err_once(&pdev->dev, "Failed to enable PCI device, ret = %d.\n", ret); return ret;
}
pci_set_master(pdev);
mem_addr = pcim_iomap_region(pdev, 0, KBUILD_MODNAME);
ret = PTR_ERR_OR_ZERO(mem_addr); if (ret) {
dev_err_once(&pdev->dev, "Failed to get PCI regions, ret = %d.\n", ret); goto disable_pci_device;
}
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); if (ret) {
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); if (ret) {
dev_err_once(&pdev->dev, "No usable DMA configuration %d\n", ret); goto disable_pci_device;
}
}
ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES); if (ret < 0) {
dev_err_once(&pdev->dev, "Failed to allocate IRQ vectors. ret = %d\n", ret); goto disable_pci_device;
}
pdev->irq = pci_irq_vector(pdev, 0);
qcdev = quicki2c_dev_init(pdev, mem_addr, ddata); if (IS_ERR(qcdev)) {
dev_err_once(&pdev->dev, "QuickI2C device init failed\n");
ret = PTR_ERR(qcdev); goto disable_pci_device;
}
pci_set_drvdata(pdev, qcdev);
ret = devm_request_threaded_irq(&pdev->dev, pdev->irq,
quicki2c_irq_quick_handler,
quicki2c_irq_thread_handler,
IRQF_ONESHOT, KBUILD_MODNAME,
qcdev); if (ret) {
dev_err_once(&pdev->dev, "Failed to request threaded IRQ, irq = %d.\n", pdev->irq); goto dev_deinit;
}
ret = quicki2c_get_device_descriptor(qcdev); if (ret) {
dev_err(&pdev->dev, "Get device descriptor failed, ret = %d\n", ret); goto dev_deinit;
}
ret = quicki2c_alloc_report_buf(qcdev); if (ret) {
dev_err(&pdev->dev, "Alloc report buffers failed, ret= %d\n", ret); goto dev_deinit;
}
ret = quicki2c_dma_init(qcdev); if (ret) {
dev_err(&pdev->dev, "Setup THC DMA failed, ret= %d\n", ret); goto dev_deinit;
}
ret = thc_interrupt_quiesce(qcdev->thc_hw, false); if (ret) goto dev_deinit;
ret = quicki2c_set_power(qcdev, HIDI2C_ON); if (ret) {
dev_err(&pdev->dev, "Set Power On command failed, ret= %d\n", ret); goto dev_deinit;
}
ret = quicki2c_reset(qcdev); if (ret) {
dev_err(&pdev->dev, "Reset HIDI2C device failed, ret= %d\n", ret); goto dev_deinit;
}
ret = quicki2c_get_report_descriptor(qcdev); if (ret) {
dev_err(&pdev->dev, "Get report descriptor failed, ret = %d\n", ret); goto dma_deinit;
}
ret = quicki2c_hid_probe(qcdev); if (ret) {
dev_err(&pdev->dev, "Failed to register HID device, ret = %d\n", ret); goto dma_deinit;
}
/** * quicki2c_remove - Device Removal Routine * @pdev: Point to PCI device structure * * This is called by the PCI subsystem to alert the driver that it should * release a PCI device.
*/ staticvoid quicki2c_remove(struct pci_dev *pdev)
{ struct quicki2c_device *qcdev;
qcdev = pci_get_drvdata(pdev); if (!qcdev) return;
/** * quicki2c_shutdown - Device Shutdown Routine * @pdev: Point to PCI device structure * * This is called from the reboot notifier, it's a simplified version of remove * so we go down faster.
*/ staticvoid quicki2c_shutdown(struct pci_dev *pdev)
{ struct quicki2c_device *qcdev;
qcdev = pci_get_drvdata(pdev); if (!qcdev) return;
/* Must stop DMA before reboot to avoid DMA entering into unknown state */
quicki2c_dma_deinit(qcdev);
qcdev = pci_get_drvdata(pdev); if (!qcdev) return -ENODEV;
/* * As I2C is THC subsystem, no register auto save/restore support, * need driver to do that explicitly for every D3 case.
*/
ret = thc_i2c_subip_regs_save(qcdev->thc_hw); if (ret) return ret;
ret = thc_interrupt_quiesce(qcdev->thc_hw, true); if (ret) return ret;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.