diff mbox series

Signed-off-by: Runa Guo-oc <RunaGuo-oc@zhaoxin.com>

Message ID 1686901766-4928-1-git-send-email-RunaGuo-oc@zhaoxin.com
State New
Headers show
Series Signed-off-by: Runa Guo-oc <RunaGuo-oc@zhaoxin.com> | expand

Commit Message

Runa Guo-oc June 16, 2023, 7:49 a.m. UTC
[PATCH] ata:sata_zhaoxin: Add support for ZhaoXin Serial ATA

Add ZhaoXin Serial ATA support for ZhaoXin CUPs.

Signed-off-by: Runa Guo-oc <RunaGuo-oc@zhaoxin.com>
---
 drivers/ata/Kconfig        |   8 +
 drivers/ata/Makefile       |   1 +
 drivers/ata/sata_zhaoxin.c | 384 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 393 insertions(+)
 create mode 100644 drivers/ata/sata_zhaoxin.c

Comments

Damien Le Moal June 16, 2023, 8:34 a.m. UTC | #1
On 6/16/23 16:49, Runa Guo-oc wrote:
> [PATCH] ata:sata_zhaoxin: Add support for ZhaoXin Serial ATA

Broken patch: the email subject is your SoB instead of the above line, which
should not be part of the message (it should be the subject). Please reformat
and resend.

> 
> Add ZhaoXin Serial ATA support for ZhaoXin CUPs.

What is "ZhaoXin" ?
And what is "CUPs" ? Please spell this out.

> 
> Signed-off-by: Runa Guo-oc <RunaGuo-oc@zhaoxin.com>
> ---
>  drivers/ata/Kconfig        |   8 +
>  drivers/ata/Makefile       |   1 +
>  drivers/ata/sata_zhaoxin.c | 384 +++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 393 insertions(+)
>  create mode 100644 drivers/ata/sata_zhaoxin.c
> 
> diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
> index 42b51c9..ae327f3 100644
> --- a/drivers/ata/Kconfig
> +++ b/drivers/ata/Kconfig
> @@ -553,6 +553,14 @@ config SATA_VITESSE
>  
>  	  If unsure, say N.
>  
> +config SATA_ZHAOXIN
> +	tristate "ZhaoXin SATA support"
> +	depends on PCI
> +	help
> +	  This option enables support for ZhaoXin Serial ATA.
> +
> +	  If unsure, say N.
> +
>  comment "PATA SFF controllers with BMDMA"
>  
>  config PATA_ALI
> diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
> index 20e6645..4b84669 100644
> --- a/drivers/ata/Makefile
> +++ b/drivers/ata/Makefile
> @@ -45,6 +45,7 @@ obj-$(CONFIG_SATA_SIL)		+= sata_sil.o
>  obj-$(CONFIG_SATA_SIS)		+= sata_sis.o
>  obj-$(CONFIG_SATA_SVW)		+= sata_svw.o
>  obj-$(CONFIG_SATA_ULI)		+= sata_uli.o
> +obj-$(CONFIG_SATA_ZHAOXIN)	+= sata_zhaoxin.o

Please sort this alphabetically.

>  obj-$(CONFIG_SATA_VIA)		+= sata_via.o
>  obj-$(CONFIG_SATA_VITESSE)	+= sata_vsc.o
>  
> diff --git a/drivers/ata/sata_zhaoxin.c b/drivers/ata/sata_zhaoxin.c
> new file mode 100644
> index 0000000..ef8c73a
> --- /dev/null
> +++ b/drivers/ata/sata_zhaoxin.c
> @@ -0,0 +1,384 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + *  sata_zhaoxin.c - ZhaoXin Serial ATA controllers
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/pci.h>
> +#include <linux/blkdev.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <scsi/scsi.h>
> +#include <scsi/scsi_cmnd.h>
> +#include <scsi/scsi_host.h>
> +#include <linux/libata.h>
> +
> +#define DRV_NAME	"sata_zx"
> +#define DRV_VERSION	"2.6.1"

Version is not needed. The driver comes with the kernel...

> +
> +enum board_ids_enum {
> +	zx100s,
> +};
> +
> +enum {
> +	SATA_CHAN_ENAB		= 0x40, /* SATA channel enable */
> +	SATA_INT_GATE		= 0x41, /* SATA interrupt gating */
> +	SATA_NATIVE_MODE	= 0x42, /* Native mode enable */
> +	PATA_UDMA_TIMING	= 0xB3, /* PATA timing for DMA/ cable detect */
> +	PATA_PIO_TIMING		= 0xAB, /* PATA timing register */
> +
> +	PORT0			= (1 << 1),
> +	PORT1			= (1 << 0),
> +	ALL_PORTS		= PORT0 | PORT1,
> +
> +	NATIVE_MODE_ALL		= (1 << 7) | (1 << 6) | (1 << 5) | (1 << 4),
> +
> +	SATA_EXT_PHY		= (1 << 6), /* 0==use PATA, 1==ext phy */
> +};
> +
> +static int zx_init_one(struct pci_dev *pdev, const struct pci_device_id *ent);
> +static int zx_scr_read(struct ata_link *link, unsigned int scr, u32 *val);
> +static int zx_scr_write(struct ata_link *link, unsigned int scr, u32 val);
> +static int zx_hardreset(struct ata_link *link, unsigned int *class,
> +				unsigned long deadline);
> +
> +static void zx_tf_load(struct ata_port *ap, const struct ata_taskfile *tf);
> +
> +static const struct pci_device_id zx_pci_tbl[] = {
> +	{ PCI_VDEVICE(ZHAOXIN, 0x9002), zx100s },
> +	{ PCI_VDEVICE(ZHAOXIN, 0x9003), zx100s },
> +

Blank line not needed.

> +	{ }	/* terminate list */

Comment not needed.

> +};
> +
> +static struct pci_driver zx_pci_driver = {
> +	.name			= DRV_NAME,
> +	.id_table		= zx_pci_tbl,
> +	.probe			= zx_init_one,
> +#ifdef CONFIG_PM_SLEEP
> +	.suspend		= ata_pci_device_suspend,
> +	.resume			= ata_pci_device_resume,
> +#endif
> +	.remove			= ata_pci_remove_one,
> +};
> +
> +static struct scsi_host_template zx_sht = {
> +	ATA_BMDMA_SHT(DRV_NAME),
> +};
> +
> +static struct ata_port_operations zx_base_ops = {
> +	.inherits		= &ata_bmdma_port_ops,
> +	.sff_tf_load		= zx_tf_load,
> +};
> +
> +static struct ata_port_operations zx_ops = {
> +	.inherits		= &zx_base_ops,
> +	.hardreset		= zx_hardreset,
> +	.scr_read		= zx_scr_read,
> +	.scr_write		= zx_scr_write,
> +};
> +
> +static struct ata_port_info zx100s_port_info = {
> +	.flags		= ATA_FLAG_SATA | ATA_FLAG_SLAVE_POSS,
> +	.pio_mask	= ATA_PIO4,
> +	.mwdma_mask	= ATA_MWDMA2,
> +	.udma_mask	= ATA_UDMA6,
> +	.port_ops	= &zx_ops,
> +};
> +
> +

Extra blank line not needed.

> +static int zx_hardreset(struct ata_link *link, unsigned int *class,
> +				unsigned long deadline)

Please align the arguments together.

> +{
> +	int rc;
> +
> +	rc = sata_std_hardreset(link, class, deadline);
> +	if (!rc || rc == -EAGAIN) {
> +		struct ata_port *ap = link->ap;
> +		int pmp = link->pmp;
> +		int tmprc;
> +
> +		if (pmp) {
> +			ap->ops->sff_dev_select(ap, pmp);
> +			tmprc = ata_sff_wait_ready(&ap->link, deadline);
> +		} else {
> +			tmprc = ata_sff_wait_ready(link, deadline);
> +		}
> +		if (tmprc)
> +			ata_link_err(link, "COMRESET failed for wait (errno=%d)\n",
> +					rc);
> +		else
> +			ata_link_err(link, "wait for bsy success\n");

Remove this. If it worked, be silent.

> +
> +		ata_link_err(link, "COMRESET success (errno=%d) ap=%d link %d\n",
> +					rc, link->ap->port_no, link->pmp);
> +	} else {
> +		ata_link_err(link, "COMRESET failed (errno=%d) ap=%d link %d\n",
> +					rc, link->ap->port_no, link->pmp);

Reverse the if condition and exit early for this case. That will make the
function code nicer. And you can drop the error message as well since
sata_std_hardreset() prints one.

> +	}
> +	return rc;
> +}
> +
> +static int zx_scr_read(struct ata_link *link, unsigned int scr, u32 *val)
> +{
> +	static const u8 ipm_tbl[] = { 1, 2, 6, 0 };
> +	struct pci_dev *pdev = to_pci_dev(link->ap->host->dev);
> +	int slot = 2 * link->ap->port_no + link->pmp;
> +	u32 v = 0;
> +	u8 raw;
> +
> +	switch (scr) {
> +	case SCR_STATUS:
> +		pci_read_config_byte(pdev, 0xA0 + slot, &raw);
> +
> +		/* read the DET field, bit0 and 1 of the config byte */
> +		v |= raw & 0x03;
> +
> +		/* read the SPD field, bit4 of the configure byte */
> +		v |= raw & 0x30;
> +
> +		/* read the IPM field, bit2 and 3 of the config byte */
> +		v |= ((ipm_tbl[(raw >> 2) & 0x3])<<8);
> +		break;
> +
> +	case SCR_ERROR:
> +		/* devices other than 5287 uses 0xA8 as base */
> +		WARN_ON(pdev->device != 0x9002 && pdev->device != 0x9003);
> +		pci_write_config_byte(pdev, 0x42, slot);
> +		pci_read_config_dword(pdev, 0xA8, &v);
> +		break;
> +
> +	case SCR_CONTROL:
> +		pci_read_config_byte(pdev, 0xA4 + slot, &raw);
> +
> +		/* read the DET field, bit0 and bit1 */
> +		v |= ((raw & 0x02) << 1) | (raw & 0x01);
> +
> +		/* read the IPM field, bit2 and bit3 */
> +		v |= ((raw >> 2) & 0x03) << 8;
> +

remove this blank line.

> +		break;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	*val = v;
> +	return 0;
> +}
> +
> +static int zx_scr_write(struct ata_link *link, unsigned int scr, u32 val)
> +{
> +	struct pci_dev *pdev = to_pci_dev(link->ap->host->dev);
> +	int slot = 2 * link->ap->port_no + link->pmp;
> +	u32 v = 0;
> +
> +	WARN_ON(pdev == NULL);

Warning about a null pointer and still dereferenceing it below is useless. The
kernel will crash... This should not happen, right ? So remove this.

> +
> +	switch (scr) {
> +	case SCR_ERROR:
> +		/* devices 0x9002 uses 0xA8 as base */
> +		WARN_ON(pdev->device != 0x9002 && pdev->device != 0x9003);
> +		pci_write_config_byte(pdev, 0x42, slot);
> +		pci_write_config_dword(pdev, 0xA8, val);
> +		return 0;
> +
> +	case SCR_CONTROL:
> +		/* set the DET field */
> +		v |= ((val & 0x4) >> 1) | (val & 0x1);
> +
> +		/* set the IPM field */
> +		v |= ((val >> 8) & 0x3) << 2;
> +
> +
> +		pci_write_config_byte(pdev, 0xA4 + slot, v);
> +
> +

Way too many blank lines.

> +		return 0;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +
> +/**
> + *	zx_tf_load - send taskfile registers to host controller
> + *	@ap: Port to which output is sent
> + *	@tf: ATA taskfile register set
> + *
> + *	Outputs ATA taskfile to standard ATA host controller.
> + *
> + *	This is to fix the internal bug of zx chipsets, which will
> + *	reset the device register after changing the IEN bit on ctl
> + *	register.
> + */
> +static void zx_tf_load(struct ata_port *ap, const struct ata_taskfile *tf)
> +{
> +	struct ata_taskfile ttf;
> +
> +	if (tf->ctl != ap->last_ctl)  {
> +		ttf = *tf;
> +		ttf.flags |= ATA_TFLAG_DEVICE;
> +		tf = &ttf;

This is very strange... Why the need for the extra local copy ? A comment would
be nice.

> +	}
> +	ata_sff_tf_load(ap, tf);
> +}
> +
> +static const unsigned int zx_bar_sizes[] = {
> +	8, 4, 8, 4, 16, 256
> +};
> +
> +static const unsigned int zx100s_bar_sizes0[] = {
> +	8, 4, 8, 4, 16, 0
> +};
> +
> +static const unsigned int zx100s_bar_sizes1[] = {
> +	8, 4, 0, 0, 16, 0
> +};
> +
> +static int zx_prepare_host(struct pci_dev *pdev, struct ata_host **r_host)
> +{
> +	const struct ata_port_info *ppi0[] = {
> +		&zx100s_port_info, NULL
> +	};
> +	const struct ata_port_info *ppi1[] = {
> +		&zx100s_port_info, &ata_dummy_port_info
> +	};
> +	struct ata_host *host;
> +	int i, rc;
> +
> +	if (pdev->device == 0x9002)
> +		rc = ata_pci_bmdma_prepare_host(pdev, ppi0, &host);
> +	else if (pdev->device == 0x9003)
> +		rc = ata_pci_bmdma_prepare_host(pdev, ppi1, &host);
> +	else
> +		rc = -EINVAL;
> +

Remove the blank line here.

> +	if (rc)
> +		return rc;
> +
> +	*r_host = host;
> +
> +	/* 9002 hosts four sata ports as M/S of the two channels */
> +	/* 9003 hosts two sata ports as M/S of the one channel */

Multi-line comment format:

	/*
 	 * ...
	 * ...
	 */

> +	for (i = 0; i < host->n_ports; i++)
> +		ata_slave_link_init(host->ports[i]);
> +
> +	return 0;
> +}
> +
> +static void zx_configure(struct pci_dev *pdev, int board_id)
> +{
> +	u8 tmp8;
> +
> +	pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &tmp8);
> +	dev_info(&pdev->dev, "routed to hard irq line %d\n",
> +		 (int) (tmp8 & 0xf0) == 0xf0 ? 0 : tmp8 & 0x0f);
> +
> +	/* make sure SATA channels are enabled */
> +	pci_read_config_byte(pdev, SATA_CHAN_ENAB, &tmp8);
> +	if ((tmp8 & ALL_PORTS) != ALL_PORTS) {
> +		dev_dbg(&pdev->dev, "enabling SATA channels (0x%x)\n",
> +			(int)tmp8);
> +		tmp8 |= ALL_PORTS;
> +		pci_write_config_byte(pdev, SATA_CHAN_ENAB, tmp8);
> +	}
> +
> +	/* make sure interrupts for each channel sent to us */
> +	pci_read_config_byte(pdev, SATA_INT_GATE, &tmp8);
> +	if ((tmp8 & ALL_PORTS) != ALL_PORTS) {
> +		dev_dbg(&pdev->dev, "enabling SATA channel interrupts (0x%x)\n",
> +			(int) tmp8);
> +		tmp8 |= ALL_PORTS;
> +		pci_write_config_byte(pdev, SATA_INT_GATE, tmp8);
> +	}
> +
> +	/* make sure native mode is enabled */
> +	pci_read_config_byte(pdev, SATA_NATIVE_MODE, &tmp8);
> +	if ((tmp8 & NATIVE_MODE_ALL) != NATIVE_MODE_ALL) {
> +		dev_dbg(&pdev->dev,
> +			"enabling SATA channel native mode (0x%x)\n",
> +			(int) tmp8);
> +		tmp8 |= NATIVE_MODE_ALL;
> +		pci_write_config_byte(pdev, SATA_NATIVE_MODE, tmp8);
> +	}
> +}
> +
> +static int zx_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
> +{
> +	unsigned int i;
> +	int rc;
> +	struct ata_host *host = NULL;
> +	int board_id = (int) ent->driver_data;
> +	const unsigned int *bar_sizes;
> +	int legacy_mode = 0;
> +
> +	ata_print_version_once(&pdev->dev, DRV_VERSION);
> +
> +	if (pdev->device == 0x9002 || pdev->device == 0x9003) {
> +		if ((pdev->class >> 8) == PCI_CLASS_STORAGE_IDE) {
> +			u8 tmp8, mask;
> +
> +			/* TODO: What if one channel is in native mode ... */

I do not know... What about it ? If this is not expected to work/not supported,
then return an error.

> +			pci_read_config_byte(pdev, PCI_CLASS_PROG, &tmp8);
> +			mask = (1 << 2) | (1 << 0);
> +			if ((tmp8 & mask) != mask)
> +				legacy_mode = 1;
> +		}
> +		if (legacy_mode)
> +			return -EINVAL;
> +	}
> +
> +	rc = pcim_enable_device(pdev);
> +	if (rc)
> +		return rc;
> +
> +	if (board_id == zx100s && pdev->device == 0x9002)
> +		bar_sizes = &zx100s_bar_sizes0[0];
> +	else if (board_id == zx100s && pdev->device == 0x9003)
> +		bar_sizes = &zx100s_bar_sizes1[0];
> +	else
> +		bar_sizes = &zx_bar_sizes[0];
> +
> +	for (i = 0; i < ARRAY_SIZE(zx_bar_sizes); i++) {
> +		if ((pci_resource_start(pdev, i) == 0) ||
> +		    (pci_resource_len(pdev, i) < bar_sizes[i])) {
> +			if (bar_sizes[i] == 0)
> +				continue;
> +
> +			dev_err(&pdev->dev,
> +				"invalid PCI BAR %u (sz 0x%llx, val 0x%llx)\n",
> +				i,
> +				(unsigned long long)pci_resource_start(pdev, i),
> +				(unsigned long long)pci_resource_len(pdev, i));
> +
> +			return -ENODEV;
> +		}
> +	}
> +
> +	switch (board_id) {
> +	case zx100s:
> +		rc = zx_prepare_host(pdev, &host);
> +		break;
> +	default:
> +		rc = -EINVAL;
> +	}
> +	if (rc)
> +		return rc;
> +
> +	zx_configure(pdev, board_id);
> +
> +	pci_set_master(pdev);
> +	return ata_host_activate(host, pdev->irq, ata_bmdma_interrupt,
> +				 IRQF_SHARED, &zx_sht);
> +}
> +
> +module_pci_driver(zx_pci_driver);
> +
> +MODULE_AUTHOR("Yanchen:YanchenSun@zhaoxin.com");
> +MODULE_DESCRIPTION("SCSI low-level driver for ZX SATA controllers");

This is not a scsi driver...

> +MODULE_LICENSE("GPL");
> +MODULE_DEVICE_TABLE(pci, zx_pci_tbl);
> +MODULE_VERSION(DRV_VERSION);
Runa Guo-oc June 26, 2023, 11:29 a.m. UTC | #2
On 2023/6/16 16:34, Damien Le Moal wrote:
> On 6/16/23 16:49, Runa Guo-oc wrote:
>> [PATCH] ata:sata_zhaoxin: Add support for ZhaoXin Serial ATA
> 
> Broken patch: the email subject is your SoB instead of the above line, which
> should not be part of the message (it should be the subject). Please reformat
> and resend.
> 

Okay.

>>
>> Add ZhaoXin Serial ATA support for ZhaoXin CUPs.
> 
> What is "ZhaoXin" ?

Zhaoxin is a Chinese company that has mastered the core technology
of independent general-purpose processors and its system platform chips,
and is committed to providing users with efficient, compatible and secure
independent general-purpose processors, chipsets and other products.
for more information, you can visit here: https://www.zhaoxin.com/.

> And what is "CUPs" ? Please spell this out.
> 

Yes, this is  a spelling error.

>>
>> Signed-off-by: Runa Guo-oc <RunaGuo-oc@zhaoxin.com>
>> ---
>>  drivers/ata/Kconfig        |   8 +
>>  drivers/ata/Makefile       |   1 +
>>  drivers/ata/sata_zhaoxin.c | 384 +++++++++++++++++++++++++++++++++++++++++++++
>>  3 files changed, 393 insertions(+)
>>  create mode 100644 drivers/ata/sata_zhaoxin.c
>>
>> diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
>> index 42b51c9..ae327f3 100644
>> --- a/drivers/ata/Kconfig
>> +++ b/drivers/ata/Kconfig
>> @@ -553,6 +553,14 @@ config SATA_VITESSE
>>  
>>  	  If unsure, say N.
>>  
>> +config SATA_ZHAOXIN
>> +	tristate "ZhaoXin SATA support"
>> +	depends on PCI
>> +	help
>> +	  This option enables support for ZhaoXin Serial ATA.
>> +
>> +	  If unsure, say N.
>> +
>>  comment "PATA SFF controllers with BMDMA"
>>  
>>  config PATA_ALI
>> diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
>> index 20e6645..4b84669 100644
>> --- a/drivers/ata/Makefile
>> +++ b/drivers/ata/Makefile
>> @@ -45,6 +45,7 @@ obj-$(CONFIG_SATA_SIL)		+= sata_sil.o
>>  obj-$(CONFIG_SATA_SIS)		+= sata_sis.o
>>  obj-$(CONFIG_SATA_SVW)		+= sata_svw.o
>>  obj-$(CONFIG_SATA_ULI)		+= sata_uli.o
>> +obj-$(CONFIG_SATA_ZHAOXIN)	+= sata_zhaoxin.o
> 
> Please sort this alphabetically.
> 

Like this?
obj-$(CONFIG_SATA_VITESSE)	        += sata_vsc.o
obj-$(CONFIG_SATA_ZHAOXIN)	        += sata_zhaoxin.o

>>  obj-$(CONFIG_SATA_VIA)		+= sata_via.o
>>  obj-$(CONFIG_SATA_VITESSE)	+= sata_vsc.o
>>  
>> diff --git a/drivers/ata/sata_zhaoxin.c b/drivers/ata/sata_zhaoxin.c
>> new file mode 100644
>> index 0000000..ef8c73a
>> --- /dev/null
>> +++ b/drivers/ata/sata_zhaoxin.c
>> @@ -0,0 +1,384 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + *  sata_zhaoxin.c - ZhaoXin Serial ATA controllers
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/pci.h>
>> +#include <linux/blkdev.h>
>> +#include <linux/delay.h>
>> +#include <linux/device.h>
>> +#include <scsi/scsi.h>
>> +#include <scsi/scsi_cmnd.h>
>> +#include <scsi/scsi_host.h>
>> +#include <linux/libata.h>
>> +
>> +#define DRV_NAME	"sata_zx"
>> +#define DRV_VERSION	"2.6.1"
> 
> Version is not needed. The driver comes with the kernel...
> 

Right, I'll remove it next time.

>> +
>> +enum board_ids_enum {
>> +	zx100s,
>> +};
>> +
>> +enum {
>> +	SATA_CHAN_ENAB		= 0x40, /* SATA channel enable */
>> +	SATA_INT_GATE		= 0x41, /* SATA interrupt gating */
>> +	SATA_NATIVE_MODE	= 0x42, /* Native mode enable */
>> +	PATA_UDMA_TIMING	= 0xB3, /* PATA timing for DMA/ cable detect */
>> +	PATA_PIO_TIMING		= 0xAB, /* PATA timing register */
>> +
>> +	PORT0			= (1 << 1),
>> +	PORT1			= (1 << 0),
>> +	ALL_PORTS		= PORT0 | PORT1,
>> +
>> +	NATIVE_MODE_ALL		= (1 << 7) | (1 << 6) | (1 << 5) | (1 << 4),
>> +
>> +	SATA_EXT_PHY		= (1 << 6), /* 0==use PATA, 1==ext phy */
>> +};
>> +
>> +static int zx_init_one(struct pci_dev *pdev, const struct pci_device_id *ent);
>> +static int zx_scr_read(struct ata_link *link, unsigned int scr, u32 *val);
>> +static int zx_scr_write(struct ata_link *link, unsigned int scr, u32 val);
>> +static int zx_hardreset(struct ata_link *link, unsigned int *class,
>> +				unsigned long deadline);
>> +
>> +static void zx_tf_load(struct ata_port *ap, const struct ata_taskfile *tf);
>> +
>> +static const struct pci_device_id zx_pci_tbl[] = {
>> +	{ PCI_VDEVICE(ZHAOXIN, 0x9002), zx100s },
>> +	{ PCI_VDEVICE(ZHAOXIN, 0x9003), zx100s },
>> +
> 
> Blank line not needed.
>>> +	{ }	/* terminate list */
> 
> Comment not needed.
> 

The purpose of writing like this is convenient to add new device IDs in
the future.
Considering that this is not likely, remove it also fine.

>> +};
>> +
>> +static struct pci_driver zx_pci_driver = {
>> +	.name			= DRV_NAME,
>> +	.id_table		= zx_pci_tbl,
>> +	.probe			= zx_init_one,
>> +#ifdef CONFIG_PM_SLEEP
>> +	.suspend		= ata_pci_device_suspend,
>> +	.resume			= ata_pci_device_resume,
>> +#endif
>> +	.remove			= ata_pci_remove_one,
>> +};
>> +
>> +static struct scsi_host_template zx_sht = {
>> +	ATA_BMDMA_SHT(DRV_NAME),
>> +};
>> +
>> +static struct ata_port_operations zx_base_ops = {
>> +	.inherits		= &ata_bmdma_port_ops,
>> +	.sff_tf_load		= zx_tf_load,
>> +};
>> +
>> +static struct ata_port_operations zx_ops = {
>> +	.inherits		= &zx_base_ops,
>> +	.hardreset		= zx_hardreset,
>> +	.scr_read		= zx_scr_read,
>> +	.scr_write		= zx_scr_write,
>> +};
>> +
>> +static struct ata_port_info zx100s_port_info = {
>> +	.flags		= ATA_FLAG_SATA | ATA_FLAG_SLAVE_POSS,
>> +	.pio_mask	= ATA_PIO4,
>> +	.mwdma_mask	= ATA_MWDMA2,
>> +	.udma_mask	= ATA_UDMA6,
>> +	.port_ops	= &zx_ops,
>> +};
>> +
>> +
> 
> Extra blank line not needed.
> 

I see

>> +static int zx_hardreset(struct ata_link *link, unsigned int *class,
>> +				unsigned long deadline)
> 
> Please align the arguments together.
> 

Okay.

>> +{
>> +	int rc;
>> +
>> +	rc = sata_std_hardreset(link, class, deadline);
>> +	if (!rc || rc == -EAGAIN) {
>> +		struct ata_port *ap = link->ap;
>> +		int pmp = link->pmp;
>> +		int tmprc;
>> +
>> +		if (pmp) {
>> +			ap->ops->sff_dev_select(ap, pmp);
>> +			tmprc = ata_sff_wait_ready(&ap->link, deadline);
>> +		} else {
>> +			tmprc = ata_sff_wait_ready(link, deadline);
>> +		}
>> +		if (tmprc)
>> +			ata_link_err(link, "COMRESET failed for wait (errno=%d)\n",
>> +					rc);
>> +		else
>> +			ata_link_err(link, "wait for bsy success\n");
> 
> Remove this. If it worked, be silent.
> 

Okay.

>> +
>> +		ata_link_err(link, "COMRESET success (errno=%d) ap=%d link %d\n",
>> +					rc, link->ap->port_no, link->pmp);
>> +	} else {
>> +		ata_link_err(link, "COMRESET failed (errno=%d) ap=%d link %d\n",
>> +					rc, link->ap->port_no, link->pmp);
> 
> Reverse the if condition and exit early for this case. That will make the
> function code nicer. And you can drop the error message as well since
> sata_std_hardreset() prints one.
> 

Yes, I agree with your. I'll adjust the above codes like these?

if(!rc || rc == -EAGAIN){
                struct ata_port *ap = link->ap;
                int pmp = link->pmp;
                int tmprc;
                if(pmp){
                        ap->ops->sff_dev_select(ap,pmp);
                        tmprc=ata_sff_wait_ready(&ap->link,deadline);
                }else
                        tmprc=ata_sff_wait_ready(link,deadline);

                if(tmprc)
                        ata_link_err(link,"COMRESET failed for
wait(errno=%d)\n",rc);

                ata_link_err(link,"COMRESET success (errno=%d) ap=%d
link%d\n",
                                  rc,link->ap->port_no,link->pmp);

>> +	}
>> +	return rc;
>> +}
>> +
>> +static int zx_scr_read(struct ata_link *link, unsigned int scr, u32 *val)
>> +{
>> +	static const u8 ipm_tbl[] = { 1, 2, 6, 0 };
>> +	struct pci_dev *pdev = to_pci_dev(link->ap->host->dev);
>> +	int slot = 2 * link->ap->port_no + link->pmp;
>> +	u32 v = 0;
>> +	u8 raw;
>> +
>> +	switch (scr) {
>> +	case SCR_STATUS:
>> +		pci_read_config_byte(pdev, 0xA0 + slot, &raw);
>> +
>> +		/* read the DET field, bit0 and 1 of the config byte */
>> +		v |= raw & 0x03;
>> +
>> +		/* read the SPD field, bit4 of the configure byte */
>> +		v |= raw & 0x30;
>> +
>> +		/* read the IPM field, bit2 and 3 of the config byte */
>> +		v |= ((ipm_tbl[(raw >> 2) & 0x3])<<8);
>> +		break;
>> +
>> +	case SCR_ERROR:
>> +		/* devices other than 5287 uses 0xA8 as base */
>> +		WARN_ON(pdev->device != 0x9002 && pdev->device != 0x9003);
>> +		pci_write_config_byte(pdev, 0x42, slot);
>> +		pci_read_config_dword(pdev, 0xA8, &v);
>> +		break;
>> +
>> +	case SCR_CONTROL:
>> +		pci_read_config_byte(pdev, 0xA4 + slot, &raw);
>> +
>> +		/* read the DET field, bit0 and bit1 */
>> +		v |= ((raw & 0x02) << 1) | (raw & 0x01);
>> +
>> +		/* read the IPM field, bit2 and bit3 */
>> +		v |= ((raw >> 2) & 0x03) << 8;
>> +
> 
> remove this blank line.
> 

Okay.

>> +		break;
>> +
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +
>> +	*val = v;
>> +	return 0;
>> +}
>> +
>> +static int zx_scr_write(struct ata_link *link, unsigned int scr, u32 val)
>> +{
>> +	struct pci_dev *pdev = to_pci_dev(link->ap->host->dev);
>> +	int slot = 2 * link->ap->port_no + link->pmp;
>> +	u32 v = 0;
>> +
>> +	WARN_ON(pdev == NULL);
> 
> Warning about a null pointer and still dereferenceing it below is useless. The
> kernel will crash... This should not happen, right ? So remove this.
> 

Okay.

>> +
>> +	switch (scr) {
>> +	case SCR_ERROR:
>> +		/* devices 0x9002 uses 0xA8 as base */
>> +		WARN_ON(pdev->device != 0x9002 && pdev->device != 0x9003);
>> +		pci_write_config_byte(pdev, 0x42, slot);
>> +		pci_write_config_dword(pdev, 0xA8, val);
>> +		return 0;
>> +
>> +	case SCR_CONTROL:
>> +		/* set the DET field */
>> +		v |= ((val & 0x4) >> 1) | (val & 0x1);
>> +
>> +		/* set the IPM field */
>> +		v |= ((val >> 8) & 0x3) << 2;
>> +
>> +
>> +		pci_write_config_byte(pdev, 0xA4 + slot, v);
>> +
>> +
> 
> Way too many blank lines.
> 

I see

>> +		return 0;
>> +
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +}
>> +
>> +
>> +/**
>> + *	zx_tf_load - send taskfile registers to host controller
>> + *	@ap: Port to which output is sent
>> + *	@tf: ATA taskfile register set
>> + *
>> + *	Outputs ATA taskfile to standard ATA host controller.
>> + *
>> + *	This is to fix the internal bug of zx chipsets, which will
>> + *	reset the device register after changing the IEN bit on ctl
>> + *	register.
>> + */
>> +static void zx_tf_load(struct ata_port *ap, const struct ata_taskfile *tf)
>> +{
>> +	struct ata_taskfile ttf;
>> +
>> +	if (tf->ctl != ap->last_ctl)  {
>> +		ttf = *tf;
>> +		ttf.flags |= ATA_TFLAG_DEVICE;
>> +		tf = &ttf;
> 
> This is very strange... Why the need for the extra local copy ? A comment would
> be nice.
> 

tf, pointer to const, the content it pointed to is constant and cannot
be changed
directly.
ttf, it is a variable.
Firstly, we change its content based on *tf;
Then, make tf pointed to it;
Lastly, *tf's content will be changed undirectly.

>> +	}
>> +	ata_sff_tf_load(ap, tf);
>> +}
>> +
>> +static const unsigned int zx_bar_sizes[] = {
>> +	8, 4, 8, 4, 16, 256
>> +};
>> +
>> +static const unsigned int zx100s_bar_sizes0[] = {
>> +	8, 4, 8, 4, 16, 0
>> +};
>> +
>> +static const unsigned int zx100s_bar_sizes1[] = {
>> +	8, 4, 0, 0, 16, 0
>> +};
>> +
>> +static int zx_prepare_host(struct pci_dev *pdev, struct ata_host **r_host)
>> +{
>> +	const struct ata_port_info *ppi0[] = {
>> +		&zx100s_port_info, NULL
>> +	};
>> +	const struct ata_port_info *ppi1[] = {
>> +		&zx100s_port_info, &ata_dummy_port_info
>> +	};
>> +	struct ata_host *host;
>> +	int i, rc;
>> +
>> +	if (pdev->device == 0x9002)
>> +		rc = ata_pci_bmdma_prepare_host(pdev, ppi0, &host);
>> +	else if (pdev->device == 0x9003)
>> +		rc = ata_pci_bmdma_prepare_host(pdev, ppi1, &host);
>> +	else
>> +		rc = -EINVAL;
>> +
> 
> Remove the blank line here.
> 

Okay.

>> +	if (rc)
>> +		return rc;
>> +
>> +	*r_host = host;
>> +
>> +	/* 9002 hosts four sata ports as M/S of the two channels */
>> +	/* 9003 hosts two sata ports as M/S of the one channel */
> 
> Multi-line comment format:
> 
> 	/*
>  	 * ...
> 	 * ...
> 	 */
> 

I got it.

>> +	for (i = 0; i < host->n_ports; i++)
>> +		ata_slave_link_init(host->ports[i]);
>> +
>> +	return 0;
>> +}
>> +
>> +static void zx_configure(struct pci_dev *pdev, int board_id)
>> +{
>> +	u8 tmp8;
>> +
>> +	pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &tmp8);
>> +	dev_info(&pdev->dev, "routed to hard irq line %d\n",
>> +		 (int) (tmp8 & 0xf0) == 0xf0 ? 0 : tmp8 & 0x0f);
>> +
>> +	/* make sure SATA channels are enabled */
>> +	pci_read_config_byte(pdev, SATA_CHAN_ENAB, &tmp8);
>> +	if ((tmp8 & ALL_PORTS) != ALL_PORTS) {
>> +		dev_dbg(&pdev->dev, "enabling SATA channels (0x%x)\n",
>> +			(int)tmp8);
>> +		tmp8 |= ALL_PORTS;
>> +		pci_write_config_byte(pdev, SATA_CHAN_ENAB, tmp8);
>> +	}
>> +
>> +	/* make sure interrupts for each channel sent to us */
>> +	pci_read_config_byte(pdev, SATA_INT_GATE, &tmp8);
>> +	if ((tmp8 & ALL_PORTS) != ALL_PORTS) {
>> +		dev_dbg(&pdev->dev, "enabling SATA channel interrupts (0x%x)\n",
>> +			(int) tmp8);
>> +		tmp8 |= ALL_PORTS;
>> +		pci_write_config_byte(pdev, SATA_INT_GATE, tmp8);
>> +	}
>> +
>> +	/* make sure native mode is enabled */
>> +	pci_read_config_byte(pdev, SATA_NATIVE_MODE, &tmp8);
>> +	if ((tmp8 & NATIVE_MODE_ALL) != NATIVE_MODE_ALL) {
>> +		dev_dbg(&pdev->dev,
>> +			"enabling SATA channel native mode (0x%x)\n",
>> +			(int) tmp8);
>> +		tmp8 |= NATIVE_MODE_ALL;
>> +		pci_write_config_byte(pdev, SATA_NATIVE_MODE, tmp8);
>> +	}
>> +}
>> +
>> +static int zx_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
>> +{
>> +	unsigned int i;
>> +	int rc;
>> +	struct ata_host *host = NULL;
>> +	int board_id = (int) ent->driver_data;
>> +	const unsigned int *bar_sizes;
>> +	int legacy_mode = 0;
>> +
>> +	ata_print_version_once(&pdev->dev, DRV_VERSION);
>> +
>> +	if (pdev->device == 0x9002 || pdev->device == 0x9003) {
>> +		if ((pdev->class >> 8) == PCI_CLASS_STORAGE_IDE) {
>> +			u8 tmp8, mask;
>> +
>> +			/* TODO: What if one channel is in native mode ... */
> 
> I do not know... What about it ? If this is not expected to work/not supported,
> then return an error.
> 

Yes, you're right. Zhaoxin sata controllers do not support legacy mode.
So we return an error here.

Based on the latest kernel code, this part may be adjusted like these:

	u8 tmp8, mask = 0;
     	pci_read_config_byte(pdev, PCI_CLASS_PROG, &tmp8);
                if (!ata_port_is_dummy(host->ports[0]))
                        mask |= (1 << 0);
                if (!ata_port_is_dummy(host->ports[1]))
                        mask |= (1 << 2);
                if ((tmp8 & mask) != mask)
                        legacy_mode = 1;

>> +			pci_read_config_byte(pdev, PCI_CLASS_PROG, &tmp8);
>> +			mask = (1 << 2) | (1 << 0);
>> +			if ((tmp8 & mask) != mask)
>> +				legacy_mode = 1;
>> +		}
>> +		if (legacy_mode)
>> +			return -EINVAL;
>> +	}
>> +
>> +	rc = pcim_enable_device(pdev);
>> +	if (rc)
>> +		return rc;
>> +
>> +	if (board_id == zx100s && pdev->device == 0x9002)
>> +		bar_sizes = &zx100s_bar_sizes0[0];
>> +	else if (board_id == zx100s && pdev->device == 0x9003)
>> +		bar_sizes = &zx100s_bar_sizes1[0];
>> +	else
>> +		bar_sizes = &zx_bar_sizes[0];
>> +
>> +	for (i = 0; i < ARRAY_SIZE(zx_bar_sizes); i++) {
>> +		if ((pci_resource_start(pdev, i) == 0) ||
>> +		    (pci_resource_len(pdev, i) < bar_sizes[i])) {
>> +			if (bar_sizes[i] == 0)
>> +				continue;
>> +
>> +			dev_err(&pdev->dev,
>> +				"invalid PCI BAR %u (sz 0x%llx, val 0x%llx)\n",
>> +				i,
>> +				(unsigned long long)pci_resource_start(pdev, i),
>> +				(unsigned long long)pci_resource_len(pdev, i));
>> +
>> +			return -ENODEV;
>> +		}
>> +	}
>> +
>> +	switch (board_id) {
>> +	case zx100s:
>> +		rc = zx_prepare_host(pdev, &host);
>> +		break;
>> +	default:
>> +		rc = -EINVAL;
>> +	}
>> +	if (rc)
>> +		return rc;
>> +
>> +	zx_configure(pdev, board_id);
>> +
>> +	pci_set_master(pdev);
>> +	return ata_host_activate(host, pdev->irq, ata_bmdma_interrupt,
>> +				 IRQF_SHARED, &zx_sht);
>> +}
>> +
>> +module_pci_driver(zx_pci_driver);
>> +
>> +MODULE_AUTHOR("Yanchen:YanchenSun@zhaoxin.com");
>> +MODULE_DESCRIPTION("SCSI low-level driver for ZX SATA controllers");
> 
> This is not a scsi driver...
> 

I treat it as a scsi driver for the following reasons,  which may be not
accurate.
1, IO path: vfs->fs->block layer->scsi layer->this driver;
2, Extracted from the following link:
"SCSI Lower level drivers (LLDs) are variously called host bus adapter
(HBA) drivers and host drivers (HD)."

https://www.kernel.org/doc/html/latest/scsi/scsi_mid_low_api.html

Maybe I shall delete it next time.

>> +MODULE_LICENSE("GPL");
>> +MODULE_DEVICE_TABLE(pci, zx_pci_tbl);
>> +MODULE_VERSION(DRV_VERSION);
>
Damien Le Moal June 26, 2023, 12:40 p.m. UTC | #3
On 6/26/23 20:29, Runa Guo-oc wrote:
> On 2023/6/16 16:34, Damien Le Moal wrote:
>> On 6/16/23 16:49, Runa Guo-oc wrote:
>>> [PATCH] ata:sata_zhaoxin: Add support for ZhaoXin Serial ATA
>>
>> Broken patch: the email subject is your SoB instead of the above line, which
>> should not be part of the message (it should be the subject). Please reformat
>> and resend.
>>
> 
> Okay.
> 
>>>
>>> Add ZhaoXin Serial ATA support for ZhaoXin CUPs.
>>
>> What is "ZhaoXin" ?
> 
> Zhaoxin is a Chinese company that has mastered the core technology
> of independent general-purpose processors and its system platform chips,
> and is committed to providing users with efficient, compatible and secure
> independent general-purpose processors, chipsets and other products.
> for more information, you can visit here: https://www.zhaoxin.com/.

A company marketing message is not very informative, technically speaking. What
is this chipset and on what board/machine can it be found ? That is the more
relevant information we need in this commit message.

> 
>> And what is "CUPs" ? Please spell this out.
>>
> 
> Yes, this is  a spelling error.
> 
>>>
>>> Signed-off-by: Runa Guo-oc <RunaGuo-oc@zhaoxin.com>
>>> ---
>>>  drivers/ata/Kconfig        |   8 +
>>>  drivers/ata/Makefile       |   1 +
>>>  drivers/ata/sata_zhaoxin.c | 384 +++++++++++++++++++++++++++++++++++++++++++++
>>>  3 files changed, 393 insertions(+)
>>>  create mode 100644 drivers/ata/sata_zhaoxin.c
>>>
>>> diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
>>> index 42b51c9..ae327f3 100644
>>> --- a/drivers/ata/Kconfig
>>> +++ b/drivers/ata/Kconfig
>>> @@ -553,6 +553,14 @@ config SATA_VITESSE
>>>  
>>>  	  If unsure, say N.
>>>  
>>> +config SATA_ZHAOXIN
>>> +	tristate "ZhaoXin SATA support"

Same here. If ZhaoXin is only a company name, we need also a chipset model to be
informative regarding which HW this driver serves.

>>> +	depends on PCI
>>> +	help
>>> +	  This option enables support for ZhaoXin Serial ATA.
>>> +
>>> +	  If unsure, say N.
>>> +
>>>  comment "PATA SFF controllers with BMDMA"
>>>  
>>>  config PATA_ALI
>>> diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
>>> index 20e6645..4b84669 100644
>>> --- a/drivers/ata/Makefile
>>> +++ b/drivers/ata/Makefile
>>> @@ -45,6 +45,7 @@ obj-$(CONFIG_SATA_SIL)		+= sata_sil.o
>>>  obj-$(CONFIG_SATA_SIS)		+= sata_sis.o
>>>  obj-$(CONFIG_SATA_SVW)		+= sata_svw.o
>>>  obj-$(CONFIG_SATA_ULI)		+= sata_uli.o
>>> +obj-$(CONFIG_SATA_ZHAOXIN)	+= sata_zhaoxin.o
>>
>> Please sort this alphabetically.
>>
> 
> Like this?
> obj-$(CONFIG_SATA_VITESSE)	        += sata_vsc.o
> obj-$(CONFIG_SATA_ZHAOXIN)	        += sata_zhaoxin.o

Yes.

>>> +
>>> +		ata_link_err(link, "COMRESET success (errno=%d) ap=%d link %d\n",
>>> +					rc, link->ap->port_no, link->pmp);
>>> +	} else {
>>> +		ata_link_err(link, "COMRESET failed (errno=%d) ap=%d link %d\n",
>>> +					rc, link->ap->port_no, link->pmp);
>>
>> Reverse the if condition and exit early for this case. That will make the
>> function code nicer. And you can drop the error message as well since
>> sata_std_hardreset() prints one.
>>
> 
> Yes, I agree with your. I'll adjust the above codes like these?
> 
> if(!rc || rc == -EAGAIN){
>                 struct ata_port *ap = link->ap;
>                 int pmp = link->pmp;
>                 int tmprc;
>                 if(pmp){
>                         ap->ops->sff_dev_select(ap,pmp);
>                         tmprc=ata_sff_wait_ready(&ap->link,deadline);
>                 }else
>                         tmprc=ata_sff_wait_ready(link,deadline);
> 
>                 if(tmprc)
>                         ata_link_err(link,"COMRESET failed for
> wait(errno=%d)\n",rc);
> 
>                 ata_link_err(link,"COMRESET success (errno=%d) ap=%d
> link%d\n",
>                                   rc,link->ap->port_no,link->pmp);
> 

You did not understand my point. Doing:

	rc = sata_std_hardreset(link, class, deadline);
	if (rc && rc != -EAGAIN) {
		ata_link_err(link, "COMRESET failed (errno=%d) ap=%d link %d\n",
			     rc, link->ap->port_no, link->pmp);
		return rc;
	}

	/* rc == 0 || rc == -EAGAIN case */
	...

Makes the code much nicer.


[...]

>>> +MODULE_AUTHOR("Yanchen:YanchenSun@zhaoxin.com");
>>> +MODULE_DESCRIPTION("SCSI low-level driver for ZX SATA controllers");
>>
>> This is not a scsi driver...
>>
> 
> I treat it as a scsi driver for the following reasons,  which may be not
> accurate.
> 1, IO path: vfs->fs->block layer->scsi layer->this driver;
> 2, Extracted from the following link:
> "SCSI Lower level drivers (LLDs) are variously called host bus adapter
> (HBA) drivers and host drivers (HD)."

Again, this is not a scsi driver. Even if in Linux all ATA drives seat under the
scsi layer, this is an ATA subsytem driver for an ATA drive. Not SCSI.
Runa Guo-oc June 27, 2023, 7:17 a.m. UTC | #4
On 2023/6/26 20:40, Damien Le Moal wrote:
> On 6/26/23 20:29, Runa Guo-oc wrote:
>> On 2023/6/16 16:34, Damien Le Moal wrote:
>>> On 6/16/23 16:49, Runa Guo-oc wrote:
>>>> [PATCH] ata:sata_zhaoxin: Add support for ZhaoXin Serial ATA
>>>
>>> Broken patch: the email subject is your SoB instead of the above line, which
>>> should not be part of the message (it should be the subject). Please reformat
>>> and resend.
>>>
>>
>> Okay.
>>
>>>>
>>>> Add ZhaoXin Serial ATA support for ZhaoXin CUPs.
>>>
>>> What is "ZhaoXin" ?
>>
>> Zhaoxin is a Chinese company that has mastered the core technology
>> of independent general-purpose processors and its system platform chips,
>> and is committed to providing users with efficient, compatible and secure
>> independent general-purpose processors, chipsets and other products.
>> for more information, you can visit here: https://www.zhaoxin.com/.
> 
> A company marketing message is not very informative, technically speaking. What
> is this chipset and on what board/machine can it be found ? That is the more
> relevant information we need in this commit message.
> 

The reason why it is called Zhaoxin SATA is actually to express that it
applies
to all Zhaoxin support of its separate chipset/SOC, for example,
ZX-100S/ZX-200
chipsets.

>>
>>> And what is "CUPs" ? Please spell this out.
>>>
>>
>> Yes, this is  a spelling error.
>>
>>>>
>>>> Signed-off-by: Runa Guo-oc <RunaGuo-oc@zhaoxin.com>
>>>> ---
>>>>  drivers/ata/Kconfig        |   8 +
>>>>  drivers/ata/Makefile       |   1 +
>>>>  drivers/ata/sata_zhaoxin.c | 384 +++++++++++++++++++++++++++++++++++++++++++++
>>>>  3 files changed, 393 insertions(+)
>>>>  create mode 100644 drivers/ata/sata_zhaoxin.c
>>>>
>>>> diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
>>>> index 42b51c9..ae327f3 100644
>>>> --- a/drivers/ata/Kconfig
>>>> +++ b/drivers/ata/Kconfig
>>>> @@ -553,6 +553,14 @@ config SATA_VITESSE
>>>>  
>>>>  	  If unsure, say N.
>>>>  
>>>> +config SATA_ZHAOXIN
>>>> +	tristate "ZhaoXin SATA support"
> 
> Same here. If ZhaoXin is only a company name, we need also a chipset model to be
> informative regarding which HW this driver serves.
> 

As mentioned before, the chipset models this driver serves  are ZX-100S
and ZX-200
currently.

>>>> +	depends on PCI
>>>> +	help
>>>> +	  This option enables support for ZhaoXin Serial ATA.
>>>> +
>>>> +	  If unsure, say N.
>>>> +
>>>>  comment "PATA SFF controllers with BMDMA"
>>>>  
>>>>  config PATA_ALI
>>>> diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
>>>> index 20e6645..4b84669 100644
>>>> --- a/drivers/ata/Makefile
>>>> +++ b/drivers/ata/Makefile
>>>> @@ -45,6 +45,7 @@ obj-$(CONFIG_SATA_SIL)		+= sata_sil.o
>>>>  obj-$(CONFIG_SATA_SIS)		+= sata_sis.o
>>>>  obj-$(CONFIG_SATA_SVW)		+= sata_svw.o
>>>>  obj-$(CONFIG_SATA_ULI)		+= sata_uli.o
>>>> +obj-$(CONFIG_SATA_ZHAOXIN)	+= sata_zhaoxin.o
>>>
>>> Please sort this alphabetically.
>>>
>>
>> Like this?
>> obj-$(CONFIG_SATA_VITESSE)	        += sata_vsc.o
>> obj-$(CONFIG_SATA_ZHAOXIN)	        += sata_zhaoxin.o
> 
> Yes.
> 
>>>> +
>>>> +		ata_link_err(link, "COMRESET success (errno=%d) ap=%d link %d\n",
>>>> +					rc, link->ap->port_no, link->pmp);
>>>> +	} else {
>>>> +		ata_link_err(link, "COMRESET failed (errno=%d) ap=%d link %d\n",
>>>> +					rc, link->ap->port_no, link->pmp);
>>>
>>> Reverse the if condition and exit early for this case. That will make the
>>> function code nicer. And you can drop the error message as well since
>>> sata_std_hardreset() prints one.
>>>
>>
>> Yes, I agree with your. I'll adjust the above codes like these?
>>
>> if(!rc || rc == -EAGAIN){
>>                 struct ata_port *ap = link->ap;
>>                 int pmp = link->pmp;
>>                 int tmprc;
>>                 if(pmp){
>>                         ap->ops->sff_dev_select(ap,pmp);
>>                         tmprc=ata_sff_wait_ready(&ap->link,deadline);
>>                 }else
>>                         tmprc=ata_sff_wait_ready(link,deadline);
>>
>>                 if(tmprc)
>>                         ata_link_err(link,"COMRESET failed for
>> wait(errno=%d)\n",rc);
>>
>>                 ata_link_err(link,"COMRESET success (errno=%d) ap=%d
>> link%d\n",
>>                                   rc,link->ap->port_no,link->pmp);
>>
> 
> You did not understand my point. Doing:
> 
> 	rc = sata_std_hardreset(link, class, deadline);
> 	if (rc && rc != -EAGAIN) {
> 		ata_link_err(link, "COMRESET failed (errno=%d) ap=%d link %d\n",
> 			     rc, link->ap->port_no, link->pmp);
> 		return rc;
> 	}
> 
> 	/* rc == 0 || rc == -EAGAIN case */
> 	...
> 
> Makes the code much nicer.
> 
> 
> [...]
> 

Okay, I'll adjust  it like this?
  ...
  struct ata_port *ap = link->ap;
  int pmp = link->pmp;
  int tmprc;

 rc = sata_std_hardreset(link, class, deadline);
 if (rc && rc != -EAGAIN) {
        ata_link_err(link, "COMRESET failed (errno=%d) ap=%d link %d\n",
                       rc, link->ap->port_no, link->pmp);
         return rc;
 }

  if(pmp){
     ap->ops->sff_dev_select(ap,pmp);
     tmprc=ata_sff_wait_ready(&ap->link,deadline);
 }else
    tmprc=ata_sff_wait_ready(link,deadline);

  if(tmprc)
      ata_link_err(link,"COMRESET failed for wait(errno=%d)\n",rc);

 return rc;


>>>> +MODULE_AUTHOR("Yanchen:YanchenSun@zhaoxin.com");
>>>> +MODULE_DESCRIPTION("SCSI low-level driver for ZX SATA controllers");
>>>
>>> This is not a scsi driver...
>>>
>>
>> I treat it as a scsi driver for the following reasons,  which may be not
>> accurate.
>> 1, IO path: vfs->fs->block layer->scsi layer->this driver;
>> 2, Extracted from the following link:
>> "SCSI Lower level drivers (LLDs) are variously called host bus adapter
>> (HBA) drivers and host drivers (HD)."
> 
> Again, this is not a scsi driver. Even if in Linux all ATA drives seat under the
> scsi layer, this is an ATA subsytem driver for an ATA drive. Not SCSI.
> 

Okay, I'll remove this inaccurate description next time.
Thank you.
Damien Le Moal June 27, 2023, 8:02 a.m. UTC | #5
On 6/27/23 16:17, Runa Guo-oc wrote:
> On 2023/6/26 20:40, Damien Le Moal wrote:
>> On 6/26/23 20:29, Runa Guo-oc wrote:
>>> On 2023/6/16 16:34, Damien Le Moal wrote:
>>>> On 6/16/23 16:49, Runa Guo-oc wrote:
>>>>> [PATCH] ata:sata_zhaoxin: Add support for ZhaoXin Serial ATA
>>>>
>>>> Broken patch: the email subject is your SoB instead of the above line, which
>>>> should not be part of the message (it should be the subject). Please reformat
>>>> and resend.
>>>>
>>>
>>> Okay.
>>>
>>>>>
>>>>> Add ZhaoXin Serial ATA support for ZhaoXin CUPs.
>>>>
>>>> What is "ZhaoXin" ?
>>>
>>> Zhaoxin is a Chinese company that has mastered the core technology
>>> of independent general-purpose processors and its system platform chips,
>>> and is committed to providing users with efficient, compatible and secure
>>> independent general-purpose processors, chipsets and other products.
>>> for more information, you can visit here: https://www.zhaoxin.com/.
>>
>> A company marketing message is not very informative, technically speaking. What
>> is this chipset and on what board/machine can it be found ? That is the more
>> relevant information we need in this commit message.
>>
> 
> The reason why it is called Zhaoxin SATA is actually to express that it
> applies
> to all Zhaoxin support of its separate chipset/SOC, for example,
> ZX-100S/ZX-200
> chipsets.

That is fine. I do not need a reason for the name. what I would like to see is
information about which product/board/soc this driver will be needed for. So
something like the above is actually fine (may be a little more details if you
have).

> 
>>>
>>>> And what is "CUPs" ? Please spell this out.
>>>>
>>>
>>> Yes, this is  a spelling error.
>>>
>>>>>
>>>>> Signed-off-by: Runa Guo-oc <RunaGuo-oc@zhaoxin.com>
>>>>> ---
>>>>>  drivers/ata/Kconfig        |   8 +
>>>>>  drivers/ata/Makefile       |   1 +
>>>>>  drivers/ata/sata_zhaoxin.c | 384 +++++++++++++++++++++++++++++++++++++++++++++
>>>>>  3 files changed, 393 insertions(+)
>>>>>  create mode 100644 drivers/ata/sata_zhaoxin.c
>>>>>
>>>>> diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
>>>>> index 42b51c9..ae327f3 100644
>>>>> --- a/drivers/ata/Kconfig
>>>>> +++ b/drivers/ata/Kconfig
>>>>> @@ -553,6 +553,14 @@ config SATA_VITESSE
>>>>>  
>>>>>  	  If unsure, say N.
>>>>>  
>>>>> +config SATA_ZHAOXIN
>>>>> +	tristate "ZhaoXin SATA support"
>>
>> Same here. If ZhaoXin is only a company name, we need also a chipset model to be
>> informative regarding which HW this driver serves.
>>
> 
> As mentioned before, the chipset models this driver serves  are ZX-100S
> and ZX-200
> currently.

Fine. Just say so in the Kconfig entry then.
If in the future your company produces a different chipset model that needs a
different driver, then the entries can be clearly differentiated even if they
use the same company name (ZhaoXin). E.g. "ZhaoXin ZX-100S and ZX-200 chipset
support" vs "ZhaoXin XYZ-newgen chipset support". Adding entries should not
require modifying existing entries.
Runa Guo-oc June 27, 2023, 8:51 a.m. UTC | #6
On 2023/6/27 16:02, Damien Le Moal wrote:
> On 6/27/23 16:17, Runa Guo-oc wrote:
>> On 2023/6/26 20:40, Damien Le Moal wrote:
>>> On 6/26/23 20:29, Runa Guo-oc wrote:
>>>> On 2023/6/16 16:34, Damien Le Moal wrote:
>>>>> On 6/16/23 16:49, Runa Guo-oc wrote:
>>>>>> [PATCH] ata:sata_zhaoxin: Add support for ZhaoXin Serial ATA
>>>>>
>>>>> Broken patch: the email subject is your SoB instead of the above line, which
>>>>> should not be part of the message (it should be the subject). Please reformat
>>>>> and resend.
>>>>>
>>>>
>>>> Okay.
>>>>
>>>>>>
>>>>>> Add ZhaoXin Serial ATA support for ZhaoXin CUPs.
>>>>>
>>>>> What is "ZhaoXin" ?
>>>>
>>>> Zhaoxin is a Chinese company that has mastered the core technology
>>>> of independent general-purpose processors and its system platform chips,
>>>> and is committed to providing users with efficient, compatible and secure
>>>> independent general-purpose processors, chipsets and other products.
>>>> for more information, you can visit here: https://www.zhaoxin.com/.
>>>
>>> A company marketing message is not very informative, technically speaking. What
>>> is this chipset and on what board/machine can it be found ? That is the more
>>> relevant information we need in this commit message.
>>>
>>
>> The reason why it is called Zhaoxin SATA is actually to express that it
>> applies
>> to all Zhaoxin support of its separate chipset/SOC, for example,
>> ZX-100S/ZX-200
>> chipsets.
> 
> That is fine. I do not need a reason for the name. what I would like to see is
> information about which product/board/soc this driver will be needed for. So
> something like the above is actually fine (may be a little more details if you
> have).
> 
For more details, take ZX-200 as an example to illustrate.
ZX-200 is an TSMC 40nm chip that integrates PCIE EP/RC and internal SB
controllers
(Serial ATA, Universal Serial Bus Controller, Network Interface
Controller, SPI, and so on).

And information about SATA is as follows:
•	Serial ATA
•	Compliant with Serial ATA Specification Revision 3.2
•	Support AHCI compliant with AHCI Specification 1.3.1
•	Internal SATA PHY supports 1.5G, 3G and 6G speed. Up to 4 ports
•	Support 4 SATA ports for one SATA bus master, primary channel and
secondary channel
•	Support Partial/Slumber/Auto Partial to Slumber/HIPM/DIPM, support DEVSLP
•	Support Esata/SATA Express
•	Support M.2
•	Support Listen Mode (both AHCI controller and legacy SATA controller )
•	Support MPS/DevSleep(AHCI controller only)
•	Do not support compatible mode
•	Do not support port multiplier
•	Do not support SGPIO
•	Do not support CPD
•	Support Hot-plug and MSI-X
•	Support FLR

>>
>>>>
>>>>> And what is "CUPs" ? Please spell this out.
>>>>>
>>>>
>>>> Yes, this is  a spelling error.
>>>>
>>>>>>
>>>>>> Signed-off-by: Runa Guo-oc <RunaGuo-oc@zhaoxin.com>
>>>>>> ---
>>>>>>  drivers/ata/Kconfig        |   8 +
>>>>>>  drivers/ata/Makefile       |   1 +
>>>>>>  drivers/ata/sata_zhaoxin.c | 384 +++++++++++++++++++++++++++++++++++++++++++++
>>>>>>  3 files changed, 393 insertions(+)
>>>>>>  create mode 100644 drivers/ata/sata_zhaoxin.c
>>>>>>
>>>>>> diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
>>>>>> index 42b51c9..ae327f3 100644
>>>>>> --- a/drivers/ata/Kconfig
>>>>>> +++ b/drivers/ata/Kconfig
>>>>>> @@ -553,6 +553,14 @@ config SATA_VITESSE
>>>>>>  
>>>>>>  	  If unsure, say N.
>>>>>>  
>>>>>> +config SATA_ZHAOXIN
>>>>>> +	tristate "ZhaoXin SATA support"
>>>
>>> Same here. If ZhaoXin is only a company name, we need also a chipset model to be
>>> informative regarding which HW this driver serves.
>>>
>>
>> As mentioned before, the chipset models this driver serves  are ZX-100S
>> and ZX-200
>> currently.
> 
> Fine. Just say so in the Kconfig entry then.
> If in the future your company produces a different chipset model that needs a
> different driver, then the entries can be clearly differentiated even if they
> use the same company name (ZhaoXin). E.g. "ZhaoXin ZX-100S and ZX-200 chipset
> support" vs "ZhaoXin XYZ-newgen chipset support". Adding entries should not
> require modifying existing entries.
> 
> 
> 

I got it. I'll add these information in Kconfig entry briefly next time.
Thank you for your  advice and detailed explanation.
diff mbox series

Patch

diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index 42b51c9..ae327f3 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -553,6 +553,14 @@  config SATA_VITESSE
 
 	  If unsure, say N.
 
+config SATA_ZHAOXIN
+	tristate "ZhaoXin SATA support"
+	depends on PCI
+	help
+	  This option enables support for ZhaoXin Serial ATA.
+
+	  If unsure, say N.
+
 comment "PATA SFF controllers with BMDMA"
 
 config PATA_ALI
diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
index 20e6645..4b84669 100644
--- a/drivers/ata/Makefile
+++ b/drivers/ata/Makefile
@@ -45,6 +45,7 @@  obj-$(CONFIG_SATA_SIL)		+= sata_sil.o
 obj-$(CONFIG_SATA_SIS)		+= sata_sis.o
 obj-$(CONFIG_SATA_SVW)		+= sata_svw.o
 obj-$(CONFIG_SATA_ULI)		+= sata_uli.o
+obj-$(CONFIG_SATA_ZHAOXIN)	+= sata_zhaoxin.o
 obj-$(CONFIG_SATA_VIA)		+= sata_via.o
 obj-$(CONFIG_SATA_VITESSE)	+= sata_vsc.o
 
diff --git a/drivers/ata/sata_zhaoxin.c b/drivers/ata/sata_zhaoxin.c
new file mode 100644
index 0000000..ef8c73a
--- /dev/null
+++ b/drivers/ata/sata_zhaoxin.c
@@ -0,0 +1,384 @@ 
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *  sata_zhaoxin.c - ZhaoXin Serial ATA controllers
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_host.h>
+#include <linux/libata.h>
+
+#define DRV_NAME	"sata_zx"
+#define DRV_VERSION	"2.6.1"
+
+enum board_ids_enum {
+	zx100s,
+};
+
+enum {
+	SATA_CHAN_ENAB		= 0x40, /* SATA channel enable */
+	SATA_INT_GATE		= 0x41, /* SATA interrupt gating */
+	SATA_NATIVE_MODE	= 0x42, /* Native mode enable */
+	PATA_UDMA_TIMING	= 0xB3, /* PATA timing for DMA/ cable detect */
+	PATA_PIO_TIMING		= 0xAB, /* PATA timing register */
+
+	PORT0			= (1 << 1),
+	PORT1			= (1 << 0),
+	ALL_PORTS		= PORT0 | PORT1,
+
+	NATIVE_MODE_ALL		= (1 << 7) | (1 << 6) | (1 << 5) | (1 << 4),
+
+	SATA_EXT_PHY		= (1 << 6), /* 0==use PATA, 1==ext phy */
+};
+
+static int zx_init_one(struct pci_dev *pdev, const struct pci_device_id *ent);
+static int zx_scr_read(struct ata_link *link, unsigned int scr, u32 *val);
+static int zx_scr_write(struct ata_link *link, unsigned int scr, u32 val);
+static int zx_hardreset(struct ata_link *link, unsigned int *class,
+				unsigned long deadline);
+
+static void zx_tf_load(struct ata_port *ap, const struct ata_taskfile *tf);
+
+static const struct pci_device_id zx_pci_tbl[] = {
+	{ PCI_VDEVICE(ZHAOXIN, 0x9002), zx100s },
+	{ PCI_VDEVICE(ZHAOXIN, 0x9003), zx100s },
+
+	{ }	/* terminate list */
+};
+
+static struct pci_driver zx_pci_driver = {
+	.name			= DRV_NAME,
+	.id_table		= zx_pci_tbl,
+	.probe			= zx_init_one,
+#ifdef CONFIG_PM_SLEEP
+	.suspend		= ata_pci_device_suspend,
+	.resume			= ata_pci_device_resume,
+#endif
+	.remove			= ata_pci_remove_one,
+};
+
+static struct scsi_host_template zx_sht = {
+	ATA_BMDMA_SHT(DRV_NAME),
+};
+
+static struct ata_port_operations zx_base_ops = {
+	.inherits		= &ata_bmdma_port_ops,
+	.sff_tf_load		= zx_tf_load,
+};
+
+static struct ata_port_operations zx_ops = {
+	.inherits		= &zx_base_ops,
+	.hardreset		= zx_hardreset,
+	.scr_read		= zx_scr_read,
+	.scr_write		= zx_scr_write,
+};
+
+static struct ata_port_info zx100s_port_info = {
+	.flags		= ATA_FLAG_SATA | ATA_FLAG_SLAVE_POSS,
+	.pio_mask	= ATA_PIO4,
+	.mwdma_mask	= ATA_MWDMA2,
+	.udma_mask	= ATA_UDMA6,
+	.port_ops	= &zx_ops,
+};
+
+
+static int zx_hardreset(struct ata_link *link, unsigned int *class,
+				unsigned long deadline)
+{
+	int rc;
+
+	rc = sata_std_hardreset(link, class, deadline);
+	if (!rc || rc == -EAGAIN) {
+		struct ata_port *ap = link->ap;
+		int pmp = link->pmp;
+		int tmprc;
+
+		if (pmp) {
+			ap->ops->sff_dev_select(ap, pmp);
+			tmprc = ata_sff_wait_ready(&ap->link, deadline);
+		} else {
+			tmprc = ata_sff_wait_ready(link, deadline);
+		}
+		if (tmprc)
+			ata_link_err(link, "COMRESET failed for wait (errno=%d)\n",
+					rc);
+		else
+			ata_link_err(link, "wait for bsy success\n");
+
+		ata_link_err(link, "COMRESET success (errno=%d) ap=%d link %d\n",
+					rc, link->ap->port_no, link->pmp);
+	} else {
+		ata_link_err(link, "COMRESET failed (errno=%d) ap=%d link %d\n",
+					rc, link->ap->port_no, link->pmp);
+	}
+	return rc;
+}
+
+static int zx_scr_read(struct ata_link *link, unsigned int scr, u32 *val)
+{
+	static const u8 ipm_tbl[] = { 1, 2, 6, 0 };
+	struct pci_dev *pdev = to_pci_dev(link->ap->host->dev);
+	int slot = 2 * link->ap->port_no + link->pmp;
+	u32 v = 0;
+	u8 raw;
+
+	switch (scr) {
+	case SCR_STATUS:
+		pci_read_config_byte(pdev, 0xA0 + slot, &raw);
+
+		/* read the DET field, bit0 and 1 of the config byte */
+		v |= raw & 0x03;
+
+		/* read the SPD field, bit4 of the configure byte */
+		v |= raw & 0x30;
+
+		/* read the IPM field, bit2 and 3 of the config byte */
+		v |= ((ipm_tbl[(raw >> 2) & 0x3])<<8);
+		break;
+
+	case SCR_ERROR:
+		/* devices other than 5287 uses 0xA8 as base */
+		WARN_ON(pdev->device != 0x9002 && pdev->device != 0x9003);
+		pci_write_config_byte(pdev, 0x42, slot);
+		pci_read_config_dword(pdev, 0xA8, &v);
+		break;
+
+	case SCR_CONTROL:
+		pci_read_config_byte(pdev, 0xA4 + slot, &raw);
+
+		/* read the DET field, bit0 and bit1 */
+		v |= ((raw & 0x02) << 1) | (raw & 0x01);
+
+		/* read the IPM field, bit2 and bit3 */
+		v |= ((raw >> 2) & 0x03) << 8;
+
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	*val = v;
+	return 0;
+}
+
+static int zx_scr_write(struct ata_link *link, unsigned int scr, u32 val)
+{
+	struct pci_dev *pdev = to_pci_dev(link->ap->host->dev);
+	int slot = 2 * link->ap->port_no + link->pmp;
+	u32 v = 0;
+
+	WARN_ON(pdev == NULL);
+
+	switch (scr) {
+	case SCR_ERROR:
+		/* devices 0x9002 uses 0xA8 as base */
+		WARN_ON(pdev->device != 0x9002 && pdev->device != 0x9003);
+		pci_write_config_byte(pdev, 0x42, slot);
+		pci_write_config_dword(pdev, 0xA8, val);
+		return 0;
+
+	case SCR_CONTROL:
+		/* set the DET field */
+		v |= ((val & 0x4) >> 1) | (val & 0x1);
+
+		/* set the IPM field */
+		v |= ((val >> 8) & 0x3) << 2;
+
+
+		pci_write_config_byte(pdev, 0xA4 + slot, v);
+
+
+		return 0;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+
+/**
+ *	zx_tf_load - send taskfile registers to host controller
+ *	@ap: Port to which output is sent
+ *	@tf: ATA taskfile register set
+ *
+ *	Outputs ATA taskfile to standard ATA host controller.
+ *
+ *	This is to fix the internal bug of zx chipsets, which will
+ *	reset the device register after changing the IEN bit on ctl
+ *	register.
+ */
+static void zx_tf_load(struct ata_port *ap, const struct ata_taskfile *tf)
+{
+	struct ata_taskfile ttf;
+
+	if (tf->ctl != ap->last_ctl)  {
+		ttf = *tf;
+		ttf.flags |= ATA_TFLAG_DEVICE;
+		tf = &ttf;
+	}
+	ata_sff_tf_load(ap, tf);
+}
+
+static const unsigned int zx_bar_sizes[] = {
+	8, 4, 8, 4, 16, 256
+};
+
+static const unsigned int zx100s_bar_sizes0[] = {
+	8, 4, 8, 4, 16, 0
+};
+
+static const unsigned int zx100s_bar_sizes1[] = {
+	8, 4, 0, 0, 16, 0
+};
+
+static int zx_prepare_host(struct pci_dev *pdev, struct ata_host **r_host)
+{
+	const struct ata_port_info *ppi0[] = {
+		&zx100s_port_info, NULL
+	};
+	const struct ata_port_info *ppi1[] = {
+		&zx100s_port_info, &ata_dummy_port_info
+	};
+	struct ata_host *host;
+	int i, rc;
+
+	if (pdev->device == 0x9002)
+		rc = ata_pci_bmdma_prepare_host(pdev, ppi0, &host);
+	else if (pdev->device == 0x9003)
+		rc = ata_pci_bmdma_prepare_host(pdev, ppi1, &host);
+	else
+		rc = -EINVAL;
+
+	if (rc)
+		return rc;
+
+	*r_host = host;
+
+	/* 9002 hosts four sata ports as M/S of the two channels */
+	/* 9003 hosts two sata ports as M/S of the one channel */
+	for (i = 0; i < host->n_ports; i++)
+		ata_slave_link_init(host->ports[i]);
+
+	return 0;
+}
+
+static void zx_configure(struct pci_dev *pdev, int board_id)
+{
+	u8 tmp8;
+
+	pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &tmp8);
+	dev_info(&pdev->dev, "routed to hard irq line %d\n",
+		 (int) (tmp8 & 0xf0) == 0xf0 ? 0 : tmp8 & 0x0f);
+
+	/* make sure SATA channels are enabled */
+	pci_read_config_byte(pdev, SATA_CHAN_ENAB, &tmp8);
+	if ((tmp8 & ALL_PORTS) != ALL_PORTS) {
+		dev_dbg(&pdev->dev, "enabling SATA channels (0x%x)\n",
+			(int)tmp8);
+		tmp8 |= ALL_PORTS;
+		pci_write_config_byte(pdev, SATA_CHAN_ENAB, tmp8);
+	}
+
+	/* make sure interrupts for each channel sent to us */
+	pci_read_config_byte(pdev, SATA_INT_GATE, &tmp8);
+	if ((tmp8 & ALL_PORTS) != ALL_PORTS) {
+		dev_dbg(&pdev->dev, "enabling SATA channel interrupts (0x%x)\n",
+			(int) tmp8);
+		tmp8 |= ALL_PORTS;
+		pci_write_config_byte(pdev, SATA_INT_GATE, tmp8);
+	}
+
+	/* make sure native mode is enabled */
+	pci_read_config_byte(pdev, SATA_NATIVE_MODE, &tmp8);
+	if ((tmp8 & NATIVE_MODE_ALL) != NATIVE_MODE_ALL) {
+		dev_dbg(&pdev->dev,
+			"enabling SATA channel native mode (0x%x)\n",
+			(int) tmp8);
+		tmp8 |= NATIVE_MODE_ALL;
+		pci_write_config_byte(pdev, SATA_NATIVE_MODE, tmp8);
+	}
+}
+
+static int zx_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	unsigned int i;
+	int rc;
+	struct ata_host *host = NULL;
+	int board_id = (int) ent->driver_data;
+	const unsigned int *bar_sizes;
+	int legacy_mode = 0;
+
+	ata_print_version_once(&pdev->dev, DRV_VERSION);
+
+	if (pdev->device == 0x9002 || pdev->device == 0x9003) {
+		if ((pdev->class >> 8) == PCI_CLASS_STORAGE_IDE) {
+			u8 tmp8, mask;
+
+			/* TODO: What if one channel is in native mode ... */
+			pci_read_config_byte(pdev, PCI_CLASS_PROG, &tmp8);
+			mask = (1 << 2) | (1 << 0);
+			if ((tmp8 & mask) != mask)
+				legacy_mode = 1;
+		}
+		if (legacy_mode)
+			return -EINVAL;
+	}
+
+	rc = pcim_enable_device(pdev);
+	if (rc)
+		return rc;
+
+	if (board_id == zx100s && pdev->device == 0x9002)
+		bar_sizes = &zx100s_bar_sizes0[0];
+	else if (board_id == zx100s && pdev->device == 0x9003)
+		bar_sizes = &zx100s_bar_sizes1[0];
+	else
+		bar_sizes = &zx_bar_sizes[0];
+
+	for (i = 0; i < ARRAY_SIZE(zx_bar_sizes); i++) {
+		if ((pci_resource_start(pdev, i) == 0) ||
+		    (pci_resource_len(pdev, i) < bar_sizes[i])) {
+			if (bar_sizes[i] == 0)
+				continue;
+
+			dev_err(&pdev->dev,
+				"invalid PCI BAR %u (sz 0x%llx, val 0x%llx)\n",
+				i,
+				(unsigned long long)pci_resource_start(pdev, i),
+				(unsigned long long)pci_resource_len(pdev, i));
+
+			return -ENODEV;
+		}
+	}
+
+	switch (board_id) {
+	case zx100s:
+		rc = zx_prepare_host(pdev, &host);
+		break;
+	default:
+		rc = -EINVAL;
+	}
+	if (rc)
+		return rc;
+
+	zx_configure(pdev, board_id);
+
+	pci_set_master(pdev);
+	return ata_host_activate(host, pdev->irq, ata_bmdma_interrupt,
+				 IRQF_SHARED, &zx_sht);
+}
+
+module_pci_driver(zx_pci_driver);
+
+MODULE_AUTHOR("Yanchen:YanchenSun@zhaoxin.com");
+MODULE_DESCRIPTION("SCSI low-level driver for ZX SATA controllers");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pci, zx_pci_tbl);
+MODULE_VERSION(DRV_VERSION);