/* * Setting this bit means each IO operation will target a different port * address; 0 means repeated IO operations will use the same port, * such as BT.
*/ #define FG_INCRADDR_LPC 0x02
struct lpc_cycle_para { unsignedint opflags; unsignedint csize; /* data length of each operation */
};
/* The minimal nanosecond interval for each query on LPC cycle status */ #define LPC_NSEC_PERWAIT 100
/* * The maximum waiting time is about 128us. It is specific for stream I/O, * such as ins. * * The fastest IO cycle time is about 390ns, but the worst case will wait * for extra 256 lpc clocks, so (256 + 13) * 30ns = 8 us. The maximum burst * cycles is 16. So, the maximum waiting time is about 128us under worst * case. * * Choose 1300 as the maximum.
*/ #define LPC_MAX_WAITCNT 1300
/* About 10us. This is specific for single IO operations, such as inb */ #define LPC_PEROP_WAITCNT 100
do {
status = readl(mbase + LPC_REG_OP_STATUS); if (status & LPC_REG_OP_STATUS_IDLE) return (status & LPC_REG_OP_STATUS_FINISHED) ? 0 : -EIO;
ndelay(LPC_NSEC_PERWAIT);
} while (--waitcnt);
return -ETIMEDOUT;
}
/* * hisi_lpc_target_in - trigger a series of LPC cycles for read operation * @lpcdev: pointer to hisi lpc device * @para: some parameters used to control the lpc I/O operations * @addr: the lpc I/O target port address * @buf: where the read back data is stored * @opcnt: how many I/O operations required, i.e. data width * * Returns 0 on success, non-zero on fail.
*/ staticint hisi_lpc_target_in(struct hisi_lpc_dev *lpcdev, struct lpc_cycle_para *para, unsignedlong addr, unsignedchar *buf, unsignedlong opcnt)
{ unsignedint cmd_word; unsignedint waitcnt; unsignedlong flags; int ret;
/* whether the operation is finished */
ret = wait_lpc_idle(lpcdev->membase, waitcnt); if (ret) {
spin_unlock_irqrestore(&lpcdev->cycle_lock, flags); return ret;
}
/* * hisi_lpc_target_out - trigger a series of LPC cycles for write operation * @lpcdev: pointer to hisi lpc device * @para: some parameters used to control the lpc I/O operations * @addr: the lpc I/O target port address * @buf: where the data to be written is stored * @opcnt: how many I/O operations required, i.e. data width * * Returns 0 on success, non-zero on fail.
*/ staticint hisi_lpc_target_out(struct hisi_lpc_dev *lpcdev, struct lpc_cycle_para *para, unsignedlong addr, constunsignedchar *buf, unsignedlong opcnt)
{ unsignedint waitcnt; unsignedlong flags;
u32 cmd_word; int ret;
if (!buf || !opcnt || !para || !lpcdev) return -EINVAL;
/* * hisi_lpc_comm_in - input the data in a single operation * @hostdata: pointer to the device information relevant to LPC controller * @pio: the target I/O port address * @dwidth: the data length required to read from the target I/O port * * When success, data is returned. Otherwise, ~0 is returned.
*/ static u32 hisi_lpc_comm_in(void *hostdata, unsignedlong pio, size_t dwidth)
{ struct hisi_lpc_dev *lpcdev = hostdata; struct lpc_cycle_para iopara; unsignedlong addr;
__le32 rd_data = 0; int ret;
if (!lpcdev || !dwidth || dwidth > LPC_MAX_DWIDTH) return ~0;
ret = hisi_lpc_target_in(lpcdev, &iopara, addr,
(unsignedchar *)&rd_data, dwidth); if (ret) return ~0;
return le32_to_cpu(rd_data);
}
/* * hisi_lpc_comm_out - output the data in a single operation * @hostdata: pointer to the device information relevant to LPC controller * @pio: the target I/O port address * @val: a value to be output from caller, maximum is four bytes * @dwidth: the data width required writing to the target I/O port * * This function corresponds to out(b,w,l) only.
*/ staticvoid hisi_lpc_comm_out(void *hostdata, unsignedlong pio,
u32 val, size_t dwidth)
{ struct hisi_lpc_dev *lpcdev = hostdata; struct lpc_cycle_para iopara; constunsignedchar *buf; unsignedlong addr;
__le32 _val = cpu_to_le32(val);
if (!lpcdev || !dwidth || dwidth > LPC_MAX_DWIDTH) return;
/* * hisi_lpc_comm_ins - input the data in the buffer in multiple operations * @hostdata: pointer to the device information relevant to LPC controller * @pio: the target I/O port address * @buffer: a buffer where read/input data bytes are stored * @dwidth: the data width required writing to the target I/O port * @count: how many data units whose length is dwidth will be read * * When success, the data read back is stored in buffer pointed by buffer. * Returns 0 on success, -errno otherwise.
*/ static u32 hisi_lpc_comm_ins(void *hostdata, unsignedlong pio, void *buffer,
size_t dwidth, unsignedint count)
{ struct hisi_lpc_dev *lpcdev = hostdata; unsignedchar *buf = buffer; struct lpc_cycle_para iopara; unsignedlong addr;
ret = hisi_lpc_target_in(lpcdev, &iopara, addr, buf, dwidth); if (ret) return ret;
buf += dwidth;
} while (--count);
return 0;
}
/* * hisi_lpc_comm_outs - output the data in the buffer in multiple operations * @hostdata: pointer to the device information relevant to LPC controller * @pio: the target I/O port address * @buffer: a buffer where write/output data bytes are stored * @dwidth: the data width required writing to the target I/O port * @count: how many data units whose length is dwidth will be written
*/ staticvoid hisi_lpc_comm_outs(void *hostdata, unsignedlong pio, constvoid *buffer, size_t dwidth, unsignedint count)
{ struct hisi_lpc_dev *lpcdev = hostdata; struct lpc_cycle_para iopara; constunsignedchar *buf = buffer; unsignedlong addr;
/* * Released firmware describes the IO port max address as 0x3fff, which is * the max host bus address. Fixup to a proper range. This will probably * never be fixed in firmware.
*/ staticvoid hisi_lpc_acpi_fixup_child_resource(struct device *hostdev, struct resource *r)
{ if (r->end != 0x3fff) return;
/* * hisi_lpc_acpi_set_io_res - set the resources for a child * @adev: ACPI companion of the device node to be updated the I/O resource * @hostdev: the device node associated with host controller * @res: double pointer to be set to the address of translated resources * @num_res: pointer to variable to hold the number of translated resources * * Returns 0 when successful, and a negative value for failure. * * For a given host controller, each child device will have an associated * host-relative address resource. This function will return the translated * logical PIO addresses for each child devices resources.
*/ staticint hisi_lpc_acpi_set_io_res(struct acpi_device *adev, struct device *hostdev, conststruct resource **res, int *num_res)
{ struct acpi_device *host = to_acpi_device(adev->dev.parent); struct resource_entry *rentry;
LIST_HEAD(resource_list); struct resource *resources; int count; int i;
if (!adev->status.present) {
dev_dbg(&adev->dev, "device is not present\n"); return -EIO;
}
if (acpi_device_enumerated(adev)) {
dev_dbg(&adev->dev, "has been enumerated\n"); return -EIO;
}
/* * The following code segment to retrieve the resources is common to * acpi_create_platform_device(), so consider a common helper function * in future.
*/
count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL); if (count <= 0) {
dev_dbg(&adev->dev, "failed to get resources\n"); return count ? count : -EIO;
}
for (; cell && cell->hid; cell++) { if (!strcmp(cell->hid, hid)) {
found = true; break;
}
}
if (!found) {
dev_warn(hostdev, "could not find cell for child device (%s), discarding\n",
hid); return 0;
}
pdev = platform_device_register_full(cell->pdevinfo); if (IS_ERR(pdev)) return PTR_ERR(pdev);
acpi_device_set_enumerated(child); return 0;
}
/* * hisi_lpc_acpi_probe - probe children for ACPI FW * @hostdev: LPC host device pointer * * Returns 0 when successful, and a negative value for failure. * * Create a platform device per child, fixing up the resources * from bus addresses to Logical PIO addresses. *
*/ staticint hisi_lpc_acpi_probe(struct device *hostdev)
{ int ret;
/* Only consider the children of the host */
ret = acpi_dev_for_each_child(ACPI_COMPANION(hostdev),
hisi_lpc_acpi_add_child, hostdev); if (ret)
hisi_lpc_acpi_remove(hostdev);
ret = logic_pio_register_range(range); if (ret) {
dev_err(dev, "register IO range failed (%d)!\n", ret); return ret;
}
/* register the LPC host PIO resources */ if (is_acpi_device_node(range->fwnode))
ret = hisi_lpc_acpi_probe(dev); else
ret = of_platform_populate(dev->of_node, NULL, NULL, dev); if (ret) {
logic_pio_unregister_range(range); 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.