Re: [PATCH v2 3/5] ata: Add APM X-Gene SATA driver

Arnd Bergmann
2013-11-10T21:06:46Z

On Saturday 09 November 2013, Loc Ho wrote:
> +
> +#undef XGENE_DBG_CSR /* Enable CSR read/write dumping */
> +#ifdef XGENE_DBG_CSR
> +#define XGENE_CSRDBG(fmt, args...) \
> + printk(KERN_INFO "XGENESATA: " fmt "\n", ## args);
> +#else
> +#define XGENE_CSRDBG(fmt, args...)
> +#endif

Please kill those private debug macros and use the generic interfaces.

> +void xgene_ahci_delayus(unsigned long us)
> +{
> + udelay(us);
> +}
> +
> +void xgene_ahci_delayms(unsigned long us)
> +{
> + mdelay(us);
> +}

Same for these pointless wrappers. Also every use of mdelay and ideally also udelay
needs a good explanation about why the hardware is so broken to require it or
why you cannot use msleep().

> +static int xgene_ahci_get_irq(struct platform_device *pdev, int index)
> +{
> + if (efi_enabled(EFI_BOOT))
> + return platform_get_irq(pdev, index);
> + return irq_of_parse_and_map(pdev->dev.of_node, index);
> +}
> +
> +static int xgene_ahci_get_resource(struct platform_device *pdev, int index,
> + struct resource *res)
> +{
> + struct resource *regs;
> + if (efi_enabled(EFI_BOOT)) {
> + regs = platform_get_resource(pdev, IORESOURCE_MEM, index);
> + if (regs == NULL)
> + return -ENODEV;
> + *res = *regs;
> + return 0;
> + }
> + return of_address_to_resource(pdev->dev.of_node, index, res);
> +}

These wrappers look wrong. Why can't you always use platform_get_irq/platform_get_resource?
What does the use of the of_* interfaces have to do with EFI?

> +static int xgene_ahci_get_u32_param(struct platform_device *pdev,
> + const char *of_name, char *acpi_name,
> + u32 *param)
> +{
> +#ifdef CONFIG_ACPI
> + if (efi_enabled(EFI_BOOT)) {
> + unsigned long long value;
> + acpi_status status;
> + if (acpi_name == NULL)
> + return -ENODEV;
> + status = acpi_evaluate_integer(ACPI_HANDLE(&pdev->dev),
> + acpi_name, NULL, &value);
> + if (ACPI_FAILURE(status))
> + return -ENODEV;
> + *param = value;
> + return 0;
> + }
> +#endif
> + if (of_name == NULL)
> + return -ENODEV;
> + return of_property_read_u32(pdev->dev.of_node, of_name, param);
> +}

This is worse. A device driver is *not* the place to put abstractions for
the bindings, those belong into common code.
Also it seems you are trying to do register-level programming based on
attributes you pull out of the respective firmware interfaces.

While there has been some progress regarding getting a common direction
for embedded x86 machines using ACPI, you should not assume that the same
makes sense for ARM systems.

* In case of the Open Firmware DT, the model is that we use per-subsystem
bindings to describe the hardware in an abstract way, e.g. using the
PHY controller, reset controller, clock controller, ... APIs. This is
how we can handle complex SoC-type systems.

* In case of a server-class ACPI based machine, the model is that those
details are handled by the firmware and you don't need to bother the
device driver with them.

> + rc = xgene_ahci_get_str_param(pdev, "clock-names", "CLNM", res_name,
> + sizeof(res_name));
> + if (rc) {
> + dev_err(&pdev->dev, "no clock name resource\n");
> + goto error;
> + }

As mentioned in the other mail, the driver must not read the "clock-names"
property but have a hardcoded name for the clock (or use NULL for simplicity).

> + if (strcmp(res_name, "eth01clk") == 0)
> + hpriv->cid = 0;
> + else if (strcmp(res_name, "eth23clk") == 0)
> + hpriv->cid = 1;
> + else
> + hpriv->cid = 2;
> +
> + if (hpriv->cid == 2) {
> + rc = xgene_ahci_get_resource(pdev, 2, &res);
> + if (rc != 0) {
> + dev_err(&pdev->dev, "no SATA/PCIE resource address\n");
> + goto error;
> + }
> + hpriv->pcie_base = devm_ioremap(&pdev->dev, res.start,
> + resource_size(&res));
> + if (!hpriv->pcie_base) {
> + dev_err(&pdev->dev, "can't map SATA/PCIe resource\n");
> + rc = -ENOMEM;
> + goto error;
> + }
> + }

This seems to incorrectly rely on side-effects of which particular clock
gets used. What are you trying to do here?

> + /* Custom Serdes override paraemter */
> + rc = xgene_ahci_get_u32_param(pdev, "gen-sel", "GENS", &gen_sel);
> + if (rc != 0)
> + gen_sel = 3; /* Default to Gen3 */
> + rc = xgene_ahci_get_u32_param(pdev, "serdes-diff-clk", "SDCL",
> + &serdes_diff_clk);
> + if (rc != 0)
> + serdes_diff_clk = SATA_CLK_EXT_DIFF; /* Default to external */
> + rc = xgene_ahci_get_u32_param(pdev, "EQA1", "EQA1",
> + &hpriv->ctrl_eq_A1);
> + if (rc != 0)
> + hpriv->ctrl_eq_A1 = CTLE_EQ;
> + rc = xgene_ahci_get_u32_param(pdev, "EQ", "EQ00", &hpriv->ctrl_eq);
> + if (rc != 0)
> + hpriv->ctrl_eq = CTLE_EQ_A2;
> + dev_dbg(&pdev->dev, "SATA%d ctrl_eq %u %u\n", hpriv->cid,
> + hpriv->ctrl_eq_A1, hpriv->ctrl_eq);
> + rc = xgene_ahci_get_u32_param(pdev, "GENAVG", "GAVG",
> + &hpriv->use_gen_avg);
> + if (rc != 0)
> + hpriv->use_gen_avg = xgene_ahci_is_A1() ? 0 : 1;
> + dev_dbg(&pdev->dev, "SATA%d use avg %u\n", hpriv->cid,
> + hpriv->use_gen_avg);
> + rc = xgene_ahci_get_u32_param(pdev, "LBA1", "LBA1",
> + &hpriv->loopback_buf_en_A1);
> + if (rc != 0)
> + hpriv->loopback_buf_en_A1 = 1;
> + rc = xgene_ahci_get_u32_param(pdev, "LB", "LB00",
> + &hpriv->loopback_buf_en);
> + if (rc != 0)
> + hpriv->loopback_buf_en = 0;
> + dev_dbg(&pdev->dev, "SATA%d loopback_buf_en %u %u\n", hpriv->cid,
> + hpriv->loopback_buf_en_A1, hpriv->loopback_buf_en);
> + rc = xgene_ahci_get_u32_param(pdev, "LCA1", "LCA1",
> + &hpriv->loopback_ena_ctle_A1);
> + if (rc != 0)
> + hpriv->loopback_ena_ctle_A1 = 1;
> + rc = xgene_ahci_get_u32_param(pdev, "LC", "LC00",
> + &hpriv->loopback_ena_ctle);
> + if (rc != 0)
> + hpriv->loopback_ena_ctle = 0;
> + dev_dbg(&pdev->dev, "SATA%d loopback_ena_ctle %u %u\n", hpriv->cid,
> + hpriv->loopback_ena_ctle_A1, hpriv->loopback_ena_ctle);
> + rc = xgene_ahci_get_u32_param(pdev, "CDRA1", "CDR1",
> + &hpriv->spd_sel_cdr_A1);
> + if (rc != 0)
> + hpriv->spd_sel_cdr_A1 = SPD_SEL;
> + rc = xgene_ahci_get_u32_param(pdev, "CDR", "CDR0",
> + &hpriv->spd_sel_cdr);
> + if (rc != 0)
> + hpriv->spd_sel_cdr = SPD_SEL;
> + dev_dbg(&pdev->dev, "SATA%d spd_sel_cdr %u %u\n", hpriv->cid,
> + hpriv->spd_sel_cdr_A1, hpriv->spd_sel_cdr);
> + rc = xgene_ahci_get_u32_param(pdev, "PQA1", "PQA1", &hpriv->pq_A1);
> + if (rc != 0)
> + hpriv->pq_A1 = PQ_REG;
> + rc = xgene_ahci_get_u32_param(pdev, "PQ", "PQ00", &hpriv->pq);

All this this crap can probably just go away if you have a proper PHY driver
(in case of DT) or handle it in the firmware (in case of a server).

> + if (rc != 0)
> + hpriv->pq = PQ_REG_A2;
> + hpriv->pq_sign = 0x1;
> + dev_dbg(&pdev->dev, "SATA%d pq %u %u %d\n", hpriv->cid, hpriv->pq_A1,
> + hpriv->pq, hpriv->pq_sign);
> + rc = xgene_ahci_get_u32_param(pdev, "coherent", "COHT",
> + &hpriv->coherent);

Coherency should be managed by the DMA-mapping API, don't introduce custom
properties for this.

> + /* Setup DMA mask */
> + pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
> + pdev->dev.coherent_dma_mask = DMA_BIT_MASK(64);

Drivers are not allowed to touch these fields directly, you have to use
dma_set_mask/dma_set_coherent_mask so the architecture code can check
that the bus is actually 64-bit capable.

> +static int xgene_ahci_remove(struct platform_device *pdev)
> +{
> + struct ata_host *host = dev_get_drvdata(&pdev->dev);
> + struct xgene_ahci_context *hpriv = host->private_data;
> +
> + dev_dbg(&pdev->dev, "SATA%d remove\n", hpriv->cid);
> + devm_kfree(&pdev->dev, hpriv);
> + return 0;
> +}

The devm_kfree seems pointless here.

Arnd