cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

cdns-dphy-rx.c (6630B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright (C) 2022 Texas Instruments Incorporated - https://www.ti.com/
      4 */
      5
      6#include <linux/bitfield.h>
      7#include <linux/bitops.h>
      8#include <linux/io.h>
      9#include <linux/iopoll.h>
     10#include <linux/module.h>
     11#include <linux/phy/phy.h>
     12#include <linux/phy/phy-mipi-dphy.h>
     13#include <linux/platform_device.h>
     14
     15#define DPHY_PMA_CMN(reg)		(reg)
     16#define DPHY_PCS(reg)			(0xb00 + (reg))
     17#define DPHY_ISO(reg)			(0xc00 + (reg))
     18
     19#define DPHY_CMN_SSM			DPHY_PMA_CMN(0x20)
     20#define DPHY_CMN_RX_MODE_EN		BIT(10)
     21#define DPHY_CMN_RX_BANDGAP_TIMER_MASK	GENMASK(8, 1)
     22#define DPHY_CMN_SSM_EN			BIT(0)
     23
     24#define DPHY_CMN_RX_BANDGAP_TIMER	0x14
     25
     26#define DPHY_BAND_CFG			DPHY_PCS(0x0)
     27#define DPHY_BAND_CFG_RIGHT_BAND	GENMASK(9, 5)
     28#define DPHY_BAND_CFG_LEFT_BAND		GENMASK(4, 0)
     29
     30#define DPHY_POWER_ISLAND_EN_DATA	DPHY_PCS(0x8)
     31#define DPHY_POWER_ISLAND_EN_DATA_VAL	0xaaaaaaaa
     32
     33#define DPHY_POWER_ISLAND_EN_CLK	DPHY_PCS(0xc)
     34#define DPHY_POWER_ISLAND_EN_CLK_VAL	0xaa
     35
     36#define DPHY_ISO_CL_CTRL_L		DPHY_ISO(0x10)
     37#define DPHY_ISO_DL_CTRL_L0		DPHY_ISO(0x14)
     38#define DPHY_ISO_DL_CTRL_L1		DPHY_ISO(0x20)
     39#define DPHY_ISO_DL_CTRL_L2		DPHY_ISO(0x30)
     40#define DPHY_ISO_DL_CTRL_L3		DPHY_ISO(0x3c)
     41
     42#define DPHY_ISO_LANE_READY_BIT		0
     43#define DPHY_ISO_LANE_READY_TIMEOUT_MS	100UL
     44
     45#define DPHY_LANES_MIN			1
     46#define DPHY_LANES_MAX			4
     47
     48struct cdns_dphy_rx {
     49	void __iomem *regs;
     50	struct device *dev;
     51	struct phy *phy;
     52};
     53
     54struct cdns_dphy_rx_band {
     55	/* Rates are in Mbps. */
     56	unsigned int min_rate;
     57	unsigned int max_rate;
     58};
     59
     60/* Order of bands is important since the index is the band number. */
     61static const struct cdns_dphy_rx_band bands[] = {
     62	{ 80, 100 }, { 100, 120 }, { 120, 160 }, { 160, 200 }, { 200, 240 },
     63	{ 240, 280 }, { 280, 320 }, { 320, 360 }, { 360, 400 }, { 400, 480 },
     64	{ 480, 560 }, { 560, 640 }, { 640, 720 }, { 720, 800 }, { 800, 880 },
     65	{ 880, 1040 }, { 1040, 1200 }, { 1200, 1350 }, { 1350, 1500 },
     66	{ 1500, 1750 }, { 1750, 2000 }, { 2000, 2250 }, { 2250, 2500 }
     67};
     68
     69static int cdns_dphy_rx_power_on(struct phy *phy)
     70{
     71	struct cdns_dphy_rx *dphy = phy_get_drvdata(phy);
     72
     73	/* Start RX state machine. */
     74	writel(DPHY_CMN_SSM_EN | DPHY_CMN_RX_MODE_EN |
     75	       FIELD_PREP(DPHY_CMN_RX_BANDGAP_TIMER_MASK,
     76			  DPHY_CMN_RX_BANDGAP_TIMER),
     77	       dphy->regs + DPHY_CMN_SSM);
     78
     79	return 0;
     80}
     81
     82static int cdns_dphy_rx_power_off(struct phy *phy)
     83{
     84	struct cdns_dphy_rx *dphy = phy_get_drvdata(phy);
     85
     86	writel(0, dphy->regs + DPHY_CMN_SSM);
     87
     88	return 0;
     89}
     90
     91static int cdns_dphy_rx_get_band_ctrl(unsigned long hs_clk_rate)
     92{
     93	unsigned int rate, i;
     94
     95	rate = hs_clk_rate / 1000000UL;
     96	/* Since CSI-2 clock is DDR, the bit rate is twice the clock rate. */
     97	rate *= 2;
     98
     99	if (rate < bands[0].min_rate)
    100		return -EOPNOTSUPP;
    101
    102	for (i = 0; i < ARRAY_SIZE(bands); i++)
    103		if (rate < bands[i].max_rate)
    104			return i;
    105
    106	return -EOPNOTSUPP;
    107}
    108
    109static inline int cdns_dphy_rx_wait_for_bit(void __iomem *addr,
    110					    unsigned int bit)
    111{
    112	u32 val;
    113
    114	return readl_relaxed_poll_timeout(addr, val, val & BIT(bit), 10,
    115					  DPHY_ISO_LANE_READY_TIMEOUT_MS * 1000);
    116}
    117
    118static int cdns_dphy_rx_wait_lane_ready(struct cdns_dphy_rx *dphy,
    119					unsigned int lanes)
    120{
    121	static const u32 data_lane_ctrl[] = {DPHY_ISO_DL_CTRL_L0,
    122					     DPHY_ISO_DL_CTRL_L1,
    123					     DPHY_ISO_DL_CTRL_L2,
    124					     DPHY_ISO_DL_CTRL_L3};
    125	void __iomem *reg = dphy->regs;
    126	unsigned int i;
    127	int ret;
    128
    129	/* Clock lane */
    130	ret = cdns_dphy_rx_wait_for_bit(reg + DPHY_ISO_CL_CTRL_L,
    131					DPHY_ISO_LANE_READY_BIT);
    132	if (ret)
    133		return ret;
    134
    135	for (i = 0; i < lanes; i++) {
    136		ret = cdns_dphy_rx_wait_for_bit(reg + data_lane_ctrl[i],
    137						DPHY_ISO_LANE_READY_BIT);
    138		if (ret)
    139			return ret;
    140	}
    141
    142	return 0;
    143}
    144
    145static int cdns_dphy_rx_configure(struct phy *phy,
    146				  union phy_configure_opts *opts)
    147{
    148	struct cdns_dphy_rx *dphy = phy_get_drvdata(phy);
    149	unsigned int reg, lanes = opts->mipi_dphy.lanes;
    150	int band_ctrl, ret;
    151
    152	/* Data lanes. Minimum one lane is mandatory. */
    153	if (lanes < DPHY_LANES_MIN || lanes > DPHY_LANES_MAX)
    154		return -EINVAL;
    155
    156	band_ctrl = cdns_dphy_rx_get_band_ctrl(opts->mipi_dphy.hs_clk_rate);
    157	if (band_ctrl < 0)
    158		return band_ctrl;
    159
    160	reg = FIELD_PREP(DPHY_BAND_CFG_LEFT_BAND, band_ctrl) |
    161	      FIELD_PREP(DPHY_BAND_CFG_RIGHT_BAND, band_ctrl);
    162	writel(reg, dphy->regs + DPHY_BAND_CFG);
    163
    164	/*
    165	 * Set the required power island phase 2 time. This is mandated by DPHY
    166	 * specs.
    167	 */
    168	reg = DPHY_POWER_ISLAND_EN_DATA_VAL;
    169	writel(reg, dphy->regs + DPHY_POWER_ISLAND_EN_DATA);
    170	reg = DPHY_POWER_ISLAND_EN_CLK_VAL;
    171	writel(reg, dphy->regs + DPHY_POWER_ISLAND_EN_CLK);
    172
    173	ret = cdns_dphy_rx_wait_lane_ready(dphy, lanes);
    174	if (ret) {
    175		dev_err(dphy->dev, "DPHY wait for lane ready timeout\n");
    176		return ret;
    177	}
    178
    179	return 0;
    180}
    181
    182static int cdns_dphy_rx_validate(struct phy *phy, enum phy_mode mode,
    183				 int submode, union phy_configure_opts *opts)
    184{
    185	int ret;
    186
    187	if (mode != PHY_MODE_MIPI_DPHY)
    188		return -EINVAL;
    189
    190	ret = cdns_dphy_rx_get_band_ctrl(opts->mipi_dphy.hs_clk_rate);
    191	if (ret < 0)
    192		return ret;
    193
    194	return phy_mipi_dphy_config_validate(&opts->mipi_dphy);
    195}
    196
    197static const struct phy_ops cdns_dphy_rx_ops = {
    198	.power_on = cdns_dphy_rx_power_on,
    199	.power_off = cdns_dphy_rx_power_off,
    200	.configure = cdns_dphy_rx_configure,
    201	.validate = cdns_dphy_rx_validate,
    202};
    203
    204static int cdns_dphy_rx_probe(struct platform_device *pdev)
    205{
    206	struct device *dev = &pdev->dev;
    207	struct phy_provider *provider;
    208	struct cdns_dphy_rx *dphy;
    209
    210	dphy = devm_kzalloc(dev, sizeof(*dphy), GFP_KERNEL);
    211	if (!dphy)
    212		return -ENOMEM;
    213
    214	dev_set_drvdata(dev, dphy);
    215	dphy->dev = dev;
    216
    217	dphy->regs = devm_platform_ioremap_resource(pdev, 0);
    218	if (IS_ERR(dphy->regs))
    219		return PTR_ERR(dphy->regs);
    220
    221	dphy->phy = devm_phy_create(dev, NULL, &cdns_dphy_rx_ops);
    222	if (IS_ERR(dphy->phy)) {
    223		dev_err(dev, "Failed to create PHY: %ld\n", PTR_ERR(dphy->phy));
    224		return PTR_ERR(dphy->phy);
    225	}
    226
    227	phy_set_drvdata(dphy->phy, dphy);
    228	provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
    229	if (IS_ERR(provider)) {
    230		dev_err(dev, "Failed to register PHY provider: %ld\n",
    231			PTR_ERR(provider));
    232		return PTR_ERR(provider);
    233	}
    234
    235	return 0;
    236}
    237
    238static const struct of_device_id cdns_dphy_rx_of_match[] = {
    239	{ .compatible = "cdns,dphy-rx" },
    240	{ /* sentinel */ },
    241};
    242MODULE_DEVICE_TABLE(of, cdns_dphy_rx_of_match);
    243
    244static struct platform_driver cdns_dphy_rx_platform_driver = {
    245	.probe		= cdns_dphy_rx_probe,
    246	.driver		= {
    247		.name		= "cdns-mipi-dphy-rx",
    248		.of_match_table	= cdns_dphy_rx_of_match,
    249	},
    250};
    251module_platform_driver(cdns_dphy_rx_platform_driver);
    252
    253MODULE_AUTHOR("Pratyush Yadav <p.yadav@ti.com>");
    254MODULE_DESCRIPTION("Cadence D-PHY Rx Driver");
    255MODULE_LICENSE("GPL");