uboot/drivers/phy/ti-pipe3-phy.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com/
   3 * Written by Jean-Jacques Hiblot  <jjhiblot@ti.com>
   4 *
   5 * SPDX-License-Identifier:     GPL-2.0+
   6 */
   7
   8#include <common.h>
   9#include <dm.h>
  10#include <dm/device.h>
  11#include <generic-phy.h>
  12#include <asm/io.h>
  13#include <asm/arch/sys_proto.h>
  14#include <syscon.h>
  15#include <regmap.h>
  16
  17/* PLLCTRL Registers */
  18#define PLL_STATUS              0x00000004
  19#define PLL_GO                  0x00000008
  20#define PLL_CONFIGURATION1      0x0000000C
  21#define PLL_CONFIGURATION2      0x00000010
  22#define PLL_CONFIGURATION3      0x00000014
  23#define PLL_CONFIGURATION4      0x00000020
  24
  25#define PLL_REGM_MASK           0x001FFE00
  26#define PLL_REGM_SHIFT          9
  27#define PLL_REGM_F_MASK         0x0003FFFF
  28#define PLL_REGM_F_SHIFT        0
  29#define PLL_REGN_MASK           0x000001FE
  30#define PLL_REGN_SHIFT          1
  31#define PLL_SELFREQDCO_MASK     0x0000000E
  32#define PLL_SELFREQDCO_SHIFT    1
  33#define PLL_SD_MASK             0x0003FC00
  34#define PLL_SD_SHIFT            10
  35#define SET_PLL_GO              0x1
  36#define PLL_TICOPWDN            BIT(16)
  37#define PLL_LDOPWDN             BIT(15)
  38#define PLL_LOCK                0x2
  39#define PLL_IDLE                0x1
  40
  41/* Software rest for the SATA PLL (in CTRL_CORE_SMA_SW_0 register)*/
  42#define SATA_PLL_SOFT_RESET (1<<18)
  43
  44/* PHY POWER CONTROL Register */
  45#define OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK         0x003FC000
  46#define OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT        0xE
  47
  48#define OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK        0xFFC00000
  49#define OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT       0x16
  50
  51#define OMAP_CTRL_PIPE3_PHY_TX_RX_POWERON       0x3
  52#define OMAP_CTRL_PIPE3_PHY_TX_RX_POWEROFF      0x0
  53
  54
  55#define PLL_IDLE_TIME   100     /* in milliseconds */
  56#define PLL_LOCK_TIME   100     /* in milliseconds */
  57
  58struct omap_pipe3 {
  59        void __iomem            *pll_ctrl_base;
  60        void __iomem            *power_reg;
  61        void __iomem            *pll_reset_reg;
  62        struct pipe3_dpll_map   *dpll_map;
  63};
  64
  65
  66struct pipe3_dpll_params {
  67        u16     m;
  68        u8      n;
  69        u8      freq:3;
  70        u8      sd;
  71        u32     mf;
  72};
  73
  74struct pipe3_dpll_map {
  75        unsigned long rate;
  76        struct pipe3_dpll_params params;
  77};
  78
  79static inline u32 omap_pipe3_readl(void __iomem *addr, unsigned offset)
  80{
  81        return readl(addr + offset);
  82}
  83
  84static inline void omap_pipe3_writel(void __iomem *addr, unsigned offset,
  85                u32 data)
  86{
  87        writel(data, addr + offset);
  88}
  89
  90static struct pipe3_dpll_params *omap_pipe3_get_dpll_params(struct omap_pipe3
  91                                                                        *pipe3)
  92{
  93        u32 rate;
  94        struct pipe3_dpll_map *dpll_map = pipe3->dpll_map;
  95
  96        rate = get_sys_clk_freq();
  97
  98        for (; dpll_map->rate; dpll_map++) {
  99                if (rate == dpll_map->rate)
 100                        return &dpll_map->params;
 101        }
 102
 103        printf("%s: No DPLL configuration for %u Hz SYS CLK\n",
 104               __func__, rate);
 105        return NULL;
 106}
 107
 108static int omap_pipe3_wait_lock(struct omap_pipe3 *pipe3)
 109{
 110        u32 val;
 111        int timeout = PLL_LOCK_TIME;
 112
 113        do {
 114                mdelay(1);
 115                val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_STATUS);
 116                if (val & PLL_LOCK)
 117                        break;
 118        } while (--timeout);
 119
 120        if (!(val & PLL_LOCK)) {
 121                printf("%s: DPLL failed to lock\n", __func__);
 122                return -EBUSY;
 123        }
 124
 125        return 0;
 126}
 127
 128static int omap_pipe3_dpll_program(struct omap_pipe3 *pipe3)
 129{
 130        u32                     val;
 131        struct pipe3_dpll_params *dpll_params;
 132
 133        dpll_params = omap_pipe3_get_dpll_params(pipe3);
 134        if (!dpll_params) {
 135                printf("%s: Invalid DPLL parameters\n", __func__);
 136                return -EINVAL;
 137        }
 138
 139        val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_CONFIGURATION1);
 140        val &= ~PLL_REGN_MASK;
 141        val |= dpll_params->n << PLL_REGN_SHIFT;
 142        omap_pipe3_writel(pipe3->pll_ctrl_base, PLL_CONFIGURATION1, val);
 143
 144        val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_CONFIGURATION2);
 145        val &= ~PLL_SELFREQDCO_MASK;
 146        val |= dpll_params->freq << PLL_SELFREQDCO_SHIFT;
 147        omap_pipe3_writel(pipe3->pll_ctrl_base, PLL_CONFIGURATION2, val);
 148
 149        val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_CONFIGURATION1);
 150        val &= ~PLL_REGM_MASK;
 151        val |= dpll_params->m << PLL_REGM_SHIFT;
 152        omap_pipe3_writel(pipe3->pll_ctrl_base, PLL_CONFIGURATION1, val);
 153
 154        val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_CONFIGURATION4);
 155        val &= ~PLL_REGM_F_MASK;
 156        val |= dpll_params->mf << PLL_REGM_F_SHIFT;
 157        omap_pipe3_writel(pipe3->pll_ctrl_base, PLL_CONFIGURATION4, val);
 158
 159        val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_CONFIGURATION3);
 160        val &= ~PLL_SD_MASK;
 161        val |= dpll_params->sd << PLL_SD_SHIFT;
 162        omap_pipe3_writel(pipe3->pll_ctrl_base, PLL_CONFIGURATION3, val);
 163
 164        omap_pipe3_writel(pipe3->pll_ctrl_base, PLL_GO, SET_PLL_GO);
 165
 166        return omap_pipe3_wait_lock(pipe3);
 167}
 168
 169static void omap_control_pipe3_power(struct omap_pipe3 *pipe3, int on)
 170{
 171        u32 val, rate;
 172
 173        val = readl(pipe3->power_reg);
 174
 175        rate = get_sys_clk_freq();
 176        rate = rate/1000000;
 177
 178        if (on) {
 179                val &= ~(OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK |
 180                                OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK);
 181                val |= OMAP_CTRL_PIPE3_PHY_TX_RX_POWERON <<
 182                        OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT;
 183                val |= rate <<
 184                        OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT;
 185        } else {
 186                val &= ~OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK;
 187                val |= OMAP_CTRL_PIPE3_PHY_TX_RX_POWEROFF <<
 188                        OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT;
 189        }
 190
 191        writel(val, pipe3->power_reg);
 192}
 193
 194static int pipe3_init(struct phy *phy)
 195{
 196        int ret;
 197        u32 val;
 198        struct omap_pipe3 *pipe3 = dev_get_priv(phy->dev);
 199
 200        /* Program the DPLL only if not locked */
 201        val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_STATUS);
 202        if (!(val & PLL_LOCK)) {
 203                ret = omap_pipe3_dpll_program(pipe3);
 204                if (ret)
 205                        return ret;
 206        } else {
 207                /* else just bring it out of IDLE mode */
 208                val = omap_pipe3_readl(pipe3->pll_ctrl_base,
 209                                       PLL_CONFIGURATION2);
 210                if (val & PLL_IDLE) {
 211                        val &= ~PLL_IDLE;
 212                        omap_pipe3_writel(pipe3->pll_ctrl_base,
 213                                          PLL_CONFIGURATION2, val);
 214                        ret = omap_pipe3_wait_lock(pipe3);
 215                        if (ret)
 216                                return ret;
 217                }
 218        }
 219        return 0;
 220}
 221
 222static int pipe3_power_on(struct phy *phy)
 223{
 224        struct omap_pipe3 *pipe3 = dev_get_priv(phy->dev);
 225
 226        /* Power up the PHY */
 227        omap_control_pipe3_power(pipe3, 1);
 228
 229        return 0;
 230}
 231
 232static int pipe3_power_off(struct phy *phy)
 233{
 234        struct omap_pipe3 *pipe3 = dev_get_priv(phy->dev);
 235
 236        /* Power down the PHY */
 237        omap_control_pipe3_power(pipe3, 0);
 238
 239        return 0;
 240}
 241
 242static int pipe3_exit(struct phy *phy)
 243{
 244        u32 val;
 245        int timeout = PLL_IDLE_TIME;
 246        struct omap_pipe3 *pipe3 = dev_get_priv(phy->dev);
 247
 248        pipe3_power_off(phy);
 249
 250        /* Put DPLL in IDLE mode */
 251        val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_CONFIGURATION2);
 252        val |= PLL_IDLE;
 253        omap_pipe3_writel(pipe3->pll_ctrl_base, PLL_CONFIGURATION2, val);
 254
 255        /* wait for LDO and Oscillator to power down */
 256        do {
 257                mdelay(1);
 258                val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_STATUS);
 259                if ((val & PLL_TICOPWDN) && (val & PLL_LDOPWDN))
 260                        break;
 261        } while (--timeout);
 262
 263        if (!(val & PLL_TICOPWDN) || !(val & PLL_LDOPWDN)) {
 264                pr_err("%s: Failed to power down DPLL: PLL_STATUS 0x%x\n",
 265                      __func__, val);
 266                return -EBUSY;
 267        }
 268
 269        val = readl(pipe3->pll_reset_reg);
 270        writel(val | SATA_PLL_SOFT_RESET, pipe3->pll_reset_reg);
 271        mdelay(1);
 272        writel(val & ~SATA_PLL_SOFT_RESET, pipe3->pll_reset_reg);
 273        return 0;
 274}
 275
 276static void *get_reg(struct udevice *dev, const char *name)
 277{
 278        struct udevice *syscon;
 279        struct regmap *regmap;
 280        const fdt32_t *cell;
 281        int len, err;
 282        void *base;
 283
 284        err = uclass_get_device_by_phandle(UCLASS_SYSCON, dev,
 285                                           name, &syscon);
 286        if (err) {
 287                pr_err("unable to find syscon device for %s (%d)\n",
 288                      name, err);
 289                return NULL;
 290        }
 291
 292        regmap = syscon_get_regmap(syscon);
 293        if (IS_ERR(regmap)) {
 294                pr_err("unable to find regmap for %s (%ld)\n",
 295                      name, PTR_ERR(regmap));
 296                return NULL;
 297        }
 298
 299        cell = fdt_getprop(gd->fdt_blob, dev_of_offset(dev), name,
 300                           &len);
 301        if (len < 2*sizeof(fdt32_t)) {
 302                pr_err("offset not available for %s\n", name);
 303                return NULL;
 304        }
 305
 306        base = regmap_get_range(regmap, 0);
 307        if (!base)
 308                return NULL;
 309
 310        return fdtdec_get_number(cell + 1, 1) + base;
 311}
 312
 313static int pipe3_phy_probe(struct udevice *dev)
 314{
 315        fdt_addr_t addr;
 316        fdt_size_t sz;
 317        struct omap_pipe3 *pipe3 = dev_get_priv(dev);
 318
 319        addr = devfdt_get_addr_size_index(dev, 2, &sz);
 320        if (addr == FDT_ADDR_T_NONE) {
 321                pr_err("missing pll ctrl address\n");
 322                return -EINVAL;
 323        }
 324
 325        pipe3->pll_ctrl_base = map_physmem(addr, sz, MAP_NOCACHE);
 326        if (!pipe3->pll_ctrl_base) {
 327                pr_err("unable to remap pll ctrl\n");
 328                return -EINVAL;
 329        }
 330
 331        pipe3->power_reg = get_reg(dev, "syscon-phy-power");
 332        if (!pipe3->power_reg)
 333                return -EINVAL;
 334
 335        pipe3->pll_reset_reg = get_reg(dev, "syscon-pllreset");
 336        if (!pipe3->pll_reset_reg)
 337                return -EINVAL;
 338
 339        pipe3->dpll_map = (struct pipe3_dpll_map *)dev_get_driver_data(dev);
 340
 341        return 0;
 342}
 343
 344static struct pipe3_dpll_map dpll_map_sata[] = {
 345        {12000000, {1000, 7, 4, 6, 0} },        /* 12 MHz */
 346        {16800000, {714, 7, 4, 6, 0} },         /* 16.8 MHz */
 347        {19200000, {625, 7, 4, 6, 0} },         /* 19.2 MHz */
 348        {20000000, {600, 7, 4, 6, 0} },         /* 20 MHz */
 349        {26000000, {461, 7, 4, 6, 0} },         /* 26 MHz */
 350        {38400000, {312, 7, 4, 6, 0} },         /* 38.4 MHz */
 351        { },                                    /* Terminator */
 352};
 353
 354static const struct udevice_id pipe3_phy_ids[] = {
 355        { .compatible = "ti,phy-pipe3-sata", .data = (ulong)&dpll_map_sata },
 356        { }
 357};
 358
 359static struct phy_ops pipe3_phy_ops = {
 360        .init = pipe3_init,
 361        .power_on = pipe3_power_on,
 362        .power_off = pipe3_power_off,
 363        .exit = pipe3_exit,
 364};
 365
 366U_BOOT_DRIVER(pipe3_phy) = {
 367        .name   = "pipe3_phy",
 368        .id     = UCLASS_PHY,
 369        .of_match = pipe3_phy_ids,
 370        .ops = &pipe3_phy_ops,
 371        .probe = pipe3_phy_probe,
 372        .priv_auto_alloc_size = sizeof(struct omap_pipe3),
 373};
 374