Booting Imac G5
Benjamin Herrenschmidt
benh at kernel.crashing.org
Sun Nov 7 11:32:48 EST 2004
On Sun, 2004-11-07 at 10:51 +1100, Benjamin Herrenschmidt wrote:
> Ok, a new Darwin is out and the driver there has some additional bits,
> related to the SATA cell. I'm hacking together a patch.
Index: linux-work/drivers/scsi/sata_svw.c
===================================================================
--- linux-work.orig/drivers/scsi/sata_svw.c 2004-10-13 09:02:05.000000000 +1000
+++ linux-work/drivers/scsi/sata_svw.c 2004-11-07 11:23:41.945588808 +1100
@@ -49,7 +49,7 @@
#endif /* CONFIG_PPC_OF */
#define DRV_NAME "sata_svw"
-#define DRV_VERSION "1.04"
+#define DRV_VERSION "1.05"
/* Taskfile registers offsets */
#define K2_SATA_TF_CMD_OFFSET 0x00
@@ -75,10 +75,19 @@
#define K2_SATA_SICR1_OFFSET 0x80
#define K2_SATA_SICR2_OFFSET 0x84
#define K2_SATA_SIM_OFFSET 0x88
+#define K2_SATA_MDIO_ACCESS 0x8c
/* Port stride */
#define K2_SATA_PORT_OFFSET 0x100
+/* Private structure */
+struct k2_sata_priv
+{
+#ifdef CONFIG_PPC_OF
+ struct device_node *of_node;
+#endif
+ int need_mdio_phy_reset;
+};
static u32 k2_sata_scr_read (struct ata_port *ap, unsigned int sc_reg)
{
@@ -96,6 +105,42 @@
writel(val, (void *) ap->ioaddr.scr_addr + (sc_reg * 4));
}
+static u16 k2_sata_mdio_read(struct ata_host_set *host, int reg)
+{
+ u16 val;
+ int timeout;
+
+ writel(host_set->mmio_base + K2_SATA_MDIO_ACCESS,
+ (reg & 0x1f) | 0x4000);
+ for(timeout = 10000; timeout > 0; timeout++) {
+ val = readl(host_set->mmio_base + K2_SATA_MDIO_ACCESS);
+ if (val & 0x8000)
+ break;
+ udelay(100);
+ }
+ if (timeout <= 0) {
+ printk(KERN_WARNING "sata_svw: timeout reading MDIO reg %d\n", reg);
+ return 0xffff;
+ }
+ return val >> 16;
+}
+
+static void k2_sata_mdio_write(struct ata_host_set *host, int reg, u16 val)
+{
+ u16 val;
+ int timeout;
+
+ writel(host_set->mmio_base + K2_SATA_MDIO_ACCESS,
+ (reg & 0x1f) | (((u32)val) << 16) | 0x2000);
+ for(timeout = 10000; timeout > 0; timeout++) {
+ val = readl(host_set->mmio_base + K2_SATA_MDIO_ACCESS);
+ if (val & 0x8000)
+ break;
+ udelay(100);
+ }
+ if (timeout <= 0)
+ printk(KERN_WARNING "sata_svw: timeout writing MDIO reg %d\n", reg);
+}
static void k2_sata_tf_load(struct ata_port *ap, struct ata_taskfile *tf)
{
@@ -220,6 +265,31 @@
return readl((void *) ap->ioaddr.status_addr);
}
+static void k2_sata_mdio_phy_reset(struct ata_host_set *host_set);
+{
+ u16 reg;
+
+ reg = k2_sata_mdio_read(host_set, 4);
+ k2_sata_mdio_write(host_set, 4, reg | 0x0008);
+ udelay(200);
+ k2_sata_mdio_write(host_set, 4, reg);
+ udelay(250);
+}
+
+static void k2_sata_host_start(struct ata_host_set *host_set)
+{
+ struct k2_sata_priv *pp;
+
+ pp = host_set->private_data;
+
+ /* Some cell revs need a HW reset of the PHY layer at this point, and
+ * on wakeup from power management
+ */
+ if (pp->need_mdio_phy_reset)
+ k2_sata_mdio_phy_reset(host_set);
+}
+
+
#ifdef CONFIG_PPC_OF
/*
* k2_sata_proc_info
@@ -237,15 +307,15 @@
{
struct ata_port *ap;
struct device_node *np;
+ struct k2_sata_priv *pp;
int len, index;
/* Find the ata_port */
ap = (struct ata_port *) &shost->hostdata[0];
if (ap == NULL)
return 0;
-
- /* Find the OF node for the PCI device proper */
- np = pci_device_to_OF_node(ap->host_set->pdev);
+ pp = ap->host_set->private_data;
+ np = pp->of_node;
if (np == NULL)
return 0;
@@ -310,6 +380,7 @@
.scr_write = k2_sata_scr_write,
.port_start = ata_port_start,
.port_stop = ata_port_stop,
+ .host_start = k2_sata_host_start,
};
static void k2_sata_setup_port(struct ata_ioports *port, unsigned long base)
@@ -338,6 +409,7 @@
struct ata_probe_ent *probe_ent = NULL;
unsigned long base;
void *mmio_base;
+ struct k2_sata_priv *pp = NULL;
int rc;
if (!printed_version++)
@@ -374,10 +446,31 @@
rc = -ENOMEM;
goto err_out_regions;
}
-
memset(probe_ent, 0, sizeof(*probe_ent));
+
+ pp = (struct k2_sata_priv *)kmalloc(sizeof(struct k2_sata_priv), GFP_KERNEL);
+ if (pp == NULL) {
+ rc = -ENOMEM;
+ goto err_out_free_ent;
+ }
+ memset(pp, 0, sizeof(struct k2_sata_priv));
+
probe_ent->pdev = pdev;
INIT_LIST_HEAD(&probe_ent->node);
+ probe_ent->private_data = pdev;
+
+#ifdef CONFIG_PPC_OF
+ /* Find the OF node for the PCI device proper */
+ pp->of_node = pci_device_to_OF_node(ap->host_set->pdev);
+
+ /* Check for revision 1 */
+ if (pp->of_node) {
+ u32 *rev;
+ rev = (u32 *)get_property(pp->of_node, "cell-revision", NULL);
+ if (rev && (*rev) > 0)
+ pp->need_mdio_phy_reset = 1;
+ }
+#endif /* CONFIG_PPC_OF */
mmio_base = ioremap(pci_resource_start(pdev, 5),
pci_resource_len(pdev, 5));
@@ -429,7 +522,10 @@
return 0;
err_out_free_ent:
- kfree(probe_ent);
+ if (pp)
+ kfree(pp);
+ if (probe_ent)
+ kfree(probe_ent);
err_out_regions:
pci_release_regions(pdev);
err_out:
Index: linux-work/drivers/scsi/libata-core.c
===================================================================
--- linux-work.orig/drivers/scsi/libata-core.c 2004-11-07 11:24:09.617382056 +1100
+++ linux-work/drivers/scsi/libata-core.c 2004-11-07 11:24:40.491688448 +1100
@@ -3271,6 +3271,9 @@
host_set->private_data = ent->private_data;
host_set->ops = ent->port_ops;
+ if (host_set->ops->host_start)
+ host_set->ops->host_start(host_set);
+
/* register each port bound to this device */
for (i = 0; i < ent->n_ports; i++) {
struct ata_port *ap;
Index: linux-work/include/linux/libata.h
===================================================================
--- linux-work.orig/include/linux/libata.h 2004-11-07 11:23:56.598361248 +1100
+++ linux-work/include/linux/libata.h 2004-11-07 11:24:54.242597992 +1100
@@ -349,6 +349,7 @@
int (*port_start) (struct ata_port *ap);
void (*port_stop) (struct ata_port *ap);
+ void (*host_start) (struct ata_host_set *host_set);
void (*host_stop) (struct ata_host_set *host_set);
};
More information about the Linuxppc64-dev
mailing list