uboot/board/highbank/ahci.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright 2012 Calxeda, Inc.
   4 */
   5
   6#include <common.h>
   7#include <ahci.h>
   8#include <asm/io.h>
   9#include <linux/delay.h>
  10
  11#define CPHY_MAP(dev, addr) ((((dev) & 0x1f) << 7) | (((addr) >> 9) & 0x7f))
  12#define CPHY_ADDR(base, dev, addr) ((base) | (((addr) & 0x1ff) << 2))
  13#define CPHY_BASE                       0xfff58000
  14#define CPHY_WIDTH                      0x1000
  15#define CPHY_DTE_XS                     5
  16#define CPHY_MII                        31
  17#define SERDES_CR_CTL                   0x80a0
  18#define SERDES_CR_ADDR                  0x80a1
  19#define SERDES_CR_DATA                  0x80a2
  20#define CR_BUSY                         0x0001
  21#define CR_START                        0x0001
  22#define CR_WR_RDN                       0x0002
  23#define CPHY_TX_INPUT_STS               0x2001
  24#define CPHY_RX_INPUT_STS               0x2002
  25#define CPHY_SATA_TX_OVERRIDE_BIT       0x8000
  26#define CPHY_SATA_RX_OVERRIDE_BIT       0x4000
  27#define CPHY_TX_INPUT_OVERRIDE          0x2004
  28#define CPHY_RX_INPUT_OVERRIDE          0x2005
  29#define SPHY_LANE                       0x100
  30#define SPHY_HALF_RATE                  0x0001
  31#define CPHY_SATA_DPLL_MODE             0x0700
  32#define CPHY_SATA_DPLL_SHIFT            8
  33#define CPHY_SATA_TX_ATTEN              0x1c00
  34#define CPHY_SATA_TX_ATTEN_SHIFT        10
  35
  36#define HB_SREG_SATA_ATTEN              0xfff3cf24
  37
  38#define SATA_PORT_BASE                  0xffe08000
  39#define SATA_VERSIONR                   0xf8
  40#define SATA_HB_VERSION                 0x3332302a
  41
  42static u32 __combo_phy_reg_read(u8 phy, u8 dev, u32 addr)
  43{
  44        u32 data;
  45        writel(CPHY_MAP(dev, addr), CPHY_BASE + 0x800 + CPHY_WIDTH * phy);
  46        data = readl(CPHY_ADDR(CPHY_BASE + CPHY_WIDTH * phy, dev, addr));
  47        return data;
  48}
  49
  50static void __combo_phy_reg_write(u8 phy, u8 dev, u32 addr, u32 data)
  51{
  52        writel(CPHY_MAP(dev, addr), CPHY_BASE + 0x800 + CPHY_WIDTH * phy);
  53        writel(data, CPHY_ADDR(CPHY_BASE + CPHY_WIDTH * phy, dev, addr));
  54}
  55
  56static u32 combo_phy_read(u8 phy, u32 addr)
  57{
  58        u8 dev = CPHY_DTE_XS;
  59        if (phy == 5)
  60                dev = CPHY_MII;
  61        while (__combo_phy_reg_read(phy, dev, SERDES_CR_CTL) & CR_BUSY)
  62                udelay(5);
  63        __combo_phy_reg_write(phy, dev, SERDES_CR_ADDR, addr);
  64        __combo_phy_reg_write(phy, dev, SERDES_CR_CTL, CR_START);
  65        while (__combo_phy_reg_read(phy, dev, SERDES_CR_CTL) & CR_BUSY)
  66                udelay(5);
  67        return __combo_phy_reg_read(phy, dev, SERDES_CR_DATA);
  68}
  69
  70static void combo_phy_write(u8 phy, u32 addr, u32 data)
  71{
  72        u8 dev = CPHY_DTE_XS;
  73        if (phy == 5)
  74                dev = CPHY_MII;
  75        while (__combo_phy_reg_read(phy, dev, SERDES_CR_CTL) & CR_BUSY)
  76                udelay(5);
  77        __combo_phy_reg_write(phy, dev, SERDES_CR_ADDR, addr);
  78        __combo_phy_reg_write(phy, dev, SERDES_CR_DATA, data);
  79        __combo_phy_reg_write(phy, dev, SERDES_CR_CTL, CR_WR_RDN | CR_START);
  80}
  81
  82static void cphy_spread_spectrum_override(u8 phy, u8 lane, u32 val)
  83{
  84        u32 tmp;
  85        tmp = combo_phy_read(phy, CPHY_RX_INPUT_STS + lane * SPHY_LANE);
  86        tmp &= ~CPHY_SATA_RX_OVERRIDE_BIT;
  87        combo_phy_write(phy, CPHY_RX_INPUT_OVERRIDE + lane * SPHY_LANE, tmp);
  88
  89        tmp |= CPHY_SATA_RX_OVERRIDE_BIT;
  90        combo_phy_write(phy, CPHY_RX_INPUT_OVERRIDE + lane * SPHY_LANE, tmp);
  91
  92        tmp &= ~CPHY_SATA_DPLL_MODE;
  93        tmp |= (val << CPHY_SATA_DPLL_SHIFT) & CPHY_SATA_DPLL_MODE;
  94        combo_phy_write(phy, CPHY_RX_INPUT_OVERRIDE + lane * SPHY_LANE, tmp);
  95}
  96
  97static void cphy_tx_attenuation_override(u8 phy, u8 lane)
  98{
  99        u32 val;
 100        u32 tmp;
 101        u8  shift;
 102
 103        shift = ((phy == 5) ? 4 : lane) * 4;
 104
 105        val = (readl(HB_SREG_SATA_ATTEN) >> shift) & 0xf;
 106
 107        if (val & 0x8)
 108                return;
 109
 110        tmp = combo_phy_read(phy, CPHY_TX_INPUT_STS + lane * SPHY_LANE);
 111        tmp &= ~CPHY_SATA_TX_OVERRIDE_BIT;
 112        combo_phy_write(phy, CPHY_TX_INPUT_OVERRIDE + lane * SPHY_LANE, tmp);
 113
 114        tmp |= CPHY_SATA_TX_OVERRIDE_BIT;
 115        combo_phy_write(phy, CPHY_TX_INPUT_OVERRIDE + lane * SPHY_LANE, tmp);
 116
 117        tmp |= (val << CPHY_SATA_TX_ATTEN_SHIFT) & CPHY_SATA_TX_ATTEN;
 118        combo_phy_write(phy, CPHY_TX_INPUT_OVERRIDE + lane * SPHY_LANE, tmp);
 119}
 120
 121static void cphy_disable_port_overrides(u8 port)
 122{
 123        u32 tmp;
 124        u8 lane = 0, phy = 0;
 125
 126        if (port == 0)
 127                phy = 5;
 128        else if (port < 5)
 129                lane = port - 1;
 130        else
 131                return;
 132        tmp = combo_phy_read(phy, CPHY_RX_INPUT_STS + lane * SPHY_LANE);
 133        tmp &= ~CPHY_SATA_RX_OVERRIDE_BIT;
 134        combo_phy_write(phy, CPHY_RX_INPUT_OVERRIDE + lane * SPHY_LANE, tmp);
 135
 136        tmp = combo_phy_read(phy, CPHY_TX_INPUT_OVERRIDE + lane * SPHY_LANE);
 137        tmp &= ~CPHY_SATA_TX_OVERRIDE_BIT;
 138        combo_phy_write(phy, CPHY_TX_INPUT_OVERRIDE + lane * SPHY_LANE, tmp);
 139}
 140
 141void cphy_disable_overrides(void)
 142{
 143        int i;
 144        u32 port_map;
 145
 146        port_map = readl(0xffe08000 + HOST_PORTS_IMPL);
 147        for (i = 0; i < 5; i++) {
 148                if (port_map & (1 << i))
 149                        cphy_disable_port_overrides(i);
 150        }
 151}
 152
 153static void cphy_override_lane(u8 port)
 154{
 155        u32 tmp, k = 0;
 156        u8 lane = 0, phy = 0;
 157
 158        if (port == 0)
 159                phy = 5;
 160        else if (port < 5)
 161                lane = port - 1;
 162        else
 163                return;
 164
 165        do {
 166                tmp = combo_phy_read(0, CPHY_RX_INPUT_STS +
 167                                        lane * SPHY_LANE);
 168        } while ((tmp & SPHY_HALF_RATE) && (k++ < 1000));
 169        cphy_spread_spectrum_override(phy, lane, 3);
 170        cphy_tx_attenuation_override(phy, lane);
 171}
 172
 173#define WAIT_MS_LINKUP  4
 174
 175int ahci_link_up(struct ahci_uc_priv *probe_ent, int port)
 176{
 177        u32 tmp;
 178        int j = 0;
 179        u8 *port_mmio = (u8 *)probe_ent->port[port].port_mmio;
 180        u32 is_highbank = readl(SATA_PORT_BASE + SATA_VERSIONR) ==
 181                                SATA_HB_VERSION ? 1 : 0;
 182
 183        /* Bring up SATA link.
 184         * SATA link bringup time is usually less than 1 ms; only very
 185         * rarely has it taken between 1-2 ms. Never seen it above 2 ms.
 186         */
 187        while (j < WAIT_MS_LINKUP) {
 188                if (is_highbank && (j == 0)) {
 189                        cphy_disable_port_overrides(port);
 190                        writel(0x301, port_mmio + PORT_SCR_CTL);
 191                        udelay(1000);
 192                        writel(0x300, port_mmio + PORT_SCR_CTL);
 193                        udelay(1000);
 194                        cphy_override_lane(port);
 195                }
 196
 197                tmp = readl(port_mmio + PORT_SCR_STAT);
 198                if ((tmp & 0xf) == 0x3)
 199                        return 0;
 200                udelay(1000);
 201                j++;
 202
 203                if ((j == WAIT_MS_LINKUP) && (tmp & 0xf))
 204                        j = 0;  /* retry phy reset */
 205        }
 206        return 1;
 207}
 208