From patchwork Wed Dec 12 08:58:51 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vasanth Ananthan X-Patchwork-Id: 205465 X-Patchwork-Delegate: promsoft@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id 567F22C008F for ; Wed, 12 Dec 2012 20:18:55 +1100 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 223654A1F8; Wed, 12 Dec 2012 10:18:50 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id K0IJxgFnX3gz; Wed, 12 Dec 2012 10:18:49 +0100 (CET) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 964304A1DD; Wed, 12 Dec 2012 10:18:29 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 4D21F4A21D for ; Wed, 12 Dec 2012 09:59:24 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id DCLsdTKRUvIR for ; Wed, 12 Dec 2012 09:59:22 +0100 (CET) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from mail-pb0-f44.google.com (mail-pb0-f44.google.com [209.85.160.44]) by theia.denx.de (Postfix) with ESMTPS id 20F724A21F for ; Wed, 12 Dec 2012 09:59:20 +0100 (CET) Received: by mail-pb0-f44.google.com with SMTP id uo1so372275pbc.3 for ; Wed, 12 Dec 2012 00:59:19 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:x-mailer:in-reply-to:references; bh=TKkEpt7DOvlVwlkbz+BYzIkpw64Raa+lwzvjOsqrnEw=; b=pXWfJEQdwyon/6/Wj6rCajvdIY9ltRhcWVGDFgDCi6/2i3rFsTynrxqAx5bLGrBEKC V5ee6CyeGy8NclcYZ8JA5+wbKaxixvuJR3/69yOF0RAuURV0VvPSJ6ETa3T6TkjDvVvj oeXm4CwBMZhAGHhRuv7q/A7RTNSsq7usZjCft4slRtIVoyYErdGjljoNreTOC/XzaNU+ y7mvyFpWtaHkgZh2+sUF4pB3Z4TbA7Vooj4SGopD1KZfF0zlpXDfUa+DNd06hqWAjvJ/ QFBFIfukK0c7ge7SDwKqTcYNzbPuh4uAx5kaXtFPe3XiaRgRdZ7JBWxX83gmoprJa9mn vxzg== Received: by 10.68.254.137 with SMTP id ai9mr793241pbd.21.1355302758943; Wed, 12 Dec 2012 00:59:18 -0800 (PST) Received: from vasanth-ubuntu.sisodomain.com ([115.113.119.130]) by mx.google.com with ESMTPS id se4sm15329073pbb.13.2012.12.12.00.59.16 (version=TLSv1/SSLv3 cipher=OTHER); Wed, 12 Dec 2012 00:59:18 -0800 (PST) From: Vasanth Ananthan To: u-boot@lists.denx.de Date: Wed, 12 Dec 2012 14:28:51 +0530 Message-Id: <1355302733-10100-4-git-send-email-vasanthananthan@gmail.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1355302733-10100-1-git-send-email-vasanthananthan@gmail.com> References: <1355302733-10100-1-git-send-email-vasanthananthan@gmail.com> X-Mailman-Approved-At: Wed, 12 Dec 2012 10:18:25 +0100 Cc: Vasanth Ananthan Subject: [U-Boot] [PATCH v3 3/4] Drivers: block: Support for SATA in Exynos5 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.11 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: u-boot-bounces@lists.denx.de Errors-To: u-boot-bounces@lists.denx.de This patch provides support for SATA in Exynos5250 Signed-off-by: Vasanth Ananthan --- drivers/block/dwc_ahsata.c | 397 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 387 insertions(+), 10 deletions(-) diff --git a/drivers/block/dwc_ahsata.c b/drivers/block/dwc_ahsata.c index c9b71f7..58537d3 100644 --- a/drivers/block/dwc_ahsata.c +++ b/drivers/block/dwc_ahsata.c @@ -35,6 +35,64 @@ #include #include "dwc_ahsata.h" +#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 0xFF0000 + +#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 +146,243 @@ 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 u8 sata_is_reg(void *base, u32 reg, u32 checkbit, u32 status) +{ + if ((readl(base + reg) & checkbit) == status) + return 1; + else + return 0; +} + +static u8 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 0; + udelay(1000); + time_limit_cnt++; + } + return 1; +} + +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 u8 sata_i2c_get_int_status(void) +{ + if ((readl(phy_i2c_base + SATA_I2C_CON)) & CON_INTPND) + return 1; + else + return 0; +} + +static u8 sata_i2c_is_tx_ack(void) +{ + if ((readl(phy_i2c_base + SATA_I2C_STAT)) & STAT_LAST) + return 0; + else + return 1; +} + +static u8 sata_i2c_is_bus_ready(void) +{ + if ((readl(phy_i2c_base + SATA_I2C_STAT)) & STAT_BSYST) + return 0; + else + return 1; +} + +static u8 sata_i2c_wait_for_busready(u32 time_out) +{ + while (--time_out) { + if (sata_i2c_is_bus_ready()) + return 1; + udelay(100); + } + return 0; +} + +static u8 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 1; + } + udelay(100); + } + return 0; +} + +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(u8 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(0); +} + +static u8 sata_i2c_send(u8 slave_addrs, u8 addrs, u8 ucData) +{ + s32 ret = 0; + if (!sata_i2c_wait_for_busready(SATA_TIME_LIMIT)) + return 0; + + 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 = 0; + goto STOP; + } + sata_i2c_write_data(addrs); + sata_i2c_clear_int_status(); + if (!sata_i2c_wait_for_tx_ack(SATA_TIME_LIMIT)) { + ret = 0; + goto STOP; + } + sata_i2c_write_data(ucData); + sata_i2c_clear_int_status(); + if (!sata_i2c_wait_for_tx_ack(SATA_TIME_LIMIT)) { + ret = 0; + goto STOP; + } + ret = 1; + +STOP: + sata_i2c_stop(); + sata_i2c_clear_int_status(); + sata_i2c_wait_for_busready(SATA_TIME_LIMIT); + + return ret; +} + +static u8 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 0; + + return 1; +} static int is_ready; @@ -127,6 +418,62 @@ static int ahci_setup_oobr(struct ahci_probe_ent *probe_ent, return 0; } +static int sata_phy_init(int port_num) +{ + int val, ret; + + if (sata_is_reg(phy_ctrl, SATA_CTRL0, + CTRL0_P0_PHY_CALIBRATED, CTRL0_P0_PHY_CALIBRATED)) + return 0; + + 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 +481,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_clk(); 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)); @@ -156,10 +504,8 @@ static int ahci_host_init(struct ahci_probe_ent *probe_ent) /* Set timer 1ms */ writel(clk / 1000, &(host_mmio->timer1ms)); - 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 +565,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 +607,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 +647,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); @@ -571,6 +945,8 @@ int init_sata(int dev) return -1; } + set_sata_clk(); + ahci_init_one(dev); probe_ent = (struct ahci_probe_ent *)sata_dev_desc[dev].priv; @@ -941,11 +1317,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); @@ -965,6 +1343,5 @@ int scan_sata(int dev) dwc_ahsata_print_info(dev); is_ready = 1; - return 0; }