# hdd activity led and serial ata (sil3112) [SOLVED]

## labba

Hi!

I've got a mainboard with Nforce2 chipset and onboard sil3112 sata controller. The problem is, that the hdd activity LED isn't working when accessing sata hdds. IDE just works fine. Is there any fix, workaround? Mainboard is a Epox 8rda3+ and an Asus a7n8x deluxe. I'm using the scsi driver (sata_sil) and kernel 2.6.10, but also tried 2.6.11.7...

Thanks in advance.Last edited by labba on Sat Aug 05, 2006 11:22 am; edited 1 time in total

----------

## adaptr

 *Quote:*   

> Mainboard is a Epox 8rda3+ and an Asus a7n8x deluxe

 

Erm.. which one ?

----------

## labba

Problem exists on both boards... seems to be a driver issue: http://www.ussg.iu.edu/hypermail/linux/kernel/0408.3/1384.html

----------

## adaptr

That's not possible - the HD activity light is simple hardware.

----------

## labba

According to the above link, it isn't the case on the 3112.

And I can remember that the LED didn't work on windows either until the appropriate driver was loaded.

Anyone else who has/hasn't this problem with similar hardware?

----------

## Andy1988

I've got an A7N8X Deluce with two Serial ATA Drives from Samsung!

And I've got the same Problem.

----------

## labba

Ok, I wrote an email to the epox support and got an answer. They confirm that this is a driver issue.

But I really wonder that nobody else ever mentioned this? the sil3112 is used on nearly every NF2 board... *confused*

Any other statements? Solutions??

----------

## irondog

I've been trying reverse engineer the redhat 9.0 driver wich does enable the LED. (So, yes: it IS something that can be controlled from software). I picked the kernel source of this two years old Linux distribution and loaded some debug kernel stuff I programmed myself into memory.

I've read all 255 config registers when the official Redhat driver is used. I've seen no remarkable differences in the PCI configuration setting for the chip between the kernel 2.4 the Sata_sil kernel module (No-led-support) and the Redhat driver (with-led-support). My personal kernel hacking skills don't have good results  :Sad:  And my conclusion is that this issue is not to be solved with changing PCI config registers (I could be wrong, but I'm pretty sure).

I've been unable to disable the LED with the Asus Linux driver.

I've also been unable to enable it in any open-source driver.

Few more things to say:

* I'm almost sure this issue can be fixed in two ways: 1. A bios FIx. 2. Fix the the device driver.

* Booting windows enables the LED AFTER ntoskrnl is loaded (the bios default makes MSDOS unable to use the LED either).

* New asus mainboards like K8N also have sil3112, but don't have this problem (could be a bios fix that default).

* Earlier kernel fixes for LED control (different chips) are mostly oneliner patches changing PCI config bytes.

I'm very sorry: I'm really unable to reverse engineer Windows drivers and this needs to be checked by someone with more kernel/hardware skills.

----------

## labba

Thank you irondog for your comment!

I've found an interesting document on the siliconimage support center which is about the LED implementation and how to realise it in hardware (maybe newer mainboards use this method?). There are drivers and other stuff listed, too. Maybe that someone who has more coding/hacking/whatever experience than me could use this stuff to clarify (or even fix?) this thing.

EDIT: No direct links  :Sad: 

Resource center: http://www.siliconimage.com/support/knowledgebase.aspxLast edited by labba on Wed May 11, 2005 7:51 pm; edited 1 time in total

----------

## irondog

For people having epox and Asus boards: Please, request a bios update to fix this issue. Seeing the K8n boards working, I really beleve a bios setting can be enough. If we want to know how the chip works, we have to contact silicon image.

 *labba wrote:*   

> I've found an interesting document on the siliconimage support center [1] which is about the LED implementation [2] and how to realise it in hardware (maybe newer mainboards use this method?). There are drivers and other stuff listed, too. Maybe that someone who has more coding/hacking/whatever experience than me could use this stuff to clarify (or even fix?) this thing.

  Your links are dead, but I fear those links only give information about how to solder the chips onto a mainboard to have it working with the drivers Silicon image offers for their chips.

We want to know how the silicon is programmed, not how it's soldered. This is done by Asus and Epox. The information above is beyond the interest of a kernel hacker I think. 

Only intel gives good specs hackers can use.

----------

## irondog

Got the led working now.

As described on the Led implementation docs. The GPIO pins on the chip controll the led.

This document provides the information about how to program the GPIO pins:

http://gkernel.sourceforge.net/specs/sii/3112A_SiI-DS-0095-B2.pdf.bz2

Result is the following patch to sata_sil:

```

--- drivers/scsi/sata_sil.c.orig 2005-07-07 10:07:19.000000000 +0900

+++ drivers/scsi/sata_sil.c 2005-07-07 23:19:36.000000000 +0900

@@ -54,6 +54,7 @@

SIL_FIFO_W3 = 0x245,

SIL_SYSCFG = 0x48,

+ SIL_GPIO = 0x54,

SIL_MASK_IDE0_INT = (1 << 22),

SIL_MASK_IDE1_INT = (1 << 23),

SIL_MASK_IDE2_INT = (1 << 24),

@@ -74,6 +75,9 @@

static u32 sil_scr_read (struct ata_port *ap, unsigned int sc_reg);

static void sil_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val);

static void sil_post_set_mode (struct ata_port *ap);

+static void sil_bmdma_start(struct ata_queued_cmd *qc);

+static void sil_bmdma_stop(struct ata_port *ap);

+static void sil_host_stop (struct ata_host_set *host_set);

static struct pci_device_id sil_pci_tbl[] = {

{ 0x1095, 0x3112, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3112 },

@@ -149,8 +152,8 @@

.phy_reset = sata_phy_reset,

.post_set_mode = sil_post_set_mode,

.bmdma_setup = ata_bmdma_setup,

- .bmdma_start = ata_bmdma_start,

- .bmdma_stop = ata_bmdma_stop,

+ .bmdma_start = sil_bmdma_start,

+ .bmdma_stop = sil_bmdma_stop,

.bmdma_status = ata_bmdma_status,

.qc_prep = ata_qc_prep,

.qc_issue = ata_qc_issue_prot,

@@ -161,7 +164,7 @@

.scr_write = sil_scr_write,

.port_start = ata_port_start,

.port_stop = ata_port_stop,

- .host_stop = ata_host_stop,

+ .host_stop = sil_host_stop,

};

static struct ata_port_info sil_port_info[] = {

@@ -204,6 +207,10 @@

/* ... port 3 */

};

+struct sil_host_priv {

+ u8 use_gpio;

+};

+

MODULE_AUTHOR("Jeff Garzik");

MODULE_DESCRIPTION("low-level driver for Silicon Image SATA controller");

MODULE_LICENSE("GPL");

@@ -217,6 +224,48 @@

return cache_line;

}

+static void sil_bmdma_start(struct ata_queued_cmd *qc)

+{

+ struct sil_host_priv* hpriv = qc->ap->host_set->private_data;

+ if (hpriv->use_gpio) {

+ void* mmio_base = qc->ap->host_set->mmio_base;

+ u32 gpio = readl(mmio_base + SIL_GPIO);

+

+ /* set the lower 8 bits to activate the LED */

+ gpio |= 0xff;

+ writel(gpio, mmio_base + SIL_GPIO);

+ readl(mmio_base + SIL_GPIO); /* flush */

+ }

+

+ ata_bmdma_start(qc);

+}

+

+static void sil_bmdma_stop(struct ata_port *ap)

+{

+ struct sil_host_priv* hpriv = ap->host_set->private_data;

+

+ ata_bmdma_stop(ap);

+

+ if (hpriv->use_gpio) {

+ void* mmio_base = ap->host_set->mmio_base;

+ u32 gpio = readl(mmio_base + SIL_GPIO);

+

+ /* set bits [15:8] to disable the LED */

+ gpio |= 0xff00;

+ writel(gpio, mmio_base + SIL_GPIO);

+ readl(mmio_base + SIL_GPIO); /* flush */

+ }

+}

+

+static void sil_host_stop (struct ata_host_set *host_set)

+{

+ if (host_set->private_data)

+ kfree(host_set->private_data);

+

+ ata_host_stop(host_set);

+}

+

+

static void sil_post_set_mode (struct ata_port *ap)

{

struct ata_host_set *host_set = ap->host_set;

@@ -353,6 +402,7 @@

{

static int printed_version;

struct ata_probe_ent *probe_ent = NULL;

+ struct sil_host_priv *hpriv = NULL;

unsigned long base;

void *mmio_base;

int rc;

@@ -391,6 +441,13 @@

goto err_out_regions;

}

+ hpriv = kmalloc(sizeof(*hpriv), GFP_KERNEL);

+ if (!hpriv) {

+ rc = -ENOMEM;

+ goto err_out_free_ent;

+ }

+ memset(hpriv, 0, sizeof(*hpriv));

+

memset(probe_ent, 0, sizeof(*probe_ent));

INIT_LIST_HEAD(&probe_ent->node);

probe_ent->dev = pci_dev_to_dev(pdev);

@@ -408,10 +465,11 @@

pci_resource_len(pdev, 5));

if (mmio_base == NULL) {

rc = -ENOMEM;

- goto err_out_free_ent;

+ goto err_out_free_hpriv;

}

probe_ent->mmio_base = mmio_base;

+ probe_ent->private_data = hpriv;

base = (unsigned long) mmio_base;

@@ -456,6 +514,12 @@

irq_mask = SIL_MASK_2PORT;

}

+ /* check for LED GPIO on 3112 parts */

+ tmp = readl(mmio_base + SIL_GPIO);

+ if ((ent->driver_data == sil_3112) && ((tmp & 0xff) == 0xff)) {

+ hpriv->use_gpio = 1;

+ }

+

/* make sure IDE0/1/2/3 interrupts are not masked */

tmp = readl(mmio_base + SIL_SYSCFG);

if (tmp & irq_mask) {

@@ -477,6 +541,8 @@

return 0;

+err_out_free_hpriv:

+ kfree(hpriv);

err_out_free_ent:

kfree(probe_ent);

err_out_regions:

```

----------

## labba

Hi irondog!

This sounds great! I'd really like to give your patch a try, but I don't know how to apply it   :Embarassed:  Maybe you could provide some patch infos for all the newbies (like me) out there...

----------

## widan

 *labba wrote:*   

> I'd really like to give your patch a try, but I don't know how to apply it   Maybe you could provide some patch infos for all the newbies (like me) out there...

 

Copy the patch into a file, go to /usr/src/linux, and run:

```
patch -p0 --dry-run < /path/to/patch/file
```

This checks that everything is ok (so you won't end up with a half-patched kernel tree if something goes wrong). If there are no "FAILED" in the output, then run:

```
patch -p0 < /path/to/patch/file
```

This will actually apply the patch.

----------

## labba

Hi widan!

Already tried this method but I ended up with 

```
patching file drivers/scsi/sata_sil.c

patch: **** malformed patch at line 4: SIL_FIFO_W3 = 0x245,
```

So I thought it was the wrong way to apply the patch... Suggestions?

----------

## widan

It's probably because the tabs/spaces got lost when copy/pasting. Get it here (or go to this url and use the "Get diff 1" link on the left).

----------

## labba

Thanks, widan, that did it!

@irondog: BIG THANKS! This is great! Finally it works   :Cool:  Will your patch get into the next official kernel release?

----------

## irondog

 *labba wrote:*   

> @irondog: BIG THANKS! This is great! Finally it works   Will your patch get into the next official kernel release?

 

The 2.6.14-rc1 kernel has a change in sata_sil.c which lists the url to the data sheet of the the sil3112. I was about to hack the kernel to support hdd led activity, but someone else discovered the document months ago (seeing the date in the diff header) and had already written a patch.

So, the credits for this patch do not go to me! But you're welcome to thank me finding it  :Wink: 

The GPIO pins (which the leds are connected to) can also be used for re-programming the FlashChip on the device. So main concern for the maintainer of the driver is the risk that this patch will do something nasty to CHIPS with a FLASHROM. This won't be available in vanilla anytime soon I think.

----------

## labba

Hehe ok. Then thank you for finding the patch  :Smile: 

 *Quote:*   

> The GPIO pins (which the leds are connected to) can also be used for re-programming the FlashChip on the device. So main concern for the maintainer of the driver is the risk that this patch will do something nasty to CHIPS with a FLASHROM. This won't be available in vanilla anytime soon I think.

 

Hmm well, that sounds reasonable. But I think I will keep this patch, because it works flawlessly for me  :Smile:  It's very nice, that Silicon Image provides this documentation to the community!

----------

## labba

For all the people that used the above patch and had problems to apply it to newer kernels (like me), Aric did a great job and updated his patch, to work with 2.6.15+ kernels.

```
--- /usr/src/linux-source-2.6.15/drivers/scsi/sata_sil.c   2006-03-03 06:18:38.000000000 +0900

+++ drivers/scsi/sata_sil.c   2006-06-11 20:59:11.000000000 +0900

@@ -65,6 +65,7 @@

    SIL_FIFO_W3      = 0x245,

 

    SIL_SYSCFG      = 0x48,

+   SIL_GPIO                = 0x54,

    SIL_MASK_IDE0_INT   = (1 << 22),

    SIL_MASK_IDE1_INT   = (1 << 23),

    SIL_MASK_IDE2_INT   = (1 << 24),

@@ -85,6 +86,9 @@

 static u32 sil_scr_read (struct ata_port *ap, unsigned int sc_reg);

 static void sil_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val);

 static void sil_post_set_mode (struct ata_port *ap);

+static void sil_bmdma_start(struct ata_queued_cmd *qc);

+static void sil_bmdma_stop(struct ata_queued_cmd *qc);

+static void sil_host_stop (struct ata_host_set *host_set);

 

 

 static const struct pci_device_id sil_pci_tbl[] = {

@@ -111,7 +115,6 @@

    { "ST380013AS",      SIL_QUIRK_MOD15WRITE },

    { "ST380023AS",      SIL_QUIRK_MOD15WRITE },

    { "ST3120023AS",   SIL_QUIRK_MOD15WRITE },

-   { "ST3160023AS",   SIL_QUIRK_MOD15WRITE },

    { "ST3120026AS",   SIL_QUIRK_MOD15WRITE },

    { "ST3200822AS",   SIL_QUIRK_MOD15WRITE },

    { "ST340014ASL",   SIL_QUIRK_MOD15WRITE },

@@ -161,8 +164,8 @@

    .phy_reset      = sata_phy_reset,

    .post_set_mode      = sil_post_set_mode,

    .bmdma_setup            = ata_bmdma_setup,

-   .bmdma_start            = ata_bmdma_start,

-   .bmdma_stop      = ata_bmdma_stop,

+   .bmdma_start            = sil_bmdma_start,

+   .bmdma_stop      = sil_bmdma_stop,

    .bmdma_status      = ata_bmdma_status,

    .qc_prep      = ata_qc_prep,

    .qc_issue      = ata_qc_issue_prot,

@@ -173,7 +176,7 @@

    .scr_write      = sil_scr_write,

    .port_start      = ata_port_start,

    .port_stop      = ata_port_stop,

-   .host_stop      = ata_pci_host_stop,

+   .host_stop      = sil_host_stop,

 };

 

 static struct ata_port_info sil_port_info[] = {

@@ -226,6 +229,10 @@

    /* ... port 3 */

 };

 

+struct sil_host_priv {

+   u8         use_gpio;

+};

+

 MODULE_AUTHOR("Jeff Garzik");

 MODULE_DESCRIPTION("low-level driver for Silicon Image SATA controller");

 MODULE_LICENSE("GPL");

@@ -240,6 +247,48 @@

    return cache_line;

 }

 

+static void sil_bmdma_start(struct ata_queued_cmd *qc)

+{

+   struct sil_host_priv* hpriv = qc->ap->host_set->private_data;

+   if (hpriv->use_gpio) {

+      void* mmio_base = qc->ap->host_set->mmio_base;

+      u32 gpio = readl(mmio_base + SIL_GPIO);

+

+      /* set the lower 8 bits to activate the LED */

+      gpio |= 0xff;

+      writel(gpio, mmio_base + SIL_GPIO);

+      readl(mmio_base + SIL_GPIO);   /* flush */

+   }

+

+   ata_bmdma_start(qc);

+}

+

+static void sil_bmdma_stop(struct ata_queued_cmd *qc)

+{

+   struct sil_host_priv* hpriv = qc->ap->host_set->private_data;

+

+   ata_bmdma_stop(qc);

+

+   if (hpriv->use_gpio) {

+      void* mmio_base = qc->ap->host_set->mmio_base;

+      u32 gpio = readl(mmio_base + SIL_GPIO);

+

+      /* set bits [15:8] to disable the LED */

+      gpio |= 0xff00;

+      writel(gpio, mmio_base + SIL_GPIO);

+      readl(mmio_base + SIL_GPIO);   /* flush */

+   }

+}

+

+static void sil_host_stop (struct ata_host_set *host_set)

+{

+   ata_pci_host_stop(host_set);

+

+   if (host_set->private_data)

+      kfree(host_set->private_data);

+}

+

+

 static void sil_post_set_mode (struct ata_port *ap)

 {

    struct ata_host_set *host_set = ap->host_set;

@@ -377,6 +426,7 @@

 {

    static int printed_version;

    struct ata_probe_ent *probe_ent = NULL;

+   struct sil_host_priv *hpriv = NULL;

    unsigned long base;

    void __iomem *mmio_base;

    int rc;

@@ -415,6 +465,13 @@

       goto err_out_regions;

    }

 

+   hpriv = kmalloc(sizeof(*hpriv), GFP_KERNEL);

+   if (!hpriv) {

+      rc = -ENOMEM;

+      goto err_out_free_ent;

+   }

+   memset(hpriv, 0, sizeof(*hpriv));

+

    memset(probe_ent, 0, sizeof(*probe_ent));

    INIT_LIST_HEAD(&probe_ent->node);

    probe_ent->dev = pci_dev_to_dev(pdev);

@@ -431,10 +488,11 @@

    mmio_base = pci_iomap(pdev, 5, 0);

    if (mmio_base == NULL) {

       rc = -ENOMEM;

-      goto err_out_free_ent;

+      goto err_out_free_hpriv;

    }

 

    probe_ent->mmio_base = mmio_base;

+   probe_ent->private_data = hpriv;

 

    base = (unsigned long) mmio_base;

 

@@ -479,6 +537,14 @@

       irq_mask = SIL_MASK_2PORT;

    }

 

+   /* check for LED GPIO on 3112 parts */

+   tmp = readl(mmio_base + SIL_GPIO);

+   if ((ent->driver_data == sil_3112 ||

+       ent->driver_data == sil_3112_m15w) && (tmp & 0xff) == 0xff) {

+      dev_printk(KERN_INFO, &pdev->dev, "using GPIO for LED control\n");

+      hpriv->use_gpio = 1;

+   }

+

    /* make sure IDE0/1/2/3 interrupts are not masked */

    tmp = readl(mmio_base + SIL_SYSCFG);

    if (tmp & irq_mask) {

@@ -500,6 +566,8 @@

 

    return 0;

 

+err_out_free_hpriv:

+   kfree(hpriv);

 err_out_free_ent:

    kfree(probe_ent);

 err_out_regions:

```

----------

## Septor

If copy&paste from labba's post doesn't work, I have updated the patch on my homepage at:

http://acyr.net/projects.php#sii3112 (backup at: http://ender.dyndns.info/projects.php#sii3112)

I have also had comfirmation from a user that this patch will NOT screw-up the flash memory 3112-based addon cards... however it also does not work, so there is no advantage/disadvantage to using it if you have an add-on card.  As I don't have one to test I can't modify the patch to make it work.  My Epox Motherboard with onboard 3112 works just beautifully though.

Enjoy!

----------

## widan

 *Septor wrote:*   

> I have also had comfirmation from a user that this patch will NOT screw-up the flash memory 3112-based addon cards...

 

There is no reason why it would. There are very specific sequences to be followed to write to flash memory, and if you don't follow them nothing will happen.

 *Septor wrote:*   

> however it also does not work, so there is no advantage/disadvantage to using it if you have an add-on card. As I don't have one to test I can't modify the patch to make it work.

 

It won't work on an add-in card because there is no way a PCI card can light up the disk led that is connected to the motherboard (the PCI bus does not carry this signal). Some cards might have a LED directly on the card, or a connector for one though.

----------

## Septor

 *widan wrote:*   

>  *Septor wrote:*   I have also had comfirmation from a user that this patch will NOT screw-up the flash memory 3112-based addon cards... 
> 
> There is no reason why it would. There are very specific sequences to be followed to write to flash memory, and if you don't follow them nothing will happen.

 

Granted, the probability is extremely low, but when the GPIO registers are labelled as "write enable" I certainly don't want to take the chance that a user will "get lucky" and have their firmware screwed.  Only takes 1 bit swap to mess things up...

 *widan wrote:*   

>  *Septor wrote:*   however it also does not work, so there is no advantage/disadvantage to using it if you have an add-on card. As I don't have one to test I can't modify the patch to make it work. 
> 
> It won't work on an add-in card because there is no way a PCI card can light up the disk led that is connected to the motherboard (the PCI bus does not carry this signal). Some cards might have a LED directly on the card, or a connector for one though.

 

Well, depends if you have an LED connected directly to the card or not (providing it has LED pins).  Even if you hook up an LED to the add-on card, there is no guarantee it will work without a similar patch.

----------

