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

phy-sun6i-mipi-dphy.c (14170B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * Copyright (c) 2016 Allwinnertech Co., Ltd.
      4 * Copyright (C) 2017-2018 Bootlin
      5 *
      6 * Maxime Ripard <maxime.ripard@free-electrons.com>
      7 */
      8
      9#include <linux/bitops.h>
     10#include <linux/clk.h>
     11#include <linux/module.h>
     12#include <linux/of_address.h>
     13#include <linux/platform_device.h>
     14#include <linux/regmap.h>
     15#include <linux/reset.h>
     16
     17#include <linux/phy/phy.h>
     18#include <linux/phy/phy-mipi-dphy.h>
     19
     20#define SUN6I_DPHY_GCTL_REG		0x00
     21#define SUN6I_DPHY_GCTL_LANE_NUM(n)		((((n) - 1) & 3) << 4)
     22#define SUN6I_DPHY_GCTL_EN			BIT(0)
     23
     24#define SUN6I_DPHY_TX_CTL_REG		0x04
     25#define SUN6I_DPHY_TX_CTL_HS_TX_CLK_CONT	BIT(28)
     26
     27#define SUN6I_DPHY_RX_CTL_REG		0x08
     28#define SUN6I_DPHY_RX_CTL_EN_DBC	BIT(31)
     29#define SUN6I_DPHY_RX_CTL_RX_CLK_FORCE	BIT(24)
     30#define SUN6I_DPHY_RX_CTL_RX_D3_FORCE	BIT(23)
     31#define SUN6I_DPHY_RX_CTL_RX_D2_FORCE	BIT(22)
     32#define SUN6I_DPHY_RX_CTL_RX_D1_FORCE	BIT(21)
     33#define SUN6I_DPHY_RX_CTL_RX_D0_FORCE	BIT(20)
     34
     35#define SUN6I_DPHY_TX_TIME0_REG		0x10
     36#define SUN6I_DPHY_TX_TIME0_HS_TRAIL(n)		(((n) & 0xff) << 24)
     37#define SUN6I_DPHY_TX_TIME0_HS_PREPARE(n)	(((n) & 0xff) << 16)
     38#define SUN6I_DPHY_TX_TIME0_LP_CLK_DIV(n)	((n) & 0xff)
     39
     40#define SUN6I_DPHY_TX_TIME1_REG		0x14
     41#define SUN6I_DPHY_TX_TIME1_CLK_POST(n)		(((n) & 0xff) << 24)
     42#define SUN6I_DPHY_TX_TIME1_CLK_PRE(n)		(((n) & 0xff) << 16)
     43#define SUN6I_DPHY_TX_TIME1_CLK_ZERO(n)		(((n) & 0xff) << 8)
     44#define SUN6I_DPHY_TX_TIME1_CLK_PREPARE(n)	((n) & 0xff)
     45
     46#define SUN6I_DPHY_TX_TIME2_REG		0x18
     47#define SUN6I_DPHY_TX_TIME2_CLK_TRAIL(n)	((n) & 0xff)
     48
     49#define SUN6I_DPHY_TX_TIME3_REG		0x1c
     50
     51#define SUN6I_DPHY_TX_TIME4_REG		0x20
     52#define SUN6I_DPHY_TX_TIME4_HS_TX_ANA1(n)	(((n) & 0xff) << 8)
     53#define SUN6I_DPHY_TX_TIME4_HS_TX_ANA0(n)	((n) & 0xff)
     54
     55#define SUN6I_DPHY_RX_TIME0_REG		0x30
     56#define SUN6I_DPHY_RX_TIME0_HS_RX_SYNC(n)	(((n) & 0xff) << 24)
     57#define SUN6I_DPHY_RX_TIME0_HS_RX_CLK_MISS(n)	(((n) & 0xff) << 16)
     58#define SUN6I_DPHY_RX_TIME0_LP_RX(n)		(((n) & 0xff) << 8)
     59
     60#define SUN6I_DPHY_RX_TIME1_REG		0x34
     61#define SUN6I_DPHY_RX_TIME1_RX_DLY(n)		(((n) & 0xfff) << 20)
     62#define SUN6I_DPHY_RX_TIME1_LP_RX_ULPS_WP(n)	((n) & 0xfffff)
     63
     64#define SUN6I_DPHY_RX_TIME2_REG		0x38
     65#define SUN6I_DPHY_RX_TIME2_HS_RX_ANA1(n)	(((n) & 0xff) << 8)
     66#define SUN6I_DPHY_RX_TIME2_HS_RX_ANA0(n)	((n) & 0xff)
     67
     68#define SUN6I_DPHY_RX_TIME3_REG		0x40
     69#define SUN6I_DPHY_RX_TIME3_LPRST_DLY(n)	(((n) & 0xffff) << 16)
     70
     71#define SUN6I_DPHY_ANA0_REG		0x4c
     72#define SUN6I_DPHY_ANA0_REG_PWS			BIT(31)
     73#define SUN6I_DPHY_ANA0_REG_DMPC		BIT(28)
     74#define SUN6I_DPHY_ANA0_REG_DMPD(n)		(((n) & 0xf) << 24)
     75#define SUN6I_DPHY_ANA0_REG_SLV(n)		(((n) & 7) << 12)
     76#define SUN6I_DPHY_ANA0_REG_DEN(n)		(((n) & 0xf) << 8)
     77#define SUN6I_DPHY_ANA0_REG_SFB(n)		(((n) & 3) << 2)
     78
     79#define SUN6I_DPHY_ANA1_REG		0x50
     80#define SUN6I_DPHY_ANA1_REG_VTTMODE		BIT(31)
     81#define SUN6I_DPHY_ANA1_REG_CSMPS(n)		(((n) & 3) << 28)
     82#define SUN6I_DPHY_ANA1_REG_SVTT(n)		(((n) & 0xf) << 24)
     83
     84#define SUN6I_DPHY_ANA2_REG		0x54
     85#define SUN6I_DPHY_ANA2_EN_P2S_CPU(n)		(((n) & 0xf) << 24)
     86#define SUN6I_DPHY_ANA2_EN_P2S_CPU_MASK		GENMASK(27, 24)
     87#define SUN6I_DPHY_ANA2_EN_CK_CPU		BIT(4)
     88#define SUN6I_DPHY_ANA2_REG_ENIB		BIT(1)
     89
     90#define SUN6I_DPHY_ANA3_REG		0x58
     91#define SUN6I_DPHY_ANA3_EN_VTTD(n)		(((n) & 0xf) << 28)
     92#define SUN6I_DPHY_ANA3_EN_VTTD_MASK		GENMASK(31, 28)
     93#define SUN6I_DPHY_ANA3_EN_VTTC			BIT(27)
     94#define SUN6I_DPHY_ANA3_EN_DIV			BIT(26)
     95#define SUN6I_DPHY_ANA3_EN_LDOC			BIT(25)
     96#define SUN6I_DPHY_ANA3_EN_LDOD			BIT(24)
     97#define SUN6I_DPHY_ANA3_EN_LDOR			BIT(18)
     98
     99#define SUN6I_DPHY_ANA4_REG		0x5c
    100#define SUN6I_DPHY_ANA4_REG_DMPLVC		BIT(24)
    101#define SUN6I_DPHY_ANA4_REG_DMPLVD(n)		(((n) & 0xf) << 20)
    102#define SUN6I_DPHY_ANA4_REG_CKDV(n)		(((n) & 0x1f) << 12)
    103#define SUN6I_DPHY_ANA4_REG_TMSC(n)		(((n) & 3) << 10)
    104#define SUN6I_DPHY_ANA4_REG_TMSD(n)		(((n) & 3) << 8)
    105#define SUN6I_DPHY_ANA4_REG_TXDNSC(n)		(((n) & 3) << 6)
    106#define SUN6I_DPHY_ANA4_REG_TXDNSD(n)		(((n) & 3) << 4)
    107#define SUN6I_DPHY_ANA4_REG_TXPUSC(n)		(((n) & 3) << 2)
    108#define SUN6I_DPHY_ANA4_REG_TXPUSD(n)		((n) & 3)
    109
    110#define SUN6I_DPHY_DBG5_REG		0xf4
    111
    112enum sun6i_dphy_direction {
    113	SUN6I_DPHY_DIRECTION_TX,
    114	SUN6I_DPHY_DIRECTION_RX,
    115};
    116
    117struct sun6i_dphy {
    118	struct clk				*bus_clk;
    119	struct clk				*mod_clk;
    120	struct regmap				*regs;
    121	struct reset_control			*reset;
    122
    123	struct phy				*phy;
    124	struct phy_configure_opts_mipi_dphy	config;
    125
    126	enum sun6i_dphy_direction		direction;
    127};
    128
    129static int sun6i_dphy_init(struct phy *phy)
    130{
    131	struct sun6i_dphy *dphy = phy_get_drvdata(phy);
    132
    133	reset_control_deassert(dphy->reset);
    134	clk_prepare_enable(dphy->mod_clk);
    135	clk_set_rate_exclusive(dphy->mod_clk, 150000000);
    136
    137	return 0;
    138}
    139
    140static int sun6i_dphy_configure(struct phy *phy, union phy_configure_opts *opts)
    141{
    142	struct sun6i_dphy *dphy = phy_get_drvdata(phy);
    143	int ret;
    144
    145	ret = phy_mipi_dphy_config_validate(&opts->mipi_dphy);
    146	if (ret)
    147		return ret;
    148
    149	memcpy(&dphy->config, opts, sizeof(dphy->config));
    150
    151	return 0;
    152}
    153
    154static int sun6i_dphy_tx_power_on(struct sun6i_dphy *dphy)
    155{
    156	u8 lanes_mask = GENMASK(dphy->config.lanes - 1, 0);
    157
    158	regmap_write(dphy->regs, SUN6I_DPHY_TX_CTL_REG,
    159		     SUN6I_DPHY_TX_CTL_HS_TX_CLK_CONT);
    160
    161	regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME0_REG,
    162		     SUN6I_DPHY_TX_TIME0_LP_CLK_DIV(14) |
    163		     SUN6I_DPHY_TX_TIME0_HS_PREPARE(6) |
    164		     SUN6I_DPHY_TX_TIME0_HS_TRAIL(10));
    165
    166	regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME1_REG,
    167		     SUN6I_DPHY_TX_TIME1_CLK_PREPARE(7) |
    168		     SUN6I_DPHY_TX_TIME1_CLK_ZERO(50) |
    169		     SUN6I_DPHY_TX_TIME1_CLK_PRE(3) |
    170		     SUN6I_DPHY_TX_TIME1_CLK_POST(10));
    171
    172	regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME2_REG,
    173		     SUN6I_DPHY_TX_TIME2_CLK_TRAIL(30));
    174
    175	regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME3_REG, 0);
    176
    177	regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME4_REG,
    178		     SUN6I_DPHY_TX_TIME4_HS_TX_ANA0(3) |
    179		     SUN6I_DPHY_TX_TIME4_HS_TX_ANA1(3));
    180
    181	regmap_write(dphy->regs, SUN6I_DPHY_GCTL_REG,
    182		     SUN6I_DPHY_GCTL_LANE_NUM(dphy->config.lanes) |
    183		     SUN6I_DPHY_GCTL_EN);
    184
    185	regmap_write(dphy->regs, SUN6I_DPHY_ANA0_REG,
    186		     SUN6I_DPHY_ANA0_REG_PWS |
    187		     SUN6I_DPHY_ANA0_REG_DMPC |
    188		     SUN6I_DPHY_ANA0_REG_SLV(7) |
    189		     SUN6I_DPHY_ANA0_REG_DMPD(lanes_mask) |
    190		     SUN6I_DPHY_ANA0_REG_DEN(lanes_mask));
    191
    192	regmap_write(dphy->regs, SUN6I_DPHY_ANA1_REG,
    193		     SUN6I_DPHY_ANA1_REG_CSMPS(1) |
    194		     SUN6I_DPHY_ANA1_REG_SVTT(7));
    195
    196	regmap_write(dphy->regs, SUN6I_DPHY_ANA4_REG,
    197		     SUN6I_DPHY_ANA4_REG_CKDV(1) |
    198		     SUN6I_DPHY_ANA4_REG_TMSC(1) |
    199		     SUN6I_DPHY_ANA4_REG_TMSD(1) |
    200		     SUN6I_DPHY_ANA4_REG_TXDNSC(1) |
    201		     SUN6I_DPHY_ANA4_REG_TXDNSD(1) |
    202		     SUN6I_DPHY_ANA4_REG_TXPUSC(1) |
    203		     SUN6I_DPHY_ANA4_REG_TXPUSD(1) |
    204		     SUN6I_DPHY_ANA4_REG_DMPLVC |
    205		     SUN6I_DPHY_ANA4_REG_DMPLVD(lanes_mask));
    206
    207	regmap_write(dphy->regs, SUN6I_DPHY_ANA2_REG,
    208		     SUN6I_DPHY_ANA2_REG_ENIB);
    209	udelay(5);
    210
    211	regmap_write(dphy->regs, SUN6I_DPHY_ANA3_REG,
    212		     SUN6I_DPHY_ANA3_EN_LDOR |
    213		     SUN6I_DPHY_ANA3_EN_LDOC |
    214		     SUN6I_DPHY_ANA3_EN_LDOD);
    215	udelay(1);
    216
    217	regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA3_REG,
    218			   SUN6I_DPHY_ANA3_EN_VTTC |
    219			   SUN6I_DPHY_ANA3_EN_VTTD_MASK,
    220			   SUN6I_DPHY_ANA3_EN_VTTC |
    221			   SUN6I_DPHY_ANA3_EN_VTTD(lanes_mask));
    222	udelay(1);
    223
    224	regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA3_REG,
    225			   SUN6I_DPHY_ANA3_EN_DIV,
    226			   SUN6I_DPHY_ANA3_EN_DIV);
    227	udelay(1);
    228
    229	regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA2_REG,
    230			   SUN6I_DPHY_ANA2_EN_CK_CPU,
    231			   SUN6I_DPHY_ANA2_EN_CK_CPU);
    232	udelay(1);
    233
    234	regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA1_REG,
    235			   SUN6I_DPHY_ANA1_REG_VTTMODE,
    236			   SUN6I_DPHY_ANA1_REG_VTTMODE);
    237
    238	regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA2_REG,
    239			   SUN6I_DPHY_ANA2_EN_P2S_CPU_MASK,
    240			   SUN6I_DPHY_ANA2_EN_P2S_CPU(lanes_mask));
    241
    242	return 0;
    243}
    244
    245static int sun6i_dphy_rx_power_on(struct sun6i_dphy *dphy)
    246{
    247	/* Physical clock rate is actually half of symbol rate with DDR. */
    248	unsigned long mipi_symbol_rate = dphy->config.hs_clk_rate;
    249	unsigned long dphy_clk_rate;
    250	unsigned int rx_dly;
    251	unsigned int lprst_dly;
    252	u32 value;
    253
    254	dphy_clk_rate = clk_get_rate(dphy->mod_clk);
    255	if (!dphy_clk_rate)
    256		return -EINVAL;
    257
    258	/* Hardcoded timing parameters from the Allwinner BSP. */
    259	regmap_write(dphy->regs, SUN6I_DPHY_RX_TIME0_REG,
    260		     SUN6I_DPHY_RX_TIME0_HS_RX_SYNC(255) |
    261		     SUN6I_DPHY_RX_TIME0_HS_RX_CLK_MISS(255) |
    262		     SUN6I_DPHY_RX_TIME0_LP_RX(255));
    263
    264	/*
    265	 * Formula from the Allwinner BSP, with hardcoded coefficients
    266	 * (probably internal divider/multiplier).
    267	 */
    268	rx_dly = 8 * (unsigned int)(dphy_clk_rate / (mipi_symbol_rate / 8));
    269
    270	/*
    271	 * The Allwinner BSP has an alternative formula for LP_RX_ULPS_WP:
    272	 * lp_ulps_wp_cnt = lp_ulps_wp_ms * lp_clk / 1000
    273	 * but does not use it and hardcodes 255 instead.
    274	 */
    275	regmap_write(dphy->regs, SUN6I_DPHY_RX_TIME1_REG,
    276		     SUN6I_DPHY_RX_TIME1_RX_DLY(rx_dly) |
    277		     SUN6I_DPHY_RX_TIME1_LP_RX_ULPS_WP(255));
    278
    279	/* HS_RX_ANA0 value is hardcoded in the Allwinner BSP. */
    280	regmap_write(dphy->regs, SUN6I_DPHY_RX_TIME2_REG,
    281		     SUN6I_DPHY_RX_TIME2_HS_RX_ANA0(4));
    282
    283	/*
    284	 * Formula from the Allwinner BSP, with hardcoded coefficients
    285	 * (probably internal divider/multiplier).
    286	 */
    287	lprst_dly = 4 * (unsigned int)(dphy_clk_rate / (mipi_symbol_rate / 2));
    288
    289	regmap_write(dphy->regs, SUN6I_DPHY_RX_TIME3_REG,
    290		     SUN6I_DPHY_RX_TIME3_LPRST_DLY(lprst_dly));
    291
    292	/* Analog parameters are hardcoded in the Allwinner BSP. */
    293	regmap_write(dphy->regs, SUN6I_DPHY_ANA0_REG,
    294		     SUN6I_DPHY_ANA0_REG_PWS |
    295		     SUN6I_DPHY_ANA0_REG_SLV(7) |
    296		     SUN6I_DPHY_ANA0_REG_SFB(2));
    297
    298	regmap_write(dphy->regs, SUN6I_DPHY_ANA1_REG,
    299		     SUN6I_DPHY_ANA1_REG_SVTT(4));
    300
    301	regmap_write(dphy->regs, SUN6I_DPHY_ANA4_REG,
    302		     SUN6I_DPHY_ANA4_REG_DMPLVC |
    303		     SUN6I_DPHY_ANA4_REG_DMPLVD(1));
    304
    305	regmap_write(dphy->regs, SUN6I_DPHY_ANA2_REG,
    306		     SUN6I_DPHY_ANA2_REG_ENIB);
    307
    308	regmap_write(dphy->regs, SUN6I_DPHY_ANA3_REG,
    309		     SUN6I_DPHY_ANA3_EN_LDOR |
    310		     SUN6I_DPHY_ANA3_EN_LDOC |
    311		     SUN6I_DPHY_ANA3_EN_LDOD);
    312
    313	/*
    314	 * Delay comes from the Allwinner BSP, likely for internal regulator
    315	 * ramp-up.
    316	 */
    317	udelay(3);
    318
    319	value = SUN6I_DPHY_RX_CTL_EN_DBC | SUN6I_DPHY_RX_CTL_RX_CLK_FORCE;
    320
    321	/*
    322	 * Rx data lane force-enable bits are used as regular RX enable by the
    323	 * Allwinner BSP.
    324	 */
    325	if (dphy->config.lanes >= 1)
    326		value |= SUN6I_DPHY_RX_CTL_RX_D0_FORCE;
    327	if (dphy->config.lanes >= 2)
    328		value |= SUN6I_DPHY_RX_CTL_RX_D1_FORCE;
    329	if (dphy->config.lanes >= 3)
    330		value |= SUN6I_DPHY_RX_CTL_RX_D2_FORCE;
    331	if (dphy->config.lanes == 4)
    332		value |= SUN6I_DPHY_RX_CTL_RX_D3_FORCE;
    333
    334	regmap_write(dphy->regs, SUN6I_DPHY_RX_CTL_REG, value);
    335
    336	regmap_write(dphy->regs, SUN6I_DPHY_GCTL_REG,
    337		     SUN6I_DPHY_GCTL_LANE_NUM(dphy->config.lanes) |
    338		     SUN6I_DPHY_GCTL_EN);
    339
    340	return 0;
    341}
    342
    343static int sun6i_dphy_power_on(struct phy *phy)
    344{
    345	struct sun6i_dphy *dphy = phy_get_drvdata(phy);
    346
    347	switch (dphy->direction) {
    348	case SUN6I_DPHY_DIRECTION_TX:
    349		return sun6i_dphy_tx_power_on(dphy);
    350	case SUN6I_DPHY_DIRECTION_RX:
    351		return sun6i_dphy_rx_power_on(dphy);
    352	default:
    353		return -EINVAL;
    354	}
    355}
    356
    357static int sun6i_dphy_power_off(struct phy *phy)
    358{
    359	struct sun6i_dphy *dphy = phy_get_drvdata(phy);
    360
    361	regmap_write(dphy->regs, SUN6I_DPHY_GCTL_REG, 0);
    362
    363	regmap_write(dphy->regs, SUN6I_DPHY_ANA0_REG, 0);
    364	regmap_write(dphy->regs, SUN6I_DPHY_ANA1_REG, 0);
    365	regmap_write(dphy->regs, SUN6I_DPHY_ANA2_REG, 0);
    366	regmap_write(dphy->regs, SUN6I_DPHY_ANA3_REG, 0);
    367	regmap_write(dphy->regs, SUN6I_DPHY_ANA4_REG, 0);
    368
    369	return 0;
    370}
    371
    372static int sun6i_dphy_exit(struct phy *phy)
    373{
    374	struct sun6i_dphy *dphy = phy_get_drvdata(phy);
    375
    376	clk_rate_exclusive_put(dphy->mod_clk);
    377	clk_disable_unprepare(dphy->mod_clk);
    378	reset_control_assert(dphy->reset);
    379
    380	return 0;
    381}
    382
    383
    384static const struct phy_ops sun6i_dphy_ops = {
    385	.configure	= sun6i_dphy_configure,
    386	.power_on	= sun6i_dphy_power_on,
    387	.power_off	= sun6i_dphy_power_off,
    388	.init		= sun6i_dphy_init,
    389	.exit		= sun6i_dphy_exit,
    390};
    391
    392static const struct regmap_config sun6i_dphy_regmap_config = {
    393	.reg_bits	= 32,
    394	.val_bits	= 32,
    395	.reg_stride	= 4,
    396	.max_register	= SUN6I_DPHY_DBG5_REG,
    397	.name		= "mipi-dphy",
    398};
    399
    400static int sun6i_dphy_probe(struct platform_device *pdev)
    401{
    402	struct phy_provider *phy_provider;
    403	struct sun6i_dphy *dphy;
    404	const char *direction;
    405	void __iomem *regs;
    406	int ret;
    407
    408	dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL);
    409	if (!dphy)
    410		return -ENOMEM;
    411
    412	regs = devm_platform_ioremap_resource(pdev, 0);
    413	if (IS_ERR(regs)) {
    414		dev_err(&pdev->dev, "Couldn't map the DPHY encoder registers\n");
    415		return PTR_ERR(regs);
    416	}
    417
    418	dphy->regs = devm_regmap_init_mmio_clk(&pdev->dev, "bus",
    419					       regs, &sun6i_dphy_regmap_config);
    420	if (IS_ERR(dphy->regs)) {
    421		dev_err(&pdev->dev, "Couldn't create the DPHY encoder regmap\n");
    422		return PTR_ERR(dphy->regs);
    423	}
    424
    425	dphy->reset = devm_reset_control_get_shared(&pdev->dev, NULL);
    426	if (IS_ERR(dphy->reset)) {
    427		dev_err(&pdev->dev, "Couldn't get our reset line\n");
    428		return PTR_ERR(dphy->reset);
    429	}
    430
    431	dphy->mod_clk = devm_clk_get(&pdev->dev, "mod");
    432	if (IS_ERR(dphy->mod_clk)) {
    433		dev_err(&pdev->dev, "Couldn't get the DPHY mod clock\n");
    434		return PTR_ERR(dphy->mod_clk);
    435	}
    436
    437	dphy->phy = devm_phy_create(&pdev->dev, NULL, &sun6i_dphy_ops);
    438	if (IS_ERR(dphy->phy)) {
    439		dev_err(&pdev->dev, "failed to create PHY\n");
    440		return PTR_ERR(dphy->phy);
    441	}
    442
    443	dphy->direction = SUN6I_DPHY_DIRECTION_TX;
    444
    445	ret = of_property_read_string(pdev->dev.of_node, "allwinner,direction",
    446				      &direction);
    447
    448	if (!ret && !strncmp(direction, "rx", 2))
    449		dphy->direction = SUN6I_DPHY_DIRECTION_RX;
    450
    451	phy_set_drvdata(dphy->phy, dphy);
    452	phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate);
    453
    454	return PTR_ERR_OR_ZERO(phy_provider);
    455}
    456
    457static const struct of_device_id sun6i_dphy_of_table[] = {
    458	{ .compatible = "allwinner,sun6i-a31-mipi-dphy" },
    459	{ }
    460};
    461MODULE_DEVICE_TABLE(of, sun6i_dphy_of_table);
    462
    463static struct platform_driver sun6i_dphy_platform_driver = {
    464	.probe		= sun6i_dphy_probe,
    465	.driver		= {
    466		.name		= "sun6i-mipi-dphy",
    467		.of_match_table	= sun6i_dphy_of_table,
    468	},
    469};
    470module_platform_driver(sun6i_dphy_platform_driver);
    471
    472MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin>");
    473MODULE_DESCRIPTION("Allwinner A31 MIPI D-PHY Driver");
    474MODULE_LICENSE("GPL");