Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/media/pci/intel/ipu6/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 14 kB image not shown  

Quelle  ipu6-isys-dwc-phy.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2013--2024 Intel Corporation
 */


#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/iopoll.h>
#include <linux/math64.h>

#include "ipu6-bus.h"
#include "ipu6-isys.h"
#include "ipu6-platform-isys-csi2-reg.h"

#define IPU6_DWC_DPHY_BASE(i)   (0x238038 + 0x34 * (i))
#define IPU6_DWC_DPHY_RSTZ   0x00
#define IPU6_DWC_DPHY_SHUTDOWNZ   0x04
#define IPU6_DWC_DPHY_HSFREQRANGE  0x08
#define IPU6_DWC_DPHY_CFGCLKFREQRANGE  0x0c
#define IPU6_DWC_DPHY_TEST_IFC_ACCESS_MODE 0x10
#define IPU6_DWC_DPHY_TEST_IFC_REQ  0x14
#define IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION 0x18
#define IPU6_DWC_DPHY_DFT_CTRL0   0x28
#define IPU6_DWC_DPHY_DFT_CTRL1   0x2c
#define IPU6_DWC_DPHY_DFT_CTRL2   0x30

/*
 * test IFC request definition:
 * - req: 0 for read, 1 for write
 * - 12 bits address
 * - 8bits data (will ignore for read)
 * --24----16------4-----0
 * --|-data-|-addr-|-req-|
 */

#define IFC_REQ(req, addr, data) (FIELD_PREP(GENMASK(23, 16), data) | \
      FIELD_PREP(GENMASK(15, 4), addr) | \
      FIELD_PREP(GENMASK(1, 0), req))

#define TEST_IFC_REQ_READ 0
#define TEST_IFC_REQ_WRITE 1
#define TEST_IFC_REQ_RESET 2

#define TEST_IFC_ACCESS_MODE_FSM 0
#define TEST_IFC_ACCESS_MODE_IFC_CTL 1

enum phy_fsm_state {
 PHY_FSM_STATE_POWERON = 0,
 PHY_FSM_STATE_BGPON = 1,
 PHY_FSM_STATE_CAL_TYPE = 2,
 PHY_FSM_STATE_BURNIN_CAL = 3,
 PHY_FSM_STATE_TERMCAL = 4,
 PHY_FSM_STATE_OFFSETCAL = 5,
 PHY_FSM_STATE_OFFSET_LANE = 6,
 PHY_FSM_STATE_IDLE = 7,
 PHY_FSM_STATE_ULP = 8,
 PHY_FSM_STATE_DDLTUNNING = 9,
 PHY_FSM_STATE_SKEW_BACKWARD = 10,
 PHY_FSM_STATE_INVALID,
};

static void dwc_dphy_write(struct ipu6_isys *isys, u32 phy_id, u32 addr,
      u32 data)
{
 struct device *dev = &isys->adev->auxdev.dev;
 void __iomem *isys_base = isys->pdata->base;
 void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id);

 dev_dbg(dev, "write: reg 0x%zx = data 0x%x", base + addr - isys_base,
  data);
 writel(data, base + addr);
}

static u32 dwc_dphy_read(struct ipu6_isys *isys, u32 phy_id, u32 addr)
{
 struct device *dev = &isys->adev->auxdev.dev;
 void __iomem *isys_base = isys->pdata->base;
 void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id);
 u32 data;

 data = readl(base + addr);
 dev_dbg(dev, "read: reg 0x%zx = data 0x%x", base + addr - isys_base,
  data);

 return data;
}

static void dwc_dphy_write_mask(struct ipu6_isys *isys, u32 phy_id, u32 addr,
    u32 data, u8 shift, u8 width)
{
 u32 temp;
 u32 mask;

 mask = (1 << width) - 1;
 temp = dwc_dphy_read(isys, phy_id, addr);
 temp &= ~(mask << shift);
 temp |= (data & mask) << shift;
 dwc_dphy_write(isys, phy_id, addr, temp);
}

static u32 __maybe_unused dwc_dphy_read_mask(struct ipu6_isys *isys, u32 phy_id,
          u32 addr, u8 shift,  u8 width)
{
 u32 val;

 val = dwc_dphy_read(isys, phy_id, addr) >> shift;
 return val & ((1 << width) - 1);
}

#define DWC_DPHY_TIMEOUT (5 * USEC_PER_SEC)
static int dwc_dphy_ifc_read(struct ipu6_isys *isys, u32 phy_id, u32 addr,
        u32 *val)
{
 struct device *dev = &isys->adev->auxdev.dev;
 void __iomem *isys_base = isys->pdata->base;
 void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id);
 void __iomem *reg;
 u32 completion;
 int ret;

 dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_REQ,
         IFC_REQ(TEST_IFC_REQ_READ, addr, 0));
 reg = base + IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION;
 ret = readl_poll_timeout(reg, completion, !(completion & BIT(0)),
     10, DWC_DPHY_TIMEOUT);
 if (ret) {
  dev_err(dev, "DWC ifc request read timeout\n");
  return ret;
 }

 *val = completion >> 8 & 0xff;
 *val = FIELD_GET(GENMASK(15, 8), completion);
 dev_dbg(dev, "DWC ifc read 0x%x = 0x%x", addr, *val);

 return 0;
}

static int dwc_dphy_ifc_write(struct ipu6_isys *isys, u32 phy_id, u32 addr,
         u32 data)
{
 struct device *dev = &isys->adev->auxdev.dev;
 void __iomem *isys_base = isys->pdata->base;
 void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id);
 void __iomem *reg;
 u32 completion;
 int ret;

 dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_REQ,
         IFC_REQ(TEST_IFC_REQ_WRITE, addr, data));
 completion = readl(base + IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION);
 reg = base + IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION;
 ret = readl_poll_timeout(reg, completion, !(completion & BIT(0)),
     10, DWC_DPHY_TIMEOUT);
 if (ret)
  dev_err(dev, "DWC ifc request write timeout\n");

 return ret;
}

static void dwc_dphy_ifc_write_mask(struct ipu6_isys *isys, u32 phy_id,
        u32 addr, u32 data, u8 shift, u8 width)
{
 u32 temp, mask;
 int ret;

 ret = dwc_dphy_ifc_read(isys, phy_id, addr, &temp);
 if (ret)
  return;

 mask = (1 << width) - 1;
 temp &= ~(mask << shift);
 temp |= (data & mask) << shift;
 dwc_dphy_ifc_write(isys, phy_id, addr, temp);
}

static u32 dwc_dphy_ifc_read_mask(struct ipu6_isys *isys, u32 phy_id, u32 addr,
      u8 shift, u8 width)
{
 int ret;
 u32 val;

 ret = dwc_dphy_ifc_read(isys, phy_id, addr, &val);
 if (ret)
  return 0;

 return ((val >> shift) & ((1 << width) - 1));
}

static int dwc_dphy_pwr_up(struct ipu6_isys *isys, u32 phy_id)
{
 struct device *dev = &isys->adev->auxdev.dev;
 u32 fsm_state;
 int ret;

 dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_RSTZ, 1);
 usleep_range(10, 20);
 dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_SHUTDOWNZ, 1);

 ret = read_poll_timeout(dwc_dphy_ifc_read_mask, fsm_state,
    (fsm_state == PHY_FSM_STATE_IDLE ||
     fsm_state == PHY_FSM_STATE_ULP),
    100, DWC_DPHY_TIMEOUT, false, isys,
    phy_id, 0x1e, 0, 4);

 if (ret)
  dev_err(dev, "Dphy %d power up failed, state 0x%x", phy_id,
   fsm_state);

 return ret;
}

struct dwc_dphy_freq_range {
 u8 hsfreq;
 u16 min;
 u16 max;
 u16 default_mbps;
 u16 osc_freq_target;
};

#define DPHY_FREQ_RANGE_NUM  (63)
#define DPHY_FREQ_RANGE_INVALID_INDEX (0xff)
static const struct dwc_dphy_freq_range freqranges[DPHY_FREQ_RANGE_NUM] = {
 {0x00, 80, 97, 80, 335},
 {0x10, 80, 107, 90, 335},
 {0x20, 84, 118, 100, 335},
 {0x30, 93, 128, 110, 335},
 {0x01, 103, 139, 120, 335},
 {0x11, 112, 149, 130, 335},
 {0x21, 122, 160, 140, 335},
 {0x31, 131, 170, 150, 335},
 {0x02, 141, 181, 160, 335},
 {0x12, 150, 191, 170, 335},
 {0x22, 160, 202, 180, 335},
 {0x32, 169, 212, 190, 335},
 {0x03, 183, 228, 205, 335},
 {0x13, 198, 244, 220, 335},
 {0x23, 212, 259, 235, 335},
 {0x33, 226, 275, 250, 335},
 {0x04, 250, 301, 275, 335},
 {0x14, 274, 328, 300, 335},
 {0x25, 297, 354, 325, 335},
 {0x35, 321, 380, 350, 335},
 {0x05, 369, 433, 400, 335},
 {0x16, 416, 485, 450, 335},
 {0x26, 464, 538, 500, 335},
 {0x37, 511, 590, 550, 335},
 {0x07, 559, 643, 600, 335},
 {0x18, 606, 695, 650, 335},
 {0x28, 654, 748, 700, 335},
 {0x39, 701, 800, 750, 335},
 {0x09, 749, 853, 800, 335},
 {0x19, 796, 905, 850, 335},
 {0x29, 844, 958, 900, 335},
 {0x3a, 891, 1010, 950, 335},
 {0x0a, 939, 1063, 1000, 335},
 {0x1a, 986, 1115, 1050, 335},
 {0x2a, 1034, 1168, 1100, 335},
 {0x3b, 1081, 1220, 1150, 335},
 {0x0b, 1129, 1273, 1200, 335},
 {0x1b, 1176, 1325, 1250, 335},
 {0x2b, 1224, 1378, 1300, 335},
 {0x3c, 1271, 1430, 1350, 335},
 {0x0c, 1319, 1483, 1400, 335},
 {0x1c, 1366, 1535, 1450, 335},
 {0x2c, 1414, 1588, 1500, 335},
 {0x3d, 1461, 1640, 1550, 208},
 {0x0d, 1509, 1693, 1600, 214},
 {0x1d, 1556, 1745, 1650, 221},
 {0x2e, 1604, 1798, 1700, 228},
 {0x3e, 1651, 1850, 1750, 234},
 {0x0e, 1699, 1903, 1800, 241},
 {0x1e, 1746, 1955, 1850, 248},
 {0x2f, 1794, 2008, 1900, 255},
 {0x3f, 1841, 2060, 1950, 261},
 {0x0f, 1889, 2113, 2000, 268},
 {0x40, 1936, 2165, 2050, 275},
 {0x41, 1984, 2218, 2100, 281},
 {0x42, 2031, 2270, 2150, 288},
 {0x43, 2079, 2323, 2200, 294},
 {0x44, 2126, 2375, 2250, 302},
 {0x45, 2174, 2428, 2300, 308},
 {0x46, 2221, 2480, 2350, 315},
 {0x47, 2269, 2500, 2400, 321},
 {0x48, 2316, 2500, 2450, 328},
 {0x49, 2364, 2500, 2500, 335}
};

static u16 get_hsfreq_by_mbps(u32 mbps)
{
 unsigned int i = DPHY_FREQ_RANGE_NUM;

 while (i--) {
  if (freqranges[i].default_mbps == mbps ||
      (mbps >= freqranges[i].min && mbps <= freqranges[i].max))
   return i;
 }

 return DPHY_FREQ_RANGE_INVALID_INDEX;
}

static int ipu6_isys_dwc_phy_config(struct ipu6_isys *isys,
        u32 phy_id, u32 mbps)
{
 struct ipu6_bus_device *adev = isys->adev;
 struct device *dev = &adev->auxdev.dev;
 struct ipu6_device *isp = adev->isp;
 u32 cfg_clk_freqrange;
 u32 osc_freq_target;
 u32 index;

 dev_dbg(dev, "config Dphy %u with %u mbps", phy_id, mbps);

 index = get_hsfreq_by_mbps(mbps);
 if (index == DPHY_FREQ_RANGE_INVALID_INDEX) {
  dev_err(dev, "link freq not found for mbps %u", mbps);
  return -EINVAL;
 }

 dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_HSFREQRANGE,
       freqranges[index].hsfreq, 0, 7);

 /* Force termination Calibration */
 if (isys->phy_termcal_val) {
  dwc_dphy_ifc_write_mask(isys, phy_id, 0x20a, 0x1, 0, 1);
  dwc_dphy_ifc_write_mask(isys, phy_id, 0x209, 0x3, 0, 2);
  dwc_dphy_ifc_write_mask(isys, phy_id, 0x209,
     isys->phy_termcal_val, 4, 4);
 }

 /*
 * Enable override to configure the DDL target oscillation
 * frequency on bit 0 of register 0xe4
 */

 dwc_dphy_ifc_write_mask(isys, phy_id, 0xe4, 0x1, 0, 1);
 /*
 * configure registers 0xe2, 0xe3 with the
 * appropriate DDL target oscillation frequency
 * 0x1cc(460)
 */

 osc_freq_target = freqranges[index].osc_freq_target;
 dwc_dphy_ifc_write_mask(isys, phy_id, 0xe2,
    osc_freq_target & 0xff, 0, 8);
 dwc_dphy_ifc_write_mask(isys, phy_id, 0xe3,
    (osc_freq_target >> 8) & 0xf, 0, 4);

 if (mbps < 1500) {
  /* deskew_polarity_rw, for < 1.5Gbps */
  dwc_dphy_ifc_write_mask(isys, phy_id, 0x8, 0x1, 5, 1);
 }

 /*
 * Set cfgclkfreqrange[5:0] = round[(Fcfg_clk(MHz)-17)*4]
 * (38.4 - 17) * 4 = ~85 (0x55)
 */

 cfg_clk_freqrange = (isp->buttress.ref_clk - 170) * 4 / 10;
 dev_dbg(dev, "ref_clk = %u clk_freqrange = %u",
  isp->buttress.ref_clk, cfg_clk_freqrange);
 dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_CFGCLKFREQRANGE,
       cfg_clk_freqrange, 0, 8);

 dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0x1, 4, 1);
 dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0x1, 8, 1);

 return 0;
}

static void ipu6_isys_dwc_phy_aggr_setup(struct ipu6_isys *isys, u32 master,
      u32 slave, u32 mbps)
{
 /* Config mastermacro */
 dwc_dphy_ifc_write_mask(isys, master, 0x133, 0x1, 0, 1);
 dwc_dphy_ifc_write_mask(isys, slave, 0x133, 0x0, 0, 1);

 /* Config master PHY clk lane to drive long channel clk */
 dwc_dphy_ifc_write_mask(isys, master, 0x307, 0x1, 2, 1);
 dwc_dphy_ifc_write_mask(isys, slave, 0x307, 0x0, 2, 1);

 /* Config both PHYs data lanes to get clk from long channel */
 dwc_dphy_ifc_write_mask(isys, master, 0x508, 0x1, 5, 1);
 dwc_dphy_ifc_write_mask(isys, slave, 0x508, 0x1, 5, 1);
 dwc_dphy_ifc_write_mask(isys, master, 0x708, 0x1, 5, 1);
 dwc_dphy_ifc_write_mask(isys, slave, 0x708, 0x1, 5, 1);

 /* Config slave PHY clk lane to bypass long channel clk to DDR clk */
 dwc_dphy_ifc_write_mask(isys, master, 0x308, 0x0, 3, 1);
 dwc_dphy_ifc_write_mask(isys, slave, 0x308, 0x1, 3, 1);

 /* Override slave PHY clk lane enable (DPHYRXCLK_CLL_demux module) */
 dwc_dphy_ifc_write_mask(isys, slave, 0xe0, 0x3, 0, 2);

 /* Override slave PHY DDR clk lane enable (DPHYHSRX_div124 module) */
 dwc_dphy_ifc_write_mask(isys, slave, 0xe1, 0x1, 1, 1);
 dwc_dphy_ifc_write_mask(isys, slave, 0x307, 0x1, 3, 1);

 /* Turn off slave PHY LP-RX clk lane */
 dwc_dphy_ifc_write_mask(isys, slave, 0x304, 0x1, 7, 1);
 dwc_dphy_ifc_write_mask(isys, slave, 0x305, 0xa, 0, 5);
}

#define PHY_E 4
static int ipu6_isys_dwc_phy_powerup_ack(struct ipu6_isys *isys, u32 phy_id)
{
 struct device *dev = &isys->adev->auxdev.dev;
 u32 rescal_done;
 int ret;

 ret = dwc_dphy_pwr_up(isys, phy_id);
 if (ret != 0) {
  dev_err(dev, "Dphy %u power up failed(%d)", phy_id, ret);
  return ret;
 }

 /* reset forcerxmode */
 dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0, 4, 1);
 dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0, 8, 1);

 dev_dbg(dev, "Dphy %u is ready!", phy_id);

 if (phy_id != PHY_E || isys->phy_termcal_val)
  return 0;

 usleep_range(100, 200);
 rescal_done = dwc_dphy_ifc_read_mask(isys, phy_id, 0x221, 7, 1);
 if (rescal_done) {
  isys->phy_termcal_val = dwc_dphy_ifc_read_mask(isys, phy_id,
              0x220, 2, 4);
  dev_dbg(dev, "termcal done with value = %u",
   isys->phy_termcal_val);
 }

 return 0;
}

static void ipu6_isys_dwc_phy_reset(struct ipu6_isys *isys, u32 phy_id)
{
 dev_dbg(&isys->adev->auxdev.dev, "Reset phy %u", phy_id);

 dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_SHUTDOWNZ, 0);
 dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_RSTZ, 0);
 dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_ACCESS_MODE,
         TEST_IFC_ACCESS_MODE_FSM);
 dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_REQ,
         TEST_IFC_REQ_RESET);
}

int ipu6_isys_dwc_phy_set_power(struct ipu6_isys *isys,
    struct ipu6_isys_csi2_config *cfg,
    const struct ipu6_isys_csi2_timing *timing,
    bool on)
{
 struct device *dev = &isys->adev->auxdev.dev;
 void __iomem *isys_base = isys->pdata->base;
 u32 phy_id, primary, secondary;
 u32 nlanes, port, mbps;
 s64 link_freq;
 int ret;

 port = cfg->port;

 if (!isys_base || port >= isys->pdata->ipdata->csi2.nports) {
  dev_warn(dev, "invalid port ID %d\n", port);
  return -EINVAL;
 }

 nlanes = cfg->nlanes;
 /* only port 0, 2 and 4 support 4 lanes */
 if (nlanes == 4 && port % 2) {
  dev_err(dev, "invalid csi-port %u with %u lanes\n", port,
   nlanes);
  return -EINVAL;
 }

 link_freq = ipu6_isys_csi2_get_link_freq(&isys->csi2[port]);
 if (link_freq < 0) {
  dev_err(dev, "get link freq failed(%lld).\n", link_freq);
  return link_freq;
 }

 mbps = div_u64(link_freq, 500000);

 phy_id = port;
 primary = port & ~1;
 secondary = primary + 1;
 if (on) {
  if (nlanes == 4) {
   dev_dbg(dev, "config phy %u and %u in aggr mode\n",
    primary, secondary);

   ipu6_isys_dwc_phy_reset(isys, primary);
   ipu6_isys_dwc_phy_reset(isys, secondary);
   ipu6_isys_dwc_phy_aggr_setup(isys, primary,
           secondary, mbps);

   ret = ipu6_isys_dwc_phy_config(isys, primary, mbps);
   if (ret)
    return ret;
   ret = ipu6_isys_dwc_phy_config(isys, secondary, mbps);
   if (ret)
    return ret;

   ret = ipu6_isys_dwc_phy_powerup_ack(isys, primary);
   if (ret)
    return ret;

   ret = ipu6_isys_dwc_phy_powerup_ack(isys, secondary);
   return ret;
  }

  dev_dbg(dev, "config phy %u with %u lanes in non-aggr mode\n",
   phy_id, nlanes);

  ipu6_isys_dwc_phy_reset(isys, phy_id);
  ret = ipu6_isys_dwc_phy_config(isys, phy_id, mbps);
  if (ret)
   return ret;

  ret = ipu6_isys_dwc_phy_powerup_ack(isys, phy_id);
  return ret;
 }

 if (nlanes == 4) {
  dev_dbg(dev, "Power down phy %u and phy %u for port %u\n",
   primary, secondary, port);
  ipu6_isys_dwc_phy_reset(isys, secondary);
  ipu6_isys_dwc_phy_reset(isys, primary);

  return 0;
 }

 dev_dbg(dev, "Powerdown phy %u with %u lanes\n", phy_id, nlanes);

 ipu6_isys_dwc_phy_reset(isys, phy_id);

 return 0;
}

Messung V0.5
C=93 H=98 G=95

¤ Dauer der Verarbeitung: 0.13 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.