Re: [PATCH v1 2/3] Bluetooth: btintel_pcie: Add support for PCIE transport

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, Mar 28, 2024 at 04:49:03PM +0530, Kiran K wrote:
> From: Tedd Ho-Jeong An <tedd.an@xxxxxxxxx>
> 
> Add initial code to support Intel bluetooth devices based on PICE
> transport. This patch allocates memory for buffers, internal structures,
> initializes interrupts for TX & RX and initializes PCIE device.

s/PCIE/PCIe/ (subject, commit log, throughout)
s/PICE/PCIe/ (commit log typo)
s/This patch allocates/Allocate/
s/initializes/initialize/ (twice)

> +#if BTITNEL_PCIE_ENABLE_HCI_DUMP
> +static inline void btintel_pcie_hci_dump(const char *p, const void *b, int s)
> +{
> +	const unsigned char *ptr = (const unsigned char *)b;
> +	char str[64];
> +	int c, i;
> +
> +	for (i = c = 0; c < s; c++) {
> +		i += snprintf(str + i, sizeof(str) - i, "%02x ", ptr[c]);
> +		if ((c > 0 && (c + 1) % 8 == 0) || (c == s - 1)) {
> +			BT_DBG("%s: %s", p, str);
> +			i = 0;
> +		}
> +	}

Maybe print_hex_dump() or similar would save you some work here?

> +/* Poll internal in microseconds */

s/internal/interval/

> +#define POLL_INTERVAL			10

I think it's nice when the name of a timeout or interval includes the
units, e.g., POLL_INTERVAL_US.

> +/* Set the doorbell for RXQ to notify the device that @index(actually index-1)
> + * is available to receive the data

Typical style is to add a space before the opening paren in English
text (many occurrences).

> +static void btintel_pcie_set_rx_db(struct btintel_pcie_data *data, u16 index)
> +{
> +	u32 val;
> +
> +	val = index;
> +	val |= (513 << 16);

Where does 513 come from?  Maybe a #define or explanatory comment?

> +static void btintel_pcie_prepare_rx(struct rxq *rxq, u16 frbd_index)
> +{
> +	struct data_buf *buf;
> +	struct frbd *frbd;
> +
> +	/* Get the buffer of the frbd for DMA */

s/frbd/FRBD/ for consistency (several occurrences).

> +static void btintel_pcie_rx_work(struct work_struct *work)
> +{
> +	struct btintel_pcie_data *data = container_of(work,
> +					struct btintel_pcie_data, rx_work);
> +	struct sk_buff *skb;
> +	int err;
> +
> +	/* Process the sk_buf in queue and send to the hci layer */

s/hci/HCI/ for consistency.

> +/* create the sk_buff with data and save it to queue and start rx work
> + */
> +static int btintel_pcie_submit_rx_work(struct btintel_pcie_data *data, u8 status,
> +				       void *buf)

Comment would fit on one line.

There's a random mix of "rx" vs "RX" in comments, would be nice to be
consistent.

> +static void btintel_pcie_msix_rx_handle(struct btintel_pcie_data *data)
> +{
> +	u16 cr_hia, cr_tia;
> +	struct rxq *rxq;
> +	struct urbd1 *urbd1;
> +	struct frbd *frbd;
> +	struct data_buf *buf;
> +	int ret;
> +
> +	cr_hia = data->ia.cr_hia[RXQ_NUM];
> +	cr_tia = data->ia.cr_tia[RXQ_NUM];
> +
> +	BT_DBG("[RXQ] cr_hia=%u  cr_tia=%u", cr_hia, cr_tia);
> +
> +	/* Check CR_TIA and CR_HIA for change */
> +	if (cr_tia == cr_hia) {
> +		BT_ERR("[RXQ] no new CD found");
> +		return;
> +	}
> +
> +	rxq = &data->rxq;
> +
> +	/* The firmware sends multiple CD in a single MSIX and it needs to
> +	 * process all received CDs in this interrupt.
> +	 */
> +	while (cr_tia != cr_hia) {
> +		/* Get URBD1 pointed by cr_tia */
> +		urbd1 = &rxq->urbd1s[cr_tia];
> +		ipc_print_urbd1(urbd1, cr_tia);
> +
> +		/* Get FRBD poined by urbd1->frbd_tag */

s/poined/pointed to/ ?

> +	 * Before sending the interrupt the HW disables it to prevent
> +	 * a nested interrupt. This is done by writing 1 to the corresponding
> +	 * bit in the mask register. After handling the interrupt, it should be
> +	 * re-enabled by clearing this bit. This register is defined as
> +	 * write 1 clear (W1C) register, meaning that it's being clear
> +	 * by writing 1 to the bit.

s/being clear/cleared/

> +	num_irqs = pci_enable_msix_range(data->pdev, data->msix_entries,
> +					 MSIX_VEC_MIN,
> +					 MSIX_VEC_MAX);

pci_enable_msix_range() is deprecated; consider
pci_alloc_irq_vectors() instead.

> +static int btintel_pcie_config_pcie(struct pci_dev *pdev,
> +				    struct btintel_pcie_data *data)
> +{
> +	int err;
> +
> +	err = pcim_enable_device(pdev);
> +	if (err) {
> +		BT_ERR("Failed to enable pci device (%d)", err);
> +		return err;
> +	}
> +	pci_set_master(pdev);
> +
> +	/* Setup DMA mask */

Superfluous comment.

> +	BT_DBG("Set DMA_MASK(64)");
> +	err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
> +	if (err) {
> +		BT_DBG("Set DMA_MASK(32)");
> +		err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
> +		/* Both attempt failed */
> +		if (err) {
> +			BT_ERR("No suitable DMA available");
> +			return err;
> +		}
> +	}
> +
> +	/* Get BAR to access CSR */

Superfluous.

> +	err = pcim_iomap_regions(pdev, BIT(0), KBUILD_MODNAME);
> +	if (err) {
> +		BT_ERR("Failed to get iomap regions (%d)", err);
> +		return err;
> +	}
> +
> +	data->base_addr = pcim_iomap_table(pdev)[0];
> +	if (!data->base_addr) {
> +		BT_ERR("Failed to get base address");
> +		return -ENODEV;
> +	}
> +
> +	err = btintel_pcie_setup_irq(data);
> +	if (err) {
> +		BT_ERR("Failed to setup irq for msix");
> +		return err;
> +	}
> +
> +	/* Configure MSI-X with causes list */

Random mix of "MSIX" and "MSI-X".  I think "MSI-X" is nicer.

> +static int btintel_pcie_setup_txq_bufs(struct btintel_pcie_data *data,
> +				       struct txq *txq)
> +{
> +	int err = 0, i;
> +	struct data_buf *buf;
> +
> +	if (txq->count == 0) {
> +		BT_ERR("invalid parameter: txq->count");
> +		err = -EINVAL;
> +		goto exit_error;

You do all the cleanup inline, so there's really no benefit to "err"
and the "exit_error" label.  You could "return -EINVAL" directly here
and similar below.

> +	}
> +
> +	/* Allocate the same number of buffers as the descriptor */
> +	txq->bufs = kmalloc_array(txq->count, sizeof(*buf), GFP_KERNEL);
> +	if (!txq->bufs) {
> +		err = -ENOMEM;
> +		goto exit_error;
> +	}
> +
> +	/* Allocate full chunk of data buffer for DMA first and do indexing and
> +	 * initialization next, so it can be freed easily
> +	 */
> +	txq->buf_v_addr = dma_alloc_coherent(&data->pdev->dev,
> +					     txq->count * BUFFER_SIZE,
> +					     &txq->buf_p_addr,
> +					     GFP_KERNEL | __GFP_NOWARN);
> +	if (!txq->buf_v_addr) {
> +		BT_ERR("Failed to allocate DMA buf");
> +		err = -ENOMEM;
> +		kfree(txq->bufs);
> +		goto exit_error;
> +	}
> +	memset(txq->buf_v_addr, 0, txq->count * BUFFER_SIZE);
> +
> +	BT_DBG("alloc bufs: p=0x%llx v=0x%p", txq->buf_p_addr, txq->buf_v_addr);
> +
> +	/* Setup the allocated DMA buffer to bufs. Each data_buf should
> +	 * have virtual address and physical address
> +	 */
> +	for (i = 0; i < txq->count; i++) {
> +		buf = &txq->bufs[i];
> +		buf->data_p_addr = txq->buf_p_addr + (i * BUFFER_SIZE);
> +		buf->data = txq->buf_v_addr + (i * BUFFER_SIZE);
> +	}
> +
> +exit_error:
> +	return err;
> +}

> +static int btintel_pcie_setup_rxq_bufs(struct btintel_pcie_data *data,
> +				       struct rxq *rxq)
> +{
> +	int err = 0, i;
> +	struct data_buf *buf;
> +
> +	if (rxq->count == 0) {
> +		BT_ERR("invalid parameter: rxq->count");
> +		err = -EINVAL;
> +		goto exit_error;

Same "err", "exit_error" comment.

> +	}
> +
> +	/* Allocate the same number of buffers as the descriptor */
> +	rxq->bufs = kmalloc_array(rxq->count, sizeof(*buf), GFP_KERNEL);
> +	if (!rxq->bufs) {
> +		err = -ENOMEM;
> +		goto exit_error;
> +	}
> +
> +	/* Allocate full chunk of data buffer for DMA first and do indexing and
> +	 * initialization next, so it can be freed easily
> +	 */
> +	rxq->buf_v_addr = dma_alloc_coherent(&data->pdev->dev,
> +					     rxq->count * BUFFER_SIZE,
> +					     &rxq->buf_p_addr,
> +					     GFP_KERNEL | __GFP_NOWARN);
> +	if (!rxq->buf_v_addr) {
> +		BT_ERR("Failed to allocate DMA buf");
> +		err = -ENOMEM;
> +		kfree(rxq->bufs);
> +		goto exit_error;
> +	}
> +	memset(rxq->buf_v_addr, 0, rxq->count * BUFFER_SIZE);
> +
> +	BT_DBG("alloc bufs: p=0x%llx v=0x%p", rxq->buf_p_addr, rxq->buf_v_addr);
> +
> +	/* Setup the allocated DMA buffer to bufs. Each data_buf should
> +	 * have virtual address and physical address
> +	 */
> +	for (i = 0; i < rxq->count; i++) {
> +		buf = &rxq->bufs[i];
> +		buf->data_p_addr = rxq->buf_p_addr + (i * BUFFER_SIZE);
> +		buf->data = rxq->buf_v_addr + (i * BUFFER_SIZE);
> +	}
> +
> +exit_error:
> +
> +	return err;
> +}

> +static void btintel_pcie_release_hdev(struct btintel_pcie_data *data)
> +{
> +	struct hci_dev *hdev;
> +
> +	hdev = data->hdev;
> +	if (hdev) {

This test shouldn't be necessary if the driver is written correctly.
We should never get here unless .probe() was successful.

> +		hci_unregister_dev(hdev);

There should be a corresponding hci_register_dev().  Since that's not
here yet, I don't think the hci_unregister_dev() should be in this
patch either.  Looks like maybe just incomplete code?

Oh, I see hci_register_dev() is added in patch 3/3.  They should be
added in the same patch.

> +		hci_free_dev(hdev);
> +	}
> +	data->hdev = NULL;
> +}

> +static int btintel_pcie_probe(struct pci_dev *pdev,
> +			      const struct pci_device_id *ent)
> +{
> +	int err;
> +	struct btintel_pcie_data *data;
> +
> +	if (!pdev)
> +		return -ENODEV;
> +
> +	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
> +	if (!data)
> +		return -ENOMEM;
> +
> +	/* initialize the btintel_pcie data struct */

Superfluous comment.

> +	data->pdev = pdev;
> +
> +	spin_lock_init(&data->irq_lock);
> +	spin_lock_init(&data->hci_rx_lock);
> +
> +	init_waitqueue_head(&data->gp0_wait_q);
> +	data->gp0_received = false;
> +
> +	init_waitqueue_head(&data->tx_wait_q);
> +	data->tx_wait_done = false;
> +
> +	data->workqueue = alloc_ordered_workqueue(KBUILD_MODNAME, WQ_HIGHPRI);
> +	if (!data->workqueue) {
> +		BT_ERR("Failed to create workqueue");
> +		return -ENOMEM;
> +	}
> +	skb_queue_head_init(&data->rx_skb_q);
> +	INIT_WORK(&data->rx_work, btintel_pcie_rx_work);
> +
> +	data->boot_stage_cache = 0x00;
> +	data->img_resp_cache = 0x00;
> +
> +	/* PCIe specific all to configure it for this device includes
> +	 * enabling pice device, setting master, reading BAR[0], configuring
> +	 * MSIx, setting DMA mask, and save the driver data.

s/pice/PCIe/
s/MSIx/MSI-X/
s/save the/saving the/

Whole comment is probably superfluous since this is common to all PCI
drivers.

> +	 */
> +	err = btintel_pcie_config_pcie(pdev, data);
> +	if (err) {
> +		BT_ERR("Failed to config pcie (%d)", err);
> +		goto exit_error;
> +	}
> +
> +	/* Set driver data for this PCI device */

Superfluous comment.

> +	pci_set_drvdata(pdev, data);
> +
> +	/* allocate the IPC struct */
> +	err = btintel_pcie_alloc(data);
> +	if (err) {
> +		BT_ERR("Failed to allocate queues(%d)", err);
> +		goto exit_error;
> +	}
> +
> +	/* Enable BT function */

Superfluous comment.

> +	err = btintel_pcie_enable_bt(data);
> +	if (err) {
> +		BT_ERR("Failed to start bluetooth device(%d)", err);
> +		goto exit_error;
> +	}
> +
> +	/* CNV information (CNVi and CNVr) is in CSR */
> +	data->cnvi = btintel_pcie_rd_reg32(data, CSR_HW_REV_REG);
> +	BT_DBG("cnvi:   0x%08x", data->cnvi);
> +
> +	data->cnvr = btintel_pcie_rd_reg32(data, CSR_RF_ID_REG);
> +	BT_DBG("cnvr:   0x%08x", data->cnvr);
> +
> +	err = btintel_pcie_start_rx(data);
> +	if (err) {
> +		BT_ERR("Failed to start rx (%d)", err);
> +		goto exit_error;
> +	}
> +
> +	err = btintel_pcie_setup_hdev(data);
> +	if (err) {
> +		BT_ERR("Failed to setup HCI module");
> +		goto exit_error;
> +	}
> +
> +	return 0;
> +
> +exit_error:
> +	/* reset device before leave */
> +	btintel_pcie_reset_bt(data);
> +
> +	/* clear bus mastering */

Superfluous comment.

> +	pci_clear_master(pdev);
> +
> +	/* Unset driver data for PCI device */

Superfluous comment.

> +	pci_set_drvdata(pdev, NULL);
> +
> +	return err;
> +}
> +
> +static void btintel_pcie_remove(struct pci_dev *pdev)
> +{
> +	struct btintel_pcie_data *data;
> +
> +	if (!pdev) {

Shouldn't need this check.  If you get here with "pdev == NULL", the
driver did something wrong and it's better to oops so the driver can
be fixed.

> +		BT_ERR("Invalid parameter: pdev");
> +		return;
> +	}
> +
> +	data = pci_get_drvdata(pdev);
> +	if (!data) {

Or this one.

> +		BT_ERR("data is empty");
> +		return;
> +	}
> +
> +	btintel_pcie_release_hdev(data);
> +
> +	flush_work(&data->rx_work);
> +
> +	destroy_workqueue(data->workqueue);
> +
> +	btintel_pcie_free(data);
> +
> +	/* reset device before leave */
> +	btintel_pcie_reset_bt(data);
> +
> +	/* clear bus mastering */

Superfluous comment.

> +	pci_clear_master(pdev);
> +
> +	/* Unset driver data for PCI device */

Superfluous comment.

> +	pci_set_drvdata(pdev, NULL);
> +}
> +
> +#ifdef CONFIG_PM
> +static int btintel_pcie_suspend(struct device *dev)
> +{
> +	/* TODO: Add support suspend */
> +	return 0;
> +}
> +
> +static int btintel_pcie_resume(struct device *dev)
> +{
> +	/* TODO: Add support resume */
> +	return 0;
> +}
> +
> +static SIMPLE_DEV_PM_OPS(btintel_pcie_pm_ops, btintel_pcie_suspend,
> +							btintel_pcie_resume);
> +#endif /* CONFIG_PM */

Seems pointless to include this #ifdef CONFIG_PM code until you
implement it.

> +static struct pci_driver btintel_pcie_driver = {
> +	.name = KBUILD_MODNAME,
> +	.id_table = btintel_pcie_table,
> +	.probe = btintel_pcie_probe,
> +	.remove = btintel_pcie_remove,
> +#ifdef CONFIG_PM
> +	.driver.pm = &btintel_pcie_pm_ops,
> +#endif /* CONFIG_PM */
> +};

> +/* Default Poll time for MAC access during init*/
> +#define DEFAULT_MAC_ACCESS_TIMEOUT	200000

Would be nice to have units here in the name (us, ms, etc).

> +/* Default interrupt timeout in msec */
> +#define DEFAULT_INTR_TIMEOUT		3000

And here.

> +/* Enum for RBD buffer size mappting */

s/mappting/mapping/ ?

> + * @dbgc_addr: DBGC first fragmemt address
> + * @dbgc_size: DBGC buffer size
> + * @early_enable: Enarly debug enable

s/fragmemt/fragment/
s/Enarly/Early/

> + * @dbg_output_mode: Debug output mode
> + *	Bit[4] DBGC O/P { 0=SRAM, 1=DRAM(not relevant for NPK) }
> + *	Bit[5] DBGC I/P { 0=BDBG, 1=DBGI }
> + *	Bits[6:7] DBGI O/P(relevant if bit[5] = 1)
> + *	 0=BT DBGC, 1=WiFi DBGC, 2=NPK }
> + * @dbg_preset: Debug preset
> + * @ext_addr: Address of context information extension
> + * @ext_size: Size of context information part
> + *
> + * Total 38 DWords
> + *

Superfluous blank line

> + */

Bjorn




[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux