diff mbox

[U-Boot,27/29] dm: ahci: Add a driver for SCSI on AHCI

Message ID 20170605191517.12376-28-sjg@chromium.org
State Superseded
Delegated to: Simon Glass
Headers show

Commit Message

Simon Glass June 5, 2017, 7:15 p.m. UTC
Some AHCI drivers use SCSI under the hood. Rather than making the AHCI
driver be in the SCSI uclass it makes sense to have the AHCI device create
a SCSI device as a child. That way we can handle any AHCI-specific
operations rather than trying to pretend tha the device is just SCSI.

To handle this we need to provide a way for AHCI drivers to bind a SCSI
device as its child, and probe it. Add functions for this.

Signed-off-by: Simon Glass <sjg@chromium.org>
---

 drivers/ata/ahci.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 include/ahci.h     | 22 ++++++++++++++++++++++
 2 files changed, 74 insertions(+)

Comments

Bin Meng June 13, 2017, 3:14 a.m. UTC | #1
Hi Simon,

On Tue, Jun 6, 2017 at 3:15 AM, Simon Glass <sjg@chromium.org> wrote:
> Some AHCI drivers use SCSI under the hood. Rather than making the AHCI
> driver be in the SCSI uclass it makes sense to have the AHCI device create
> a SCSI device as a child. That way we can handle any AHCI-specific
> operations rather than trying to pretend tha the device is just SCSI.
>
> To handle this we need to provide a way for AHCI drivers to bind a SCSI
> device as its child, and probe it. Add functions for this.
>
> Signed-off-by: Simon Glass <sjg@chromium.org>
> ---
>
>  drivers/ata/ahci.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>  include/ahci.h     | 22 ++++++++++++++++++++++
>  2 files changed, 74 insertions(+)
>
> diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
> index 3528a1f3da..c67a144f02 100644
> --- a/drivers/ata/ahci.c
> +++ b/drivers/ata/ahci.c
> @@ -23,6 +23,8 @@
>  #include <libata.h>
>  #include <linux/ctype.h>
>  #include <ahci.h>
> +#include <dm/device-internal.h>
> +#include <dm/lists.h>
>
>  static int ata_io_flush(struct ahci_uc_priv *uc_priv, u8 port);
>
> @@ -1142,10 +1144,60 @@ static int ahci_scsi_bus_reset(struct udevice *dev)
>  }
>
>  #ifdef CONFIG_DM_SCSI
> +int ahci_bind_scsi(struct udevice *ahci_dev, struct udevice **devp)
> +{
> +       struct udevice *dev;
> +       int ret;
> +
> +       ret = device_bind_driver(ahci_dev, "ahci_scsi", "ahci_scsi", &dev);
> +       if (ret)
> +               return ret;
> +       *devp = dev;
> +
> +       return 0;
> +}
> +
> +int ahci_probe_scsi(struct udevice *ahci_dev)
> +{
> +       struct ahci_uc_priv *uc_priv;
> +       struct scsi_platdata *uc_plat;
> +       struct udevice *dev;
> +       int ret;
> +
> +       device_find_first_child(ahci_dev, &dev);
> +       if (!dev)
> +               return -ENODEV;
> +       uc_plat = dev_get_uclass_platdata(dev);
> +       uc_plat->base = (ulong)dm_pci_map_bar(ahci_dev, PCI_BASE_ADDRESS_5,
> +                                             PCI_REGION_MEM);

This assumes AHCI controller is on PCI bus. How about other AHCI
controllers integrated directly into the SoC?

> +       uc_plat->max_lun = 1;
> +       uc_plat->max_id = 2;
> +       uc_priv = dev_get_uclass_priv(dev);
> +       ret = ahci_init_one(uc_priv, dev);
> +       if (ret)
> +               return ret;
> +       ret = ahci_start_ports(uc_priv);
> +       if (ret)
> +               return ret;
> +
> +       debug("Scanning %s\n", dev->name);
> +       ret = scsi_scan_dev(dev, true);
> +       if (ret)
> +               return ret;
> +
> +       return 0;
> +}
> +
>  struct scsi_ops scsi_ops = {
>         .exec           = ahci_scsi_exec,
>         .bus_reset      = ahci_scsi_bus_reset,
>  };
> +
> +U_BOOT_DRIVER(ahci_scsi) = {
> +       .name           = "ahci_scsi",
> +       .id             = UCLASS_SCSI,
> +       .ops            = &scsi_ops,
> +};
>  #else
>  int scsi_exec(struct udevice *dev, struct scsi_cmd *pccb)
>  {
> diff --git a/include/ahci.h b/include/ahci.h
> index 648e56a4cf..746bff083a 100644
> --- a/include/ahci.h
> +++ b/include/ahci.h
> @@ -203,4 +203,26 @@ int achi_start_ports_dm(struct udevice *dev);
>   */
>  int ahci_init_dm(struct udevice *dev, void __iomem *base);
>
> +/**
> + * ahci_bind_scsi() - bind a new SCSI bus as a child
> + *
> + * Note that the SCSI bus device will itself bind block devices
> + *
> + * @ahci_dev: AHCI parent device
> + * @devp: Returns new SCSI bus device
> + * @return 0 if OK, -ve on error
> + */
> +int ahci_bind_scsi(struct udevice *ahci_dev, struct udevice **devp);
> +
> +/**
> + * ahci_probe_scsi() - probe and scan the attached SCSI bus
> + *
> + * Note that the SCSI device will itself bind block devices for any storage
> + * devices it finds.
> + *
> + * @ahci_dev: AHCI parent device
> + * @return 0 if OK, -ve on error
> + */
> +int ahci_probe_scsi(struct udevice *ahci_dev);
> +
>  #endif
> --

Regards,
Bin
Simon Glass June 14, 2017, 10:59 a.m. UTC | #2
Hi Bin,

On 12 June 2017 at 21:14, Bin Meng <bmeng.cn@gmail.com> wrote:
> Hi Simon,
>
> On Tue, Jun 6, 2017 at 3:15 AM, Simon Glass <sjg@chromium.org> wrote:
>> Some AHCI drivers use SCSI under the hood. Rather than making the AHCI
>> driver be in the SCSI uclass it makes sense to have the AHCI device create
>> a SCSI device as a child. That way we can handle any AHCI-specific
>> operations rather than trying to pretend tha the device is just SCSI.
>>
>> To handle this we need to provide a way for AHCI drivers to bind a SCSI
>> device as its child, and probe it. Add functions for this.
>>
>> Signed-off-by: Simon Glass <sjg@chromium.org>
>> ---
>>
>>  drivers/ata/ahci.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>>  include/ahci.h     | 22 ++++++++++++++++++++++
>>  2 files changed, 74 insertions(+)
>>
>> diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
>> index 3528a1f3da..c67a144f02 100644
>> --- a/drivers/ata/ahci.c
>> +++ b/drivers/ata/ahci.c
>> @@ -23,6 +23,8 @@
>>  #include <libata.h>
>>  #include <linux/ctype.h>
>>  #include <ahci.h>
>> +#include <dm/device-internal.h>
>> +#include <dm/lists.h>
>>
>>  static int ata_io_flush(struct ahci_uc_priv *uc_priv, u8 port);
>>
>> @@ -1142,10 +1144,60 @@ static int ahci_scsi_bus_reset(struct udevice *dev)
>>  }
>>
>>  #ifdef CONFIG_DM_SCSI
>> +int ahci_bind_scsi(struct udevice *ahci_dev, struct udevice **devp)
>> +{
>> +       struct udevice *dev;
>> +       int ret;
>> +
>> +       ret = device_bind_driver(ahci_dev, "ahci_scsi", "ahci_scsi", &dev);
>> +       if (ret)
>> +               return ret;
>> +       *devp = dev;
>> +
>> +       return 0;
>> +}
>> +
>> +int ahci_probe_scsi(struct udevice *ahci_dev)
>> +{
>> +       struct ahci_uc_priv *uc_priv;
>> +       struct scsi_platdata *uc_plat;
>> +       struct udevice *dev;
>> +       int ret;
>> +
>> +       device_find_first_child(ahci_dev, &dev);
>> +       if (!dev)
>> +               return -ENODEV;
>> +       uc_plat = dev_get_uclass_platdata(dev);
>> +       uc_plat->base = (ulong)dm_pci_map_bar(ahci_dev, PCI_BASE_ADDRESS_5,
>> +                                             PCI_REGION_MEM);
>
> This assumes AHCI controller is on PCI bus. How about other AHCI
> controllers integrated directly into the SoC?

I have not done this yet - it would have to be a follow-up series.
This one focuses mostly on SCSI. The ahci.c driver seems to me to be
only supporting using an AHCI controller via SCSI. I added a comment
to the drop to make that clear once I figured it out.

Also sata.h will need converting and the AHCI uclass will need to have
operations, etc.

Regards,
Simon
diff mbox

Patch

diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index 3528a1f3da..c67a144f02 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -23,6 +23,8 @@ 
 #include <libata.h>
 #include <linux/ctype.h>
 #include <ahci.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
 
 static int ata_io_flush(struct ahci_uc_priv *uc_priv, u8 port);
 
@@ -1142,10 +1144,60 @@  static int ahci_scsi_bus_reset(struct udevice *dev)
 }
 
 #ifdef CONFIG_DM_SCSI
+int ahci_bind_scsi(struct udevice *ahci_dev, struct udevice **devp)
+{
+	struct udevice *dev;
+	int ret;
+
+	ret = device_bind_driver(ahci_dev, "ahci_scsi", "ahci_scsi", &dev);
+	if (ret)
+		return ret;
+	*devp = dev;
+
+	return 0;
+}
+
+int ahci_probe_scsi(struct udevice *ahci_dev)
+{
+	struct ahci_uc_priv *uc_priv;
+	struct scsi_platdata *uc_plat;
+	struct udevice *dev;
+	int ret;
+
+	device_find_first_child(ahci_dev, &dev);
+	if (!dev)
+		return -ENODEV;
+	uc_plat = dev_get_uclass_platdata(dev);
+	uc_plat->base = (ulong)dm_pci_map_bar(ahci_dev, PCI_BASE_ADDRESS_5,
+					      PCI_REGION_MEM);
+	uc_plat->max_lun = 1;
+	uc_plat->max_id = 2;
+	uc_priv = dev_get_uclass_priv(dev);
+	ret = ahci_init_one(uc_priv, dev);
+	if (ret)
+		return ret;
+	ret = ahci_start_ports(uc_priv);
+	if (ret)
+		return ret;
+
+	debug("Scanning %s\n", dev->name);
+	ret = scsi_scan_dev(dev, true);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
 struct scsi_ops scsi_ops = {
 	.exec		= ahci_scsi_exec,
 	.bus_reset	= ahci_scsi_bus_reset,
 };
+
+U_BOOT_DRIVER(ahci_scsi) = {
+	.name		= "ahci_scsi",
+	.id		= UCLASS_SCSI,
+	.ops		= &scsi_ops,
+};
 #else
 int scsi_exec(struct udevice *dev, struct scsi_cmd *pccb)
 {
diff --git a/include/ahci.h b/include/ahci.h
index 648e56a4cf..746bff083a 100644
--- a/include/ahci.h
+++ b/include/ahci.h
@@ -203,4 +203,26 @@  int achi_start_ports_dm(struct udevice *dev);
  */
 int ahci_init_dm(struct udevice *dev, void __iomem *base);
 
+/**
+ * ahci_bind_scsi() - bind a new SCSI bus as a child
+ *
+ * Note that the SCSI bus device will itself bind block devices
+ *
+ * @ahci_dev: AHCI parent device
+ * @devp: Returns new SCSI bus device
+ * @return 0 if OK, -ve on error
+ */
+int ahci_bind_scsi(struct udevice *ahci_dev, struct udevice **devp);
+
+/**
+ * ahci_probe_scsi() - probe and scan the attached SCSI bus
+ *
+ * Note that the SCSI device will itself bind block devices for any storage
+ * devices it finds.
+ *
+ * @ahci_dev: AHCI parent device
+ * @return 0 if OK, -ve on error
+ */
+int ahci_probe_scsi(struct udevice *ahci_dev);
+
 #endif