537 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2013--2024 Intel Corporation4 */5 6#include <linux/bitfield.h>7#include <linux/bits.h>8#include <linux/delay.h>9#include <linux/device.h>10#include <linux/iopoll.h>11#include <linux/math64.h>12 13#include "ipu6-bus.h"14#include "ipu6-isys.h"15#include "ipu6-platform-isys-csi2-reg.h"16 17#define IPU6_DWC_DPHY_BASE(i) (0x238038 + 0x34 * (i))18#define IPU6_DWC_DPHY_RSTZ 0x0019#define IPU6_DWC_DPHY_SHUTDOWNZ 0x0420#define IPU6_DWC_DPHY_HSFREQRANGE 0x0821#define IPU6_DWC_DPHY_CFGCLKFREQRANGE 0x0c22#define IPU6_DWC_DPHY_TEST_IFC_ACCESS_MODE 0x1023#define IPU6_DWC_DPHY_TEST_IFC_REQ 0x1424#define IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION 0x1825#define IPU6_DWC_DPHY_DFT_CTRL0 0x2826#define IPU6_DWC_DPHY_DFT_CTRL1 0x2c27#define IPU6_DWC_DPHY_DFT_CTRL2 0x3028 29/*30 * test IFC request definition:31 * - req: 0 for read, 1 for write32 * - 12 bits address33 * - 8bits data (will ignore for read)34 * --24----16------4-----035 * --|-data-|-addr-|-req-|36 */37#define IFC_REQ(req, addr, data) (FIELD_PREP(GENMASK(23, 16), data) | \38 FIELD_PREP(GENMASK(15, 4), addr) | \39 FIELD_PREP(GENMASK(1, 0), req))40 41#define TEST_IFC_REQ_READ 042#define TEST_IFC_REQ_WRITE 143#define TEST_IFC_REQ_RESET 244 45#define TEST_IFC_ACCESS_MODE_FSM 046#define TEST_IFC_ACCESS_MODE_IFC_CTL 147 48enum phy_fsm_state {49 PHY_FSM_STATE_POWERON = 0,50 PHY_FSM_STATE_BGPON = 1,51 PHY_FSM_STATE_CAL_TYPE = 2,52 PHY_FSM_STATE_BURNIN_CAL = 3,53 PHY_FSM_STATE_TERMCAL = 4,54 PHY_FSM_STATE_OFFSETCAL = 5,55 PHY_FSM_STATE_OFFSET_LANE = 6,56 PHY_FSM_STATE_IDLE = 7,57 PHY_FSM_STATE_ULP = 8,58 PHY_FSM_STATE_DDLTUNNING = 9,59 PHY_FSM_STATE_SKEW_BACKWARD = 10,60 PHY_FSM_STATE_INVALID,61};62 63static void dwc_dphy_write(struct ipu6_isys *isys, u32 phy_id, u32 addr,64 u32 data)65{66 struct device *dev = &isys->adev->auxdev.dev;67 void __iomem *isys_base = isys->pdata->base;68 void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id);69 70 dev_dbg(dev, "write: reg 0x%lx = data 0x%x", base + addr - isys_base,71 data);72 writel(data, base + addr);73}74 75static u32 dwc_dphy_read(struct ipu6_isys *isys, u32 phy_id, u32 addr)76{77 struct device *dev = &isys->adev->auxdev.dev;78 void __iomem *isys_base = isys->pdata->base;79 void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id);80 u32 data;81 82 data = readl(base + addr);83 dev_dbg(dev, "read: reg 0x%lx = data 0x%x", base + addr - isys_base,84 data);85 86 return data;87}88 89static void dwc_dphy_write_mask(struct ipu6_isys *isys, u32 phy_id, u32 addr,90 u32 data, u8 shift, u8 width)91{92 u32 temp;93 u32 mask;94 95 mask = (1 << width) - 1;96 temp = dwc_dphy_read(isys, phy_id, addr);97 temp &= ~(mask << shift);98 temp |= (data & mask) << shift;99 dwc_dphy_write(isys, phy_id, addr, temp);100}101 102static u32 __maybe_unused dwc_dphy_read_mask(struct ipu6_isys *isys, u32 phy_id,103 u32 addr, u8 shift, u8 width)104{105 u32 val;106 107 val = dwc_dphy_read(isys, phy_id, addr) >> shift;108 return val & ((1 << width) - 1);109}110 111#define DWC_DPHY_TIMEOUT (5 * USEC_PER_SEC)112static int dwc_dphy_ifc_read(struct ipu6_isys *isys, u32 phy_id, u32 addr,113 u32 *val)114{115 struct device *dev = &isys->adev->auxdev.dev;116 void __iomem *isys_base = isys->pdata->base;117 void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id);118 void __iomem *reg;119 u32 completion;120 int ret;121 122 dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_REQ,123 IFC_REQ(TEST_IFC_REQ_READ, addr, 0));124 reg = base + IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION;125 ret = readl_poll_timeout(reg, completion, !(completion & BIT(0)),126 10, DWC_DPHY_TIMEOUT);127 if (ret) {128 dev_err(dev, "DWC ifc request read timeout\n");129 return ret;130 }131 132 *val = completion >> 8 & 0xff;133 *val = FIELD_GET(GENMASK(15, 8), completion);134 dev_dbg(dev, "DWC ifc read 0x%x = 0x%x", addr, *val);135 136 return 0;137}138 139static int dwc_dphy_ifc_write(struct ipu6_isys *isys, u32 phy_id, u32 addr,140 u32 data)141{142 struct device *dev = &isys->adev->auxdev.dev;143 void __iomem *isys_base = isys->pdata->base;144 void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id);145 void __iomem *reg;146 u32 completion;147 int ret;148 149 dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_REQ,150 IFC_REQ(TEST_IFC_REQ_WRITE, addr, data));151 completion = readl(base + IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION);152 reg = base + IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION;153 ret = readl_poll_timeout(reg, completion, !(completion & BIT(0)),154 10, DWC_DPHY_TIMEOUT);155 if (ret)156 dev_err(dev, "DWC ifc request write timeout\n");157 158 return ret;159}160 161static void dwc_dphy_ifc_write_mask(struct ipu6_isys *isys, u32 phy_id,162 u32 addr, u32 data, u8 shift, u8 width)163{164 u32 temp, mask;165 int ret;166 167 ret = dwc_dphy_ifc_read(isys, phy_id, addr, &temp);168 if (ret)169 return;170 171 mask = (1 << width) - 1;172 temp &= ~(mask << shift);173 temp |= (data & mask) << shift;174 dwc_dphy_ifc_write(isys, phy_id, addr, temp);175}176 177static u32 dwc_dphy_ifc_read_mask(struct ipu6_isys *isys, u32 phy_id, u32 addr,178 u8 shift, u8 width)179{180 int ret;181 u32 val;182 183 ret = dwc_dphy_ifc_read(isys, phy_id, addr, &val);184 if (ret)185 return 0;186 187 return ((val >> shift) & ((1 << width) - 1));188}189 190static int dwc_dphy_pwr_up(struct ipu6_isys *isys, u32 phy_id)191{192 struct device *dev = &isys->adev->auxdev.dev;193 u32 fsm_state;194 int ret;195 196 dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_RSTZ, 1);197 usleep_range(10, 20);198 dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_SHUTDOWNZ, 1);199 200 ret = read_poll_timeout(dwc_dphy_ifc_read_mask, fsm_state,201 (fsm_state == PHY_FSM_STATE_IDLE ||202 fsm_state == PHY_FSM_STATE_ULP),203 100, DWC_DPHY_TIMEOUT, false, isys,204 phy_id, 0x1e, 0, 4);205 206 if (ret)207 dev_err(dev, "Dphy %d power up failed, state 0x%x", phy_id,208 fsm_state);209 210 return ret;211}212 213struct dwc_dphy_freq_range {214 u8 hsfreq;215 u16 min;216 u16 max;217 u16 default_mbps;218 u16 osc_freq_target;219};220 221#define DPHY_FREQ_RANGE_NUM (63)222#define DPHY_FREQ_RANGE_INVALID_INDEX (0xff)223static const struct dwc_dphy_freq_range freqranges[DPHY_FREQ_RANGE_NUM] = {224 {0x00, 80, 97, 80, 335},225 {0x10, 80, 107, 90, 335},226 {0x20, 84, 118, 100, 335},227 {0x30, 93, 128, 110, 335},228 {0x01, 103, 139, 120, 335},229 {0x11, 112, 149, 130, 335},230 {0x21, 122, 160, 140, 335},231 {0x31, 131, 170, 150, 335},232 {0x02, 141, 181, 160, 335},233 {0x12, 150, 191, 170, 335},234 {0x22, 160, 202, 180, 335},235 {0x32, 169, 212, 190, 335},236 {0x03, 183, 228, 205, 335},237 {0x13, 198, 244, 220, 335},238 {0x23, 212, 259, 235, 335},239 {0x33, 226, 275, 250, 335},240 {0x04, 250, 301, 275, 335},241 {0x14, 274, 328, 300, 335},242 {0x25, 297, 354, 325, 335},243 {0x35, 321, 380, 350, 335},244 {0x05, 369, 433, 400, 335},245 {0x16, 416, 485, 450, 335},246 {0x26, 464, 538, 500, 335},247 {0x37, 511, 590, 550, 335},248 {0x07, 559, 643, 600, 335},249 {0x18, 606, 695, 650, 335},250 {0x28, 654, 748, 700, 335},251 {0x39, 701, 800, 750, 335},252 {0x09, 749, 853, 800, 335},253 {0x19, 796, 905, 850, 335},254 {0x29, 844, 958, 900, 335},255 {0x3a, 891, 1010, 950, 335},256 {0x0a, 939, 1063, 1000, 335},257 {0x1a, 986, 1115, 1050, 335},258 {0x2a, 1034, 1168, 1100, 335},259 {0x3b, 1081, 1220, 1150, 335},260 {0x0b, 1129, 1273, 1200, 335},261 {0x1b, 1176, 1325, 1250, 335},262 {0x2b, 1224, 1378, 1300, 335},263 {0x3c, 1271, 1430, 1350, 335},264 {0x0c, 1319, 1483, 1400, 335},265 {0x1c, 1366, 1535, 1450, 335},266 {0x2c, 1414, 1588, 1500, 335},267 {0x3d, 1461, 1640, 1550, 208},268 {0x0d, 1509, 1693, 1600, 214},269 {0x1d, 1556, 1745, 1650, 221},270 {0x2e, 1604, 1798, 1700, 228},271 {0x3e, 1651, 1850, 1750, 234},272 {0x0e, 1699, 1903, 1800, 241},273 {0x1e, 1746, 1955, 1850, 248},274 {0x2f, 1794, 2008, 1900, 255},275 {0x3f, 1841, 2060, 1950, 261},276 {0x0f, 1889, 2113, 2000, 268},277 {0x40, 1936, 2165, 2050, 275},278 {0x41, 1984, 2218, 2100, 281},279 {0x42, 2031, 2270, 2150, 288},280 {0x43, 2079, 2323, 2200, 294},281 {0x44, 2126, 2375, 2250, 302},282 {0x45, 2174, 2428, 2300, 308},283 {0x46, 2221, 2480, 2350, 315},284 {0x47, 2269, 2500, 2400, 321},285 {0x48, 2316, 2500, 2450, 328},286 {0x49, 2364, 2500, 2500, 335}287};288 289static u16 get_hsfreq_by_mbps(u32 mbps)290{291 unsigned int i = DPHY_FREQ_RANGE_NUM;292 293 while (i--) {294 if (freqranges[i].default_mbps == mbps ||295 (mbps >= freqranges[i].min && mbps <= freqranges[i].max))296 return i;297 }298 299 return DPHY_FREQ_RANGE_INVALID_INDEX;300}301 302static int ipu6_isys_dwc_phy_config(struct ipu6_isys *isys,303 u32 phy_id, u32 mbps)304{305 struct ipu6_bus_device *adev = isys->adev;306 struct device *dev = &adev->auxdev.dev;307 struct ipu6_device *isp = adev->isp;308 u32 cfg_clk_freqrange;309 u32 osc_freq_target;310 u32 index;311 312 dev_dbg(dev, "config Dphy %u with %u mbps", phy_id, mbps);313 314 index = get_hsfreq_by_mbps(mbps);315 if (index == DPHY_FREQ_RANGE_INVALID_INDEX) {316 dev_err(dev, "link freq not found for mbps %u", mbps);317 return -EINVAL;318 }319 320 dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_HSFREQRANGE,321 freqranges[index].hsfreq, 0, 7);322 323 /* Force termination Calibration */324 if (isys->phy_termcal_val) {325 dwc_dphy_ifc_write_mask(isys, phy_id, 0x20a, 0x1, 0, 1);326 dwc_dphy_ifc_write_mask(isys, phy_id, 0x209, 0x3, 0, 2);327 dwc_dphy_ifc_write_mask(isys, phy_id, 0x209,328 isys->phy_termcal_val, 4, 4);329 }330 331 /*332 * Enable override to configure the DDL target oscillation333 * frequency on bit 0 of register 0xe4334 */335 dwc_dphy_ifc_write_mask(isys, phy_id, 0xe4, 0x1, 0, 1);336 /*337 * configure registers 0xe2, 0xe3 with the338 * appropriate DDL target oscillation frequency339 * 0x1cc(460)340 */341 osc_freq_target = freqranges[index].osc_freq_target;342 dwc_dphy_ifc_write_mask(isys, phy_id, 0xe2,343 osc_freq_target & 0xff, 0, 8);344 dwc_dphy_ifc_write_mask(isys, phy_id, 0xe3,345 (osc_freq_target >> 8) & 0xf, 0, 4);346 347 if (mbps < 1500) {348 /* deskew_polarity_rw, for < 1.5Gbps */349 dwc_dphy_ifc_write_mask(isys, phy_id, 0x8, 0x1, 5, 1);350 }351 352 /*353 * Set cfgclkfreqrange[5:0] = round[(Fcfg_clk(MHz)-17)*4]354 * (38.4 - 17) * 4 = ~85 (0x55)355 */356 cfg_clk_freqrange = (isp->buttress.ref_clk - 170) * 4 / 10;357 dev_dbg(dev, "ref_clk = %u clk_freqrange = %u",358 isp->buttress.ref_clk, cfg_clk_freqrange);359 dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_CFGCLKFREQRANGE,360 cfg_clk_freqrange, 0, 8);361 362 dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0x1, 4, 1);363 dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0x1, 8, 1);364 365 return 0;366}367 368static void ipu6_isys_dwc_phy_aggr_setup(struct ipu6_isys *isys, u32 master,369 u32 slave, u32 mbps)370{371 /* Config mastermacro */372 dwc_dphy_ifc_write_mask(isys, master, 0x133, 0x1, 0, 1);373 dwc_dphy_ifc_write_mask(isys, slave, 0x133, 0x0, 0, 1);374 375 /* Config master PHY clk lane to drive long channel clk */376 dwc_dphy_ifc_write_mask(isys, master, 0x307, 0x1, 2, 1);377 dwc_dphy_ifc_write_mask(isys, slave, 0x307, 0x0, 2, 1);378 379 /* Config both PHYs data lanes to get clk from long channel */380 dwc_dphy_ifc_write_mask(isys, master, 0x508, 0x1, 5, 1);381 dwc_dphy_ifc_write_mask(isys, slave, 0x508, 0x1, 5, 1);382 dwc_dphy_ifc_write_mask(isys, master, 0x708, 0x1, 5, 1);383 dwc_dphy_ifc_write_mask(isys, slave, 0x708, 0x1, 5, 1);384 385 /* Config slave PHY clk lane to bypass long channel clk to DDR clk */386 dwc_dphy_ifc_write_mask(isys, master, 0x308, 0x0, 3, 1);387 dwc_dphy_ifc_write_mask(isys, slave, 0x308, 0x1, 3, 1);388 389 /* Override slave PHY clk lane enable (DPHYRXCLK_CLL_demux module) */390 dwc_dphy_ifc_write_mask(isys, slave, 0xe0, 0x3, 0, 2);391 392 /* Override slave PHY DDR clk lane enable (DPHYHSRX_div124 module) */393 dwc_dphy_ifc_write_mask(isys, slave, 0xe1, 0x1, 1, 1);394 dwc_dphy_ifc_write_mask(isys, slave, 0x307, 0x1, 3, 1);395 396 /* Turn off slave PHY LP-RX clk lane */397 dwc_dphy_ifc_write_mask(isys, slave, 0x304, 0x1, 7, 1);398 dwc_dphy_ifc_write_mask(isys, slave, 0x305, 0xa, 0, 5);399}400 401#define PHY_E 4402static int ipu6_isys_dwc_phy_powerup_ack(struct ipu6_isys *isys, u32 phy_id)403{404 struct device *dev = &isys->adev->auxdev.dev;405 u32 rescal_done;406 int ret;407 408 ret = dwc_dphy_pwr_up(isys, phy_id);409 if (ret != 0) {410 dev_err(dev, "Dphy %u power up failed(%d)", phy_id, ret);411 return ret;412 }413 414 /* reset forcerxmode */415 dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0, 4, 1);416 dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0, 8, 1);417 418 dev_dbg(dev, "Dphy %u is ready!", phy_id);419 420 if (phy_id != PHY_E || isys->phy_termcal_val)421 return 0;422 423 usleep_range(100, 200);424 rescal_done = dwc_dphy_ifc_read_mask(isys, phy_id, 0x221, 7, 1);425 if (rescal_done) {426 isys->phy_termcal_val = dwc_dphy_ifc_read_mask(isys, phy_id,427 0x220, 2, 4);428 dev_dbg(dev, "termcal done with value = %u",429 isys->phy_termcal_val);430 }431 432 return 0;433}434 435static void ipu6_isys_dwc_phy_reset(struct ipu6_isys *isys, u32 phy_id)436{437 dev_dbg(&isys->adev->auxdev.dev, "Reset phy %u", phy_id);438 439 dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_SHUTDOWNZ, 0);440 dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_RSTZ, 0);441 dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_ACCESS_MODE,442 TEST_IFC_ACCESS_MODE_FSM);443 dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_REQ,444 TEST_IFC_REQ_RESET);445}446 447int ipu6_isys_dwc_phy_set_power(struct ipu6_isys *isys,448 struct ipu6_isys_csi2_config *cfg,449 const struct ipu6_isys_csi2_timing *timing,450 bool on)451{452 struct device *dev = &isys->adev->auxdev.dev;453 void __iomem *isys_base = isys->pdata->base;454 u32 phy_id, primary, secondary;455 u32 nlanes, port, mbps;456 s64 link_freq;457 int ret;458 459 port = cfg->port;460 461 if (!isys_base || port >= isys->pdata->ipdata->csi2.nports) {462 dev_warn(dev, "invalid port ID %d\n", port);463 return -EINVAL;464 }465 466 nlanes = cfg->nlanes;467 /* only port 0, 2 and 4 support 4 lanes */468 if (nlanes == 4 && port % 2) {469 dev_err(dev, "invalid csi-port %u with %u lanes\n", port,470 nlanes);471 return -EINVAL;472 }473 474 link_freq = ipu6_isys_csi2_get_link_freq(&isys->csi2[port]);475 if (link_freq < 0) {476 dev_err(dev, "get link freq failed(%lld).\n", link_freq);477 return link_freq;478 }479 480 mbps = div_u64(link_freq, 500000);481 482 phy_id = port;483 primary = port & ~1;484 secondary = primary + 1;485 if (on) {486 if (nlanes == 4) {487 dev_dbg(dev, "config phy %u and %u in aggr mode\n",488 primary, secondary);489 490 ipu6_isys_dwc_phy_reset(isys, primary);491 ipu6_isys_dwc_phy_reset(isys, secondary);492 ipu6_isys_dwc_phy_aggr_setup(isys, primary,493 secondary, mbps);494 495 ret = ipu6_isys_dwc_phy_config(isys, primary, mbps);496 if (ret)497 return ret;498 ret = ipu6_isys_dwc_phy_config(isys, secondary, mbps);499 if (ret)500 return ret;501 502 ret = ipu6_isys_dwc_phy_powerup_ack(isys, primary);503 if (ret)504 return ret;505 506 ret = ipu6_isys_dwc_phy_powerup_ack(isys, secondary);507 return ret;508 }509 510 dev_dbg(dev, "config phy %u with %u lanes in non-aggr mode\n",511 phy_id, nlanes);512 513 ipu6_isys_dwc_phy_reset(isys, phy_id);514 ret = ipu6_isys_dwc_phy_config(isys, phy_id, mbps);515 if (ret)516 return ret;517 518 ret = ipu6_isys_dwc_phy_powerup_ack(isys, phy_id);519 return ret;520 }521 522 if (nlanes == 4) {523 dev_dbg(dev, "Power down phy %u and phy %u for port %u\n",524 primary, secondary, port);525 ipu6_isys_dwc_phy_reset(isys, secondary);526 ipu6_isys_dwc_phy_reset(isys, primary);527 528 return 0;529 }530 531 dev_dbg(dev, "Powerdown phy %u with %u lanes\n", phy_id, nlanes);532 533 ipu6_isys_dwc_phy_reset(isys, phy_id);534 535 return 0;536}537