This patch provides support for SATA in Exynos5250

Signed-off-by: Vasanth Ananthan <vasant...@samsung.com>
---
 drivers/block/dwc_ahsata.c |  394 +++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 387 insertions(+), 7 deletions(-)

diff --git a/drivers/block/dwc_ahsata.c b/drivers/block/dwc_ahsata.c
index c9b71f7..5125134 100644
--- a/drivers/block/dwc_ahsata.c
+++ b/drivers/block/dwc_ahsata.c
@@ -35,6 +35,69 @@
 #include <asm/arch/clock.h>
 #include "dwc_ahsata.h"
 
+
+#define bool unsigned char
+#define false  0
+#define true   1
+
+#ifdef SATA_DEBUG
+#define debug(fmt, args...)    printf(fmt, ##args)
+#else
+#define debug(fmt, args...)
+#endif /* MKIMAGE_DEBUG */
+
+#define MAX_DATA_BYTES_PER_SG  (4 * 1024 * 1024)
+#define MAX_BYTES_PER_TRANS (AHCI_MAX_SG * MAX_DATA_BYTES_PER_SG)
+
+#define writel_with_flush(a, b)        do { writel(a, b); readl(b); } while (0)
+
+#define EXYNOS5_SATA_PHY_CONTROL       (0x10040000 + 0x724)
+#define S5P_PMU_SATA_PHY_CONTROL_EN    0x1
+
+#define SATA_TIME_LIMIT                        10000
+#define SATA_PHY_I2C_SLAVE_ADDRS       0x70
+
+#define SATA_RESET                     0x4
+#define RESET_CMN_RST_N                        (1 << 1)
+#define LINK_RESET                     0xF0000
+
+#define SATA_MODE0                     0x10
+
+#define SATA_CTRL0                     0x14
+#define CTRL0_P0_PHY_CALIBRATED_SEL    (1 << 9)
+#define CTRL0_P0_PHY_CALIBRATED                (1 << 8)
+
+#define SATA_PHSATA_CTRLM              0xE0
+#define PHCTRLM_REF_RATE               (1 << 1)
+#define PHCTRLM_HIGH_SPEED             (1 << 0)
+
+#define SATA_PHSATA_STATM              0xF0
+#define PHSTATM_PLL_LOCKED             (1 << 0)
+
+#define SATA_I2C_CON                   0x00
+#define SATA_I2C_STAT                  0x04
+#define SATA_I2C_ADDR                  0x08
+#define SATA_I2C_DS                    0x0C
+#define SATA_I2C_LC                    0x10
+
+/* I2CCON reg */
+#define CON_ACKEN                      (1 << 7)
+#define CON_CLK512                     (1 << 6)
+#define CON_CLK16                      (~CON_CLK512)
+#define CON_INTEN                      (1 << 5)
+#define CON_INTPND                     (1 << 4)
+#define CON_TXCLK_PS                   (0xF)
+
+/* I2CSTAT reg */
+#define STAT_MSTT                      (0x3 << 6)
+#define STAT_BSYST                     (1 << 5)
+#define STAT_RTEN                      (1 << 4)
+#define STAT_LAST                      (1 << 0)
+
+#define LC_FLTR_EN                     (1 << 2)
+
+#define SATA_PHY_CON_RESET             0xF003F
+
 struct sata_port_regs {
        u32 clb;
        u32 clbu;
@@ -88,10 +151,244 @@ struct sata_host_regs {
        u32 idr;
 };
 
-#define MAX_DATA_BYTES_PER_SG  (4 * 1024 * 1024)
-#define MAX_BYTES_PER_TRANS (AHCI_MAX_SG * MAX_DATA_BYTES_PER_SG)
+void * const phy_ctrl = (void *)EXYNOS5_SATA_PHY_BASE;
+void * const phy_i2c_base = (void *)EXYNOS5_SATA_PHY_I2C;
 
-#define writel_with_flush(a, b)        do { writel(a, b); readl(b); } while (0)
+enum {
+       SATA_GENERATION1,
+       SATA_GENERATION2,
+       SATA_GENERATION3,
+};
+
+static bool sata_is_reg(void *base, u32 reg, u32 checkbit, u32 status)
+{
+       if ((readl(base + reg) & checkbit) == status)
+               return true;
+       else
+               return false;
+}
+
+static bool wait_for_reg_status(void *base, u32 reg, u32 checkbit,
+               u32 status)
+{
+       u32 time_limit_cnt = 0;
+       while (!sata_is_reg(base, reg, checkbit, status)) {
+               if (time_limit_cnt == SATA_TIME_LIMIT)
+                       return false;
+               udelay(1000);
+               time_limit_cnt++;
+       }
+       return true;
+}
+
+static void sata_set_gen(u8 gen)
+{
+       writel(gen, phy_ctrl + SATA_MODE0);
+}
+
+/* Address :I2C Address */
+static void sata_i2c_write_addrs(u8 data)
+{
+       writeb((data & 0xFE), phy_i2c_base + SATA_I2C_DS);
+}
+
+static void sata_i2c_write_data(u8 data)
+{
+       writeb((data), phy_i2c_base + SATA_I2C_DS);
+}
+
+static void sata_i2c_start(void)
+{
+       u32 val;
+       val = readl(phy_i2c_base + SATA_I2C_STAT);
+       val |= STAT_BSYST;
+       writel(val, phy_i2c_base + SATA_I2C_STAT);
+}
+
+static void sata_i2c_stop(void)
+{
+       u32 val;
+       val = readl(phy_i2c_base + SATA_I2C_STAT);
+       val &= ~STAT_BSYST;
+       writel(val, phy_i2c_base + SATA_I2C_STAT);
+}
+
+static bool sata_i2c_get_int_status(void)
+{
+       if ((readl(phy_i2c_base + SATA_I2C_CON)) & CON_INTPND)
+               return true;
+       else
+               return false;
+}
+
+static bool sata_i2c_is_tx_ack(void)
+{
+       if ((readl(phy_i2c_base + SATA_I2C_STAT)) & STAT_LAST)
+               return false;
+       else
+               return true;
+}
+
+static bool sata_i2c_is_bus_ready(void)
+{
+       if ((readl(phy_i2c_base + SATA_I2C_STAT)) & STAT_BSYST)
+               return false;
+       else
+               return true;
+}
+
+static bool sata_i2c_wait_for_busready(u32 time_out)
+{
+       while (--time_out) {
+               if (sata_i2c_is_bus_ready())
+                       return true;
+               udelay(100);
+       }
+       return false;
+}
+
+static bool sata_i2c_wait_for_tx_ack(u32 time_out)
+{
+       while (--time_out) {
+               if (sata_i2c_get_int_status()) {
+                       if (sata_i2c_is_tx_ack())
+                               return true;
+               }
+               udelay(100);
+       }
+       return false;
+}
+
+static void sata_i2c_clear_int_status(void)
+{
+       u32 val;
+       val = readl(phy_i2c_base + SATA_I2C_CON);
+       val &= ~CON_INTPND;
+       writel(val, phy_i2c_base + SATA_I2C_CON);
+}
+
+
+static void sata_i2c_set_ack_gen(bool enable)
+{
+       u32 val;
+       if (enable) {
+               val = (readl(phy_i2c_base + SATA_I2C_CON)) | CON_ACKEN;
+               writel(val, phy_i2c_base + SATA_I2C_CON);
+       } else {
+               val = readl(phy_i2c_base + SATA_I2C_CON);
+               val &= ~CON_ACKEN;
+               writel(val, phy_i2c_base + SATA_I2C_CON);
+       }
+
+}
+
+static void sata_i2c_set_master_tx(void)
+{
+       u32 val;
+       /* Disable I2C */
+       val = readl(phy_i2c_base + SATA_I2C_STAT);
+       val &= ~STAT_RTEN;
+       writel(val, phy_i2c_base + SATA_I2C_STAT);
+       /* Clear Mode */
+       val = readl(phy_i2c_base + SATA_I2C_STAT);
+       val &= ~STAT_MSTT;
+       writel(val, phy_i2c_base + SATA_I2C_STAT);
+       sata_i2c_clear_int_status();
+       /* interrupt disable */
+       val = readl(phy_i2c_base + SATA_I2C_CON);
+       val &= ~CON_INTEN;
+       writel(val, phy_i2c_base + SATA_I2C_CON);
+
+       /* Master, Send mode */
+       val = readl(phy_i2c_base + SATA_I2C_STAT);
+       val |=  STAT_MSTT;
+       writel(val, phy_i2c_base + SATA_I2C_STAT);
+
+       /* interrupt enable */
+       val = readl(phy_i2c_base + SATA_I2C_CON);
+       val |=  CON_INTEN;
+       writel(val, phy_i2c_base + SATA_I2C_CON);
+
+       /* Enable I2C */
+       val = readl(phy_i2c_base + SATA_I2C_STAT);
+       val |= STAT_RTEN;
+       writel(val, phy_i2c_base + SATA_I2C_STAT);
+}
+
+static void sata_i2c_init(void)
+{
+       u32 val;
+
+       val = readl(phy_i2c_base + SATA_I2C_CON);
+       val &= CON_CLK16;
+       writel(val, phy_i2c_base + SATA_I2C_CON);
+
+       val = readl(phy_i2c_base + SATA_I2C_CON);
+       val &= ~(CON_TXCLK_PS);
+       writel(val, phy_i2c_base + SATA_I2C_CON);
+
+       val = readl(phy_i2c_base + SATA_I2C_CON);
+       val |= (2 & CON_TXCLK_PS);
+       writel(val, phy_i2c_base + SATA_I2C_CON);
+
+       val = readl(phy_i2c_base + SATA_I2C_LC);
+       val &= ~(LC_FLTR_EN);
+       writel(val, phy_i2c_base + SATA_I2C_LC);
+
+       sata_i2c_set_ack_gen(false);
+}
+
+static bool sata_i2c_send(u8 slave_addrs, u8 addrs, u8 ucData)
+{
+       s32 ret = 0;
+       if (!sata_i2c_wait_for_busready(SATA_TIME_LIMIT))
+               return false;
+
+       sata_i2c_init();
+       sata_i2c_set_master_tx();
+
+       writel(SATA_PHY_CON_RESET, phy_ctrl + SATA_RESET);
+       sata_i2c_write_addrs(slave_addrs);
+       sata_i2c_start();
+       if (!sata_i2c_wait_for_tx_ack(SATA_TIME_LIMIT)) {
+               ret = false;
+               goto STOP;
+       }
+       sata_i2c_write_data(addrs);
+       sata_i2c_clear_int_status();
+       if (!sata_i2c_wait_for_tx_ack(SATA_TIME_LIMIT)) {
+               ret = false;
+               goto STOP;
+       }
+       sata_i2c_write_data(ucData);
+       sata_i2c_clear_int_status();
+       if (!sata_i2c_wait_for_tx_ack(SATA_TIME_LIMIT)) {
+               ret = false;
+               goto STOP;
+       }
+       ret = true;
+
+STOP:
+       sata_i2c_stop();
+       sata_i2c_clear_int_status();
+       sata_i2c_wait_for_busready(SATA_TIME_LIMIT);
+
+       return ret;
+}
+
+static bool sata_phy_i2c_init()
+{
+       /* 0x3A for 40bit I/F */
+       u8 reg_addrs =  0x3A;
+       /* 0x0B for 40bit I/F */
+       u8 default_setting_value = 0x0B;
+
+       if (!sata_i2c_send(SATA_PHY_I2C_SLAVE_ADDRS, reg_addrs,
+                                       default_setting_value))
+               return false;
+
+       return true;
+}
 
 static int is_ready;
 
@@ -127,6 +424,58 @@ static int ahci_setup_oobr(struct ahci_probe_ent 
*probe_ent,
        return 0;
 }
 
+static int sata_phy_init(int port_num)
+{
+       int val, ret;
+
+       writel(S5P_PMU_SATA_PHY_CONTROL_EN, EXYNOS5_SATA_PHY_CONTROL);
+
+       val = 0;
+       writel(val, phy_ctrl + SATA_RESET);
+       val = readl(phy_ctrl + SATA_RESET);
+       val |= 0x3D;
+       writel(val, phy_ctrl + SATA_RESET);
+
+       val = readl(phy_ctrl + SATA_RESET);
+       val |= LINK_RESET;
+       writel(val, phy_ctrl + SATA_RESET);
+
+       val = readl(phy_ctrl + SATA_RESET);
+       val |= RESET_CMN_RST_N;
+       writel(val, phy_ctrl + SATA_RESET);
+
+       val = readl(phy_ctrl + SATA_PHSATA_CTRLM);
+       val &= ~PHCTRLM_REF_RATE;
+       writel(val, phy_ctrl + SATA_PHSATA_CTRLM);
+
+       /* High speed enable for Gen3 */
+       val = readl(phy_ctrl + SATA_PHSATA_CTRLM);
+       val |= PHCTRLM_HIGH_SPEED;
+       writel(val, phy_ctrl + SATA_PHSATA_CTRLM);
+
+       ret = sata_phy_i2c_init();
+
+       val = readl(phy_ctrl + SATA_CTRL0);
+       val |= CTRL0_P0_PHY_CALIBRATED_SEL|CTRL0_P0_PHY_CALIBRATED;
+       writel(val, phy_ctrl + SATA_CTRL0);
+       sata_set_gen(SATA_GENERATION3);
+
+       /* release cmu reset */
+       val = readl(phy_ctrl + SATA_RESET);
+       val &= ~RESET_CMN_RST_N;
+       writel(val, phy_ctrl + SATA_RESET);
+
+       val = readl(phy_ctrl + SATA_RESET);
+       val |= RESET_CMN_RST_N;
+       writel(val, phy_ctrl + SATA_RESET);
+
+       if (wait_for_reg_status(phy_ctrl, SATA_PHSATA_STATM,
+                                       PHSTATM_PLL_LOCKED, 1)) {
+               return ret;
+       }
+       return 0;
+}
+
 static int ahci_host_init(struct ahci_probe_ent *probe_ent)
 {
        u32 tmp, cap_save, num_ports;
@@ -134,10 +483,11 @@ static int ahci_host_init(struct ahci_probe_ent 
*probe_ent)
        struct sata_port_regs *port_mmio = NULL;
        struct sata_host_regs *host_mmio =
                (struct sata_host_regs *)probe_ent->mmio_base;
-       int clk = mxc_get_clock(MXC_SATA_CLK);
+       int clk = get_sata_clock();
 
        cap_save = readl(&(host_mmio->cap));
        cap_save |= SATA_HOST_CAP_SSS;
+       cap_save &= ~(SATA_HOST_CAP_SMPS);
 
        /* global controller reset */
        tmp = readl(&(host_mmio->ghc));
@@ -159,7 +509,7 @@ static int ahci_host_init(struct ahci_probe_ent *probe_ent)
        ahci_setup_oobr(probe_ent, 0);
 
        writel_with_flush(SATA_HOST_GHC_AE, &(host_mmio->ghc));
-       writel(cap_save, &(host_mmio->cap));
+       writel_with_flush(cap_save, &(host_mmio->cap));
        num_ports = (cap_save & SATA_HOST_CAP_NP_MASK) + 1;
        writel_with_flush((1 << num_ports) - 1,
                                &(host_mmio->pi));
@@ -219,11 +569,37 @@ static int ahci_host_init(struct ahci_probe_ent 
*probe_ent)
                                debug("port reset failed (0x%x)\n", tmp);
                                return -1;
                        }
+
+                       tmp &= ~SATA_PORT_CMD_FRE;
+                       writel_with_flush(tmp, &(port_mmio->cmd));
+
+                       timeout = 1000;
+
+                       while ((readl(&(port_mmio->cmd)) & SATA_PORT_CMD_FR)
+                               && --timeout)
+                               ;
+
+                       if (timeout <= 0) {
+                               debug("port reset failed (0x%x)\n", tmp);
+                               return -1;
+                       }
                }
 
+               tmp = readl(&(port_mmio->sctl));
+               tmp &= (SATA_PORT_SSTS_DET_MASK);
+               writel_with_flush(tmp, &(port_mmio->sctl));
+
                /* Spin-up device */
                tmp = readl(&(port_mmio->cmd));
-               writel((tmp | SATA_PORT_CMD_SUD), &(port_mmio->cmd));
+
+               tmp |= (SATA_PORT_CMD_SUD |
+                       SATA_PORT_CMD_HPCP);
+
+               tmp &= ~(SATA_PORT_CMD_MPSP |
+                        SATA_PORT_CMD_CPD  |
+                        SATA_PORT_CMD_ESP);
+
+               writel(tmp, &(port_mmio->cmd));
 
                /* Wait for spin-up to finish */
                timeout = 1000;
@@ -235,6 +611,8 @@ static int ahci_host_init(struct ahci_probe_ent *probe_ent)
                        return -1;
                }
 
+               sata_phy_init(i);
+
                for (j = 0; j < 100; ++j) {
                        mdelay(10);
                        tmp = readl(&(port_mmio->ssts));
@@ -273,7 +651,7 @@ static int ahci_host_init(struct ahci_probe_ent *probe_ent)
 
                /* set irq mask (enables interrupts) */
                writel(DEF_PORT_IRQ, &(port_mmio->ie));
-
+               mdelay(100);
                /* register linkup ports */
                tmp = readl(&(port_mmio->ssts));
                debug("Port %d status: 0x%x\n", i, tmp);
@@ -941,11 +1319,13 @@ int scan_sata(int dev)
        pdev->blksz = ATA_SECT_SIZE;
        pdev->lun = 0 ;
 
+#ifdef CONFIG_LBA48
        /* Check if support LBA48 */
        if (ata_id_has_lba48(id)) {
                pdev->lba48 = 1;
                debug("Device support LBA48\n\r");
        }
+#endif
 
        /* Get the NCQ queue depth from device */
        probe_ent->flags &= (~SATA_FLAG_Q_DEP_MASK);
-- 
1.7.9.5

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot

Reply via email to