17a343c8bSMaxime Ripard // SPDX-License-Identifier: GPL-2.0+ 27a343c8bSMaxime Ripard /* 37a343c8bSMaxime Ripard * Copyright: 2017-2018 Cadence Design Systems, Inc. 47a343c8bSMaxime Ripard */ 57a343c8bSMaxime Ripard 6*efcd5f52SRahul T R #include <linux/bitfield.h> 77a343c8bSMaxime Ripard #include <linux/bitops.h> 87a343c8bSMaxime Ripard #include <linux/clk.h> 97a343c8bSMaxime Ripard #include <linux/io.h> 107a343c8bSMaxime Ripard #include <linux/module.h> 117a343c8bSMaxime Ripard #include <linux/of_address.h> 127a343c8bSMaxime Ripard #include <linux/of_device.h> 137a343c8bSMaxime Ripard #include <linux/platform_device.h> 147a343c8bSMaxime Ripard #include <linux/reset.h> 157a343c8bSMaxime Ripard 167a343c8bSMaxime Ripard #include <linux/phy/phy.h> 177a343c8bSMaxime Ripard #include <linux/phy/phy-mipi-dphy.h> 187a343c8bSMaxime Ripard 197a343c8bSMaxime Ripard #define REG_WAKEUP_TIME_NS 800 207a343c8bSMaxime Ripard #define DPHY_PLL_RATE_HZ 108000000 217a343c8bSMaxime Ripard 227a343c8bSMaxime Ripard /* DPHY registers */ 237a343c8bSMaxime Ripard #define DPHY_PMA_CMN(reg) (reg) 247a343c8bSMaxime Ripard #define DPHY_PMA_LCLK(reg) (0x100 + (reg)) 257a343c8bSMaxime Ripard #define DPHY_PMA_LDATA(lane, reg) (0x200 + ((lane) * 0x100) + (reg)) 267a343c8bSMaxime Ripard #define DPHY_PMA_RCLK(reg) (0x600 + (reg)) 277a343c8bSMaxime Ripard #define DPHY_PMA_RDATA(lane, reg) (0x700 + ((lane) * 0x100) + (reg)) 287a343c8bSMaxime Ripard #define DPHY_PCS(reg) (0xb00 + (reg)) 297a343c8bSMaxime Ripard 307a343c8bSMaxime Ripard #define DPHY_CMN_SSM DPHY_PMA_CMN(0x20) 317a343c8bSMaxime Ripard #define DPHY_CMN_SSM_EN BIT(0) 327a343c8bSMaxime Ripard #define DPHY_CMN_TX_MODE_EN BIT(9) 337a343c8bSMaxime Ripard 347a343c8bSMaxime Ripard #define DPHY_CMN_PWM DPHY_PMA_CMN(0x40) 357a343c8bSMaxime Ripard #define DPHY_CMN_PWM_DIV(x) ((x) << 20) 367a343c8bSMaxime Ripard #define DPHY_CMN_PWM_LOW(x) ((x) << 10) 377a343c8bSMaxime Ripard #define DPHY_CMN_PWM_HIGH(x) (x) 387a343c8bSMaxime Ripard 397a343c8bSMaxime Ripard #define DPHY_CMN_FBDIV DPHY_PMA_CMN(0x4c) 407a343c8bSMaxime Ripard #define DPHY_CMN_FBDIV_VAL(low, high) (((high) << 11) | ((low) << 22)) 417a343c8bSMaxime Ripard #define DPHY_CMN_FBDIV_FROM_REG (BIT(10) | BIT(21)) 427a343c8bSMaxime Ripard 437a343c8bSMaxime Ripard #define DPHY_CMN_OPIPDIV DPHY_PMA_CMN(0x50) 447a343c8bSMaxime Ripard #define DPHY_CMN_IPDIV_FROM_REG BIT(0) 457a343c8bSMaxime Ripard #define DPHY_CMN_IPDIV(x) ((x) << 1) 467a343c8bSMaxime Ripard #define DPHY_CMN_OPDIV_FROM_REG BIT(6) 477a343c8bSMaxime Ripard #define DPHY_CMN_OPDIV(x) ((x) << 7) 487a343c8bSMaxime Ripard 49*efcd5f52SRahul T R #define DPHY_BAND_CFG DPHY_PCS(0x0) 50*efcd5f52SRahul T R #define DPHY_BAND_CFG_LEFT_BAND GENMASK(4, 0) 51*efcd5f52SRahul T R #define DPHY_BAND_CFG_RIGHT_BAND GENMASK(9, 5) 52*efcd5f52SRahul T R 537a343c8bSMaxime Ripard #define DPHY_PSM_CFG DPHY_PCS(0x4) 547a343c8bSMaxime Ripard #define DPHY_PSM_CFG_FROM_REG BIT(0) 557a343c8bSMaxime Ripard #define DPHY_PSM_CLK_DIV(x) ((x) << 1) 567a343c8bSMaxime Ripard 577a343c8bSMaxime Ripard #define DSI_HBP_FRAME_OVERHEAD 12 587a343c8bSMaxime Ripard #define DSI_HSA_FRAME_OVERHEAD 14 597a343c8bSMaxime Ripard #define DSI_HFP_FRAME_OVERHEAD 6 607a343c8bSMaxime Ripard #define DSI_HSS_VSS_VSE_FRAME_OVERHEAD 4 617a343c8bSMaxime Ripard #define DSI_BLANKING_FRAME_OVERHEAD 6 627a343c8bSMaxime Ripard #define DSI_NULL_FRAME_OVERHEAD 6 637a343c8bSMaxime Ripard #define DSI_EOT_PKT_SIZE 4 647a343c8bSMaxime Ripard 657a343c8bSMaxime Ripard struct cdns_dphy_cfg { 667a343c8bSMaxime Ripard u8 pll_ipdiv; 677a343c8bSMaxime Ripard u8 pll_opdiv; 687a343c8bSMaxime Ripard u16 pll_fbdiv; 697a343c8bSMaxime Ripard unsigned int nlanes; 707a343c8bSMaxime Ripard }; 717a343c8bSMaxime Ripard 727a343c8bSMaxime Ripard enum cdns_dphy_clk_lane_cfg { 737a343c8bSMaxime Ripard DPHY_CLK_CFG_LEFT_DRIVES_ALL = 0, 747a343c8bSMaxime Ripard DPHY_CLK_CFG_LEFT_DRIVES_RIGHT = 1, 757a343c8bSMaxime Ripard DPHY_CLK_CFG_LEFT_DRIVES_LEFT = 2, 767a343c8bSMaxime Ripard DPHY_CLK_CFG_RIGHT_DRIVES_ALL = 3, 777a343c8bSMaxime Ripard }; 787a343c8bSMaxime Ripard 797a343c8bSMaxime Ripard struct cdns_dphy; 807a343c8bSMaxime Ripard struct cdns_dphy_ops { 817a343c8bSMaxime Ripard int (*probe)(struct cdns_dphy *dphy); 827a343c8bSMaxime Ripard void (*remove)(struct cdns_dphy *dphy); 837a343c8bSMaxime Ripard void (*set_psm_div)(struct cdns_dphy *dphy, u8 div); 847a343c8bSMaxime Ripard void (*set_clk_lane_cfg)(struct cdns_dphy *dphy, 857a343c8bSMaxime Ripard enum cdns_dphy_clk_lane_cfg cfg); 867a343c8bSMaxime Ripard void (*set_pll_cfg)(struct cdns_dphy *dphy, 877a343c8bSMaxime Ripard const struct cdns_dphy_cfg *cfg); 887a343c8bSMaxime Ripard unsigned long (*get_wakeup_time_ns)(struct cdns_dphy *dphy); 897a343c8bSMaxime Ripard }; 907a343c8bSMaxime Ripard 917a343c8bSMaxime Ripard struct cdns_dphy { 927a343c8bSMaxime Ripard struct cdns_dphy_cfg cfg; 937a343c8bSMaxime Ripard void __iomem *regs; 947a343c8bSMaxime Ripard struct clk *psm_clk; 957a343c8bSMaxime Ripard struct clk *pll_ref_clk; 967a343c8bSMaxime Ripard const struct cdns_dphy_ops *ops; 977a343c8bSMaxime Ripard struct phy *phy; 987a343c8bSMaxime Ripard }; 997a343c8bSMaxime Ripard 100*efcd5f52SRahul T R /* Order of bands is important since the index is the band number. */ 101*efcd5f52SRahul T R static const unsigned int tx_bands[] = { 102*efcd5f52SRahul T R 80, 100, 120, 160, 200, 240, 320, 390, 450, 510, 560, 640, 690, 770, 103*efcd5f52SRahul T R 870, 950, 1000, 1200, 1400, 1600, 1800, 2000, 2200, 2500 104*efcd5f52SRahul T R }; 105*efcd5f52SRahul T R 1067a343c8bSMaxime Ripard static int cdns_dsi_get_dphy_pll_cfg(struct cdns_dphy *dphy, 1077a343c8bSMaxime Ripard struct cdns_dphy_cfg *cfg, 1087a343c8bSMaxime Ripard struct phy_configure_opts_mipi_dphy *opts, 1097a343c8bSMaxime Ripard unsigned int *dsi_hfp_ext) 1107a343c8bSMaxime Ripard { 1117a343c8bSMaxime Ripard unsigned long pll_ref_hz = clk_get_rate(dphy->pll_ref_clk); 1127a343c8bSMaxime Ripard u64 dlane_bps; 1137a343c8bSMaxime Ripard 1147a343c8bSMaxime Ripard memset(cfg, 0, sizeof(*cfg)); 1157a343c8bSMaxime Ripard 1167a343c8bSMaxime Ripard if (pll_ref_hz < 9600000 || pll_ref_hz >= 150000000) 1177a343c8bSMaxime Ripard return -EINVAL; 1187a343c8bSMaxime Ripard else if (pll_ref_hz < 19200000) 1197a343c8bSMaxime Ripard cfg->pll_ipdiv = 1; 1207a343c8bSMaxime Ripard else if (pll_ref_hz < 38400000) 1217a343c8bSMaxime Ripard cfg->pll_ipdiv = 2; 1227a343c8bSMaxime Ripard else if (pll_ref_hz < 76800000) 1237a343c8bSMaxime Ripard cfg->pll_ipdiv = 4; 1247a343c8bSMaxime Ripard else 1257a343c8bSMaxime Ripard cfg->pll_ipdiv = 8; 1267a343c8bSMaxime Ripard 1277a343c8bSMaxime Ripard dlane_bps = opts->hs_clk_rate; 1287a343c8bSMaxime Ripard 1297a343c8bSMaxime Ripard if (dlane_bps > 2500000000UL || dlane_bps < 160000000UL) 1307a343c8bSMaxime Ripard return -EINVAL; 1317a343c8bSMaxime Ripard else if (dlane_bps >= 1250000000) 1327a343c8bSMaxime Ripard cfg->pll_opdiv = 1; 1337a343c8bSMaxime Ripard else if (dlane_bps >= 630000000) 1347a343c8bSMaxime Ripard cfg->pll_opdiv = 2; 1357a343c8bSMaxime Ripard else if (dlane_bps >= 320000000) 1367a343c8bSMaxime Ripard cfg->pll_opdiv = 4; 1377a343c8bSMaxime Ripard else if (dlane_bps >= 160000000) 1387a343c8bSMaxime Ripard cfg->pll_opdiv = 8; 1397a343c8bSMaxime Ripard 1407a343c8bSMaxime Ripard cfg->pll_fbdiv = DIV_ROUND_UP_ULL(dlane_bps * 2 * cfg->pll_opdiv * 1417a343c8bSMaxime Ripard cfg->pll_ipdiv, 1427a343c8bSMaxime Ripard pll_ref_hz); 1437a343c8bSMaxime Ripard 1447a343c8bSMaxime Ripard return 0; 1457a343c8bSMaxime Ripard } 1467a343c8bSMaxime Ripard 1477a343c8bSMaxime Ripard static int cdns_dphy_setup_psm(struct cdns_dphy *dphy) 1487a343c8bSMaxime Ripard { 1497a343c8bSMaxime Ripard unsigned long psm_clk_hz = clk_get_rate(dphy->psm_clk); 1507a343c8bSMaxime Ripard unsigned long psm_div; 1517a343c8bSMaxime Ripard 1527a343c8bSMaxime Ripard if (!psm_clk_hz || psm_clk_hz > 100000000) 1537a343c8bSMaxime Ripard return -EINVAL; 1547a343c8bSMaxime Ripard 1557a343c8bSMaxime Ripard psm_div = DIV_ROUND_CLOSEST(psm_clk_hz, 1000000); 1567a343c8bSMaxime Ripard if (dphy->ops->set_psm_div) 1577a343c8bSMaxime Ripard dphy->ops->set_psm_div(dphy, psm_div); 1587a343c8bSMaxime Ripard 1597a343c8bSMaxime Ripard return 0; 1607a343c8bSMaxime Ripard } 1617a343c8bSMaxime Ripard 1627a343c8bSMaxime Ripard static void cdns_dphy_set_clk_lane_cfg(struct cdns_dphy *dphy, 1637a343c8bSMaxime Ripard enum cdns_dphy_clk_lane_cfg cfg) 1647a343c8bSMaxime Ripard { 1657a343c8bSMaxime Ripard if (dphy->ops->set_clk_lane_cfg) 1667a343c8bSMaxime Ripard dphy->ops->set_clk_lane_cfg(dphy, cfg); 1677a343c8bSMaxime Ripard } 1687a343c8bSMaxime Ripard 1697a343c8bSMaxime Ripard static void cdns_dphy_set_pll_cfg(struct cdns_dphy *dphy, 1707a343c8bSMaxime Ripard const struct cdns_dphy_cfg *cfg) 1717a343c8bSMaxime Ripard { 1727a343c8bSMaxime Ripard if (dphy->ops->set_pll_cfg) 1737a343c8bSMaxime Ripard dphy->ops->set_pll_cfg(dphy, cfg); 1747a343c8bSMaxime Ripard } 1757a343c8bSMaxime Ripard 1767a343c8bSMaxime Ripard static unsigned long cdns_dphy_get_wakeup_time_ns(struct cdns_dphy *dphy) 1777a343c8bSMaxime Ripard { 1787a343c8bSMaxime Ripard return dphy->ops->get_wakeup_time_ns(dphy); 1797a343c8bSMaxime Ripard } 1807a343c8bSMaxime Ripard 1817a343c8bSMaxime Ripard static unsigned long cdns_dphy_ref_get_wakeup_time_ns(struct cdns_dphy *dphy) 1827a343c8bSMaxime Ripard { 1837a343c8bSMaxime Ripard /* Default wakeup time is 800 ns (in a simulated environment). */ 1847a343c8bSMaxime Ripard return 800; 1857a343c8bSMaxime Ripard } 1867a343c8bSMaxime Ripard 1877a343c8bSMaxime Ripard static void cdns_dphy_ref_set_pll_cfg(struct cdns_dphy *dphy, 1887a343c8bSMaxime Ripard const struct cdns_dphy_cfg *cfg) 1897a343c8bSMaxime Ripard { 1907a343c8bSMaxime Ripard u32 fbdiv_low, fbdiv_high; 1917a343c8bSMaxime Ripard 1927a343c8bSMaxime Ripard fbdiv_low = (cfg->pll_fbdiv / 4) - 2; 1937a343c8bSMaxime Ripard fbdiv_high = cfg->pll_fbdiv - fbdiv_low - 2; 1947a343c8bSMaxime Ripard 1957a343c8bSMaxime Ripard writel(DPHY_CMN_IPDIV_FROM_REG | DPHY_CMN_OPDIV_FROM_REG | 1967a343c8bSMaxime Ripard DPHY_CMN_IPDIV(cfg->pll_ipdiv) | 1977a343c8bSMaxime Ripard DPHY_CMN_OPDIV(cfg->pll_opdiv), 1987a343c8bSMaxime Ripard dphy->regs + DPHY_CMN_OPIPDIV); 1997a343c8bSMaxime Ripard writel(DPHY_CMN_FBDIV_FROM_REG | 2007a343c8bSMaxime Ripard DPHY_CMN_FBDIV_VAL(fbdiv_low, fbdiv_high), 2017a343c8bSMaxime Ripard dphy->regs + DPHY_CMN_FBDIV); 2027a343c8bSMaxime Ripard writel(DPHY_CMN_PWM_HIGH(6) | DPHY_CMN_PWM_LOW(0x101) | 2037a343c8bSMaxime Ripard DPHY_CMN_PWM_DIV(0x8), 2047a343c8bSMaxime Ripard dphy->regs + DPHY_CMN_PWM); 2057a343c8bSMaxime Ripard } 2067a343c8bSMaxime Ripard 2077a343c8bSMaxime Ripard static void cdns_dphy_ref_set_psm_div(struct cdns_dphy *dphy, u8 div) 2087a343c8bSMaxime Ripard { 2097a343c8bSMaxime Ripard writel(DPHY_PSM_CFG_FROM_REG | DPHY_PSM_CLK_DIV(div), 2107a343c8bSMaxime Ripard dphy->regs + DPHY_PSM_CFG); 2117a343c8bSMaxime Ripard } 2127a343c8bSMaxime Ripard 2137a343c8bSMaxime Ripard /* 2147a343c8bSMaxime Ripard * This is the reference implementation of DPHY hooks. Specific integration of 2157a343c8bSMaxime Ripard * this IP may have to re-implement some of them depending on how they decided 2167a343c8bSMaxime Ripard * to wire things in the SoC. 2177a343c8bSMaxime Ripard */ 2187a343c8bSMaxime Ripard static const struct cdns_dphy_ops ref_dphy_ops = { 2197a343c8bSMaxime Ripard .get_wakeup_time_ns = cdns_dphy_ref_get_wakeup_time_ns, 2207a343c8bSMaxime Ripard .set_pll_cfg = cdns_dphy_ref_set_pll_cfg, 2217a343c8bSMaxime Ripard .set_psm_div = cdns_dphy_ref_set_psm_div, 2227a343c8bSMaxime Ripard }; 2237a343c8bSMaxime Ripard 2247a343c8bSMaxime Ripard static int cdns_dphy_config_from_opts(struct phy *phy, 2257a343c8bSMaxime Ripard struct phy_configure_opts_mipi_dphy *opts, 2267a343c8bSMaxime Ripard struct cdns_dphy_cfg *cfg) 2277a343c8bSMaxime Ripard { 2287a343c8bSMaxime Ripard struct cdns_dphy *dphy = phy_get_drvdata(phy); 2297a343c8bSMaxime Ripard unsigned int dsi_hfp_ext = 0; 2307a343c8bSMaxime Ripard int ret; 2317a343c8bSMaxime Ripard 2327a343c8bSMaxime Ripard ret = phy_mipi_dphy_config_validate(opts); 2337a343c8bSMaxime Ripard if (ret) 2347a343c8bSMaxime Ripard return ret; 2357a343c8bSMaxime Ripard 2367a343c8bSMaxime Ripard ret = cdns_dsi_get_dphy_pll_cfg(dphy, cfg, 2377a343c8bSMaxime Ripard opts, &dsi_hfp_ext); 2387a343c8bSMaxime Ripard if (ret) 2397a343c8bSMaxime Ripard return ret; 2407a343c8bSMaxime Ripard 2417a343c8bSMaxime Ripard opts->wakeup = cdns_dphy_get_wakeup_time_ns(dphy) / 1000; 2427a343c8bSMaxime Ripard 2437a343c8bSMaxime Ripard return 0; 2447a343c8bSMaxime Ripard } 2457a343c8bSMaxime Ripard 246*efcd5f52SRahul T R static int cdns_dphy_tx_get_band_ctrl(unsigned long hs_clk_rate) 247*efcd5f52SRahul T R { 248*efcd5f52SRahul T R unsigned int rate; 249*efcd5f52SRahul T R int i; 250*efcd5f52SRahul T R 251*efcd5f52SRahul T R rate = hs_clk_rate / 1000000UL; 252*efcd5f52SRahul T R 253*efcd5f52SRahul T R if (rate < tx_bands[0]) 254*efcd5f52SRahul T R return -EOPNOTSUPP; 255*efcd5f52SRahul T R 256*efcd5f52SRahul T R for (i = 0; i < ARRAY_SIZE(tx_bands) - 1; i++) { 257*efcd5f52SRahul T R if (rate >= tx_bands[i] && rate < tx_bands[i + 1]) 258*efcd5f52SRahul T R return i; 259*efcd5f52SRahul T R } 260*efcd5f52SRahul T R 261*efcd5f52SRahul T R return -EOPNOTSUPP; 262*efcd5f52SRahul T R } 263*efcd5f52SRahul T R 2647a343c8bSMaxime Ripard static int cdns_dphy_validate(struct phy *phy, enum phy_mode mode, int submode, 2657a343c8bSMaxime Ripard union phy_configure_opts *opts) 2667a343c8bSMaxime Ripard { 2677a343c8bSMaxime Ripard struct cdns_dphy_cfg cfg = { 0 }; 2687a343c8bSMaxime Ripard 2697a343c8bSMaxime Ripard if (mode != PHY_MODE_MIPI_DPHY) 2707a343c8bSMaxime Ripard return -EINVAL; 2717a343c8bSMaxime Ripard 2727a343c8bSMaxime Ripard return cdns_dphy_config_from_opts(phy, &opts->mipi_dphy, &cfg); 2737a343c8bSMaxime Ripard } 2747a343c8bSMaxime Ripard 2757a343c8bSMaxime Ripard static int cdns_dphy_configure(struct phy *phy, union phy_configure_opts *opts) 2767a343c8bSMaxime Ripard { 2777a343c8bSMaxime Ripard struct cdns_dphy *dphy = phy_get_drvdata(phy); 2787a343c8bSMaxime Ripard struct cdns_dphy_cfg cfg = { 0 }; 279*efcd5f52SRahul T R int ret, band_ctrl; 280*efcd5f52SRahul T R unsigned int reg; 2817a343c8bSMaxime Ripard 2827a343c8bSMaxime Ripard ret = cdns_dphy_config_from_opts(phy, &opts->mipi_dphy, &cfg); 2837a343c8bSMaxime Ripard if (ret) 2847a343c8bSMaxime Ripard return ret; 2857a343c8bSMaxime Ripard 2867a343c8bSMaxime Ripard /* 2877a343c8bSMaxime Ripard * Configure the internal PSM clk divider so that the DPHY has a 2887a343c8bSMaxime Ripard * 1MHz clk (or something close). 2897a343c8bSMaxime Ripard */ 2907a343c8bSMaxime Ripard ret = cdns_dphy_setup_psm(dphy); 2917a343c8bSMaxime Ripard if (ret) 2927a343c8bSMaxime Ripard return ret; 2937a343c8bSMaxime Ripard 2947a343c8bSMaxime Ripard /* 2957a343c8bSMaxime Ripard * Configure attach clk lanes to data lanes: the DPHY has 2 clk lanes 2967a343c8bSMaxime Ripard * and 8 data lanes, each clk lane can be attache different set of 2977a343c8bSMaxime Ripard * data lanes. The 2 groups are named 'left' and 'right', so here we 2987a343c8bSMaxime Ripard * just say that we want the 'left' clk lane to drive the 'left' data 2997a343c8bSMaxime Ripard * lanes. 3007a343c8bSMaxime Ripard */ 3017a343c8bSMaxime Ripard cdns_dphy_set_clk_lane_cfg(dphy, DPHY_CLK_CFG_LEFT_DRIVES_LEFT); 3027a343c8bSMaxime Ripard 3037a343c8bSMaxime Ripard /* 3047a343c8bSMaxime Ripard * Configure the DPHY PLL that will be used to generate the TX byte 3057a343c8bSMaxime Ripard * clk. 3067a343c8bSMaxime Ripard */ 3077a343c8bSMaxime Ripard cdns_dphy_set_pll_cfg(dphy, &cfg); 3087a343c8bSMaxime Ripard 309*efcd5f52SRahul T R band_ctrl = cdns_dphy_tx_get_band_ctrl(opts->mipi_dphy.hs_clk_rate); 310*efcd5f52SRahul T R if (band_ctrl < 0) 311*efcd5f52SRahul T R return band_ctrl; 312*efcd5f52SRahul T R 313*efcd5f52SRahul T R reg = FIELD_PREP(DPHY_BAND_CFG_LEFT_BAND, band_ctrl) | 314*efcd5f52SRahul T R FIELD_PREP(DPHY_BAND_CFG_RIGHT_BAND, band_ctrl); 315*efcd5f52SRahul T R writel(reg, dphy->regs + DPHY_BAND_CFG); 316*efcd5f52SRahul T R 3177a343c8bSMaxime Ripard return 0; 3187a343c8bSMaxime Ripard } 3197a343c8bSMaxime Ripard 3207a343c8bSMaxime Ripard static int cdns_dphy_power_on(struct phy *phy) 3217a343c8bSMaxime Ripard { 3227a343c8bSMaxime Ripard struct cdns_dphy *dphy = phy_get_drvdata(phy); 3237a343c8bSMaxime Ripard 3247a343c8bSMaxime Ripard clk_prepare_enable(dphy->psm_clk); 3257a343c8bSMaxime Ripard clk_prepare_enable(dphy->pll_ref_clk); 3267a343c8bSMaxime Ripard 3277a343c8bSMaxime Ripard /* Start TX state machine. */ 3287a343c8bSMaxime Ripard writel(DPHY_CMN_SSM_EN | DPHY_CMN_TX_MODE_EN, 3297a343c8bSMaxime Ripard dphy->regs + DPHY_CMN_SSM); 3307a343c8bSMaxime Ripard 3317a343c8bSMaxime Ripard return 0; 3327a343c8bSMaxime Ripard } 3337a343c8bSMaxime Ripard 3347a343c8bSMaxime Ripard static int cdns_dphy_power_off(struct phy *phy) 3357a343c8bSMaxime Ripard { 3367a343c8bSMaxime Ripard struct cdns_dphy *dphy = phy_get_drvdata(phy); 3377a343c8bSMaxime Ripard 3387a343c8bSMaxime Ripard clk_disable_unprepare(dphy->pll_ref_clk); 3397a343c8bSMaxime Ripard clk_disable_unprepare(dphy->psm_clk); 3407a343c8bSMaxime Ripard 3417a343c8bSMaxime Ripard return 0; 3427a343c8bSMaxime Ripard } 3437a343c8bSMaxime Ripard 3447a343c8bSMaxime Ripard static const struct phy_ops cdns_dphy_ops = { 3457a343c8bSMaxime Ripard .configure = cdns_dphy_configure, 3467a343c8bSMaxime Ripard .validate = cdns_dphy_validate, 3477a343c8bSMaxime Ripard .power_on = cdns_dphy_power_on, 3487a343c8bSMaxime Ripard .power_off = cdns_dphy_power_off, 3497a343c8bSMaxime Ripard }; 3507a343c8bSMaxime Ripard 3517a343c8bSMaxime Ripard static int cdns_dphy_probe(struct platform_device *pdev) 3527a343c8bSMaxime Ripard { 3537a343c8bSMaxime Ripard struct phy_provider *phy_provider; 3547a343c8bSMaxime Ripard struct cdns_dphy *dphy; 3557a343c8bSMaxime Ripard int ret; 3567a343c8bSMaxime Ripard 3577a343c8bSMaxime Ripard dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL); 3587a343c8bSMaxime Ripard if (!dphy) 3597a343c8bSMaxime Ripard return -ENOMEM; 3607a343c8bSMaxime Ripard dev_set_drvdata(&pdev->dev, dphy); 3617a343c8bSMaxime Ripard 3627a343c8bSMaxime Ripard dphy->ops = of_device_get_match_data(&pdev->dev); 3637a343c8bSMaxime Ripard if (!dphy->ops) 3647a343c8bSMaxime Ripard return -EINVAL; 3657a343c8bSMaxime Ripard 366fa629094SChunfeng Yun dphy->regs = devm_platform_ioremap_resource(pdev, 0); 3677a343c8bSMaxime Ripard if (IS_ERR(dphy->regs)) 3687a343c8bSMaxime Ripard return PTR_ERR(dphy->regs); 3697a343c8bSMaxime Ripard 3707a343c8bSMaxime Ripard dphy->psm_clk = devm_clk_get(&pdev->dev, "psm"); 3717a343c8bSMaxime Ripard if (IS_ERR(dphy->psm_clk)) 3727a343c8bSMaxime Ripard return PTR_ERR(dphy->psm_clk); 3737a343c8bSMaxime Ripard 3747a343c8bSMaxime Ripard dphy->pll_ref_clk = devm_clk_get(&pdev->dev, "pll_ref"); 3757a343c8bSMaxime Ripard if (IS_ERR(dphy->pll_ref_clk)) 3767a343c8bSMaxime Ripard return PTR_ERR(dphy->pll_ref_clk); 3777a343c8bSMaxime Ripard 3787a343c8bSMaxime Ripard if (dphy->ops->probe) { 3797a343c8bSMaxime Ripard ret = dphy->ops->probe(dphy); 3807a343c8bSMaxime Ripard if (ret) 3817a343c8bSMaxime Ripard return ret; 3827a343c8bSMaxime Ripard } 3837a343c8bSMaxime Ripard 3847a343c8bSMaxime Ripard dphy->phy = devm_phy_create(&pdev->dev, NULL, &cdns_dphy_ops); 3857a343c8bSMaxime Ripard if (IS_ERR(dphy->phy)) { 3867a343c8bSMaxime Ripard dev_err(&pdev->dev, "failed to create PHY\n"); 3877a343c8bSMaxime Ripard if (dphy->ops->remove) 3887a343c8bSMaxime Ripard dphy->ops->remove(dphy); 3897a343c8bSMaxime Ripard return PTR_ERR(dphy->phy); 3907a343c8bSMaxime Ripard } 3917a343c8bSMaxime Ripard 3927a343c8bSMaxime Ripard phy_set_drvdata(dphy->phy, dphy); 3937a343c8bSMaxime Ripard phy_provider = devm_of_phy_provider_register(&pdev->dev, 3947a343c8bSMaxime Ripard of_phy_simple_xlate); 3957a343c8bSMaxime Ripard 3967a343c8bSMaxime Ripard return PTR_ERR_OR_ZERO(phy_provider); 3977a343c8bSMaxime Ripard } 3987a343c8bSMaxime Ripard 3997a343c8bSMaxime Ripard static int cdns_dphy_remove(struct platform_device *pdev) 4007a343c8bSMaxime Ripard { 4017a343c8bSMaxime Ripard struct cdns_dphy *dphy = dev_get_drvdata(&pdev->dev); 4027a343c8bSMaxime Ripard 4037a343c8bSMaxime Ripard if (dphy->ops->remove) 4047a343c8bSMaxime Ripard dphy->ops->remove(dphy); 4057a343c8bSMaxime Ripard 4067a343c8bSMaxime Ripard return 0; 4077a343c8bSMaxime Ripard } 4087a343c8bSMaxime Ripard 4097a343c8bSMaxime Ripard static const struct of_device_id cdns_dphy_of_match[] = { 4107a343c8bSMaxime Ripard { .compatible = "cdns,dphy", .data = &ref_dphy_ops }, 4117a343c8bSMaxime Ripard { /* sentinel */ }, 4127a343c8bSMaxime Ripard }; 4137a343c8bSMaxime Ripard MODULE_DEVICE_TABLE(of, cdns_dphy_of_match); 4147a343c8bSMaxime Ripard 4157a343c8bSMaxime Ripard static struct platform_driver cdns_dphy_platform_driver = { 4167a343c8bSMaxime Ripard .probe = cdns_dphy_probe, 4177a343c8bSMaxime Ripard .remove = cdns_dphy_remove, 4187a343c8bSMaxime Ripard .driver = { 4197a343c8bSMaxime Ripard .name = "cdns-mipi-dphy", 4207a343c8bSMaxime Ripard .of_match_table = cdns_dphy_of_match, 4217a343c8bSMaxime Ripard }, 4227a343c8bSMaxime Ripard }; 4237a343c8bSMaxime Ripard module_platform_driver(cdns_dphy_platform_driver); 4247a343c8bSMaxime Ripard 4257a343c8bSMaxime Ripard MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin.com>"); 4267a343c8bSMaxime Ripard MODULE_DESCRIPTION("Cadence MIPI D-PHY Driver"); 4277a343c8bSMaxime Ripard MODULE_LICENSE("GPL"); 428