xref: /linux/drivers/phy/phy-nxp-tja1145.c (revision 3d5e48944e824bddc20d7b874e784f7b279636fe)
1*e5a9c1c9SDimitri Fedrau // SPDX-License-Identifier: GPL-2.0
2*e5a9c1c9SDimitri Fedrau /*
3*e5a9c1c9SDimitri Fedrau  * Copyright (C) 2025 Liebherr-Electronics and Drives GmbH
4*e5a9c1c9SDimitri Fedrau  */
5*e5a9c1c9SDimitri Fedrau #include <linux/module.h>
6*e5a9c1c9SDimitri Fedrau #include <linux/regmap.h>
7*e5a9c1c9SDimitri Fedrau 
8*e5a9c1c9SDimitri Fedrau #include <linux/phy/phy.h>
9*e5a9c1c9SDimitri Fedrau #include <linux/spi/spi.h>
10*e5a9c1c9SDimitri Fedrau 
11*e5a9c1c9SDimitri Fedrau #define TJA1145_MODE_CTRL		0x01
12*e5a9c1c9SDimitri Fedrau #define TJA1145_MODE_CTRL_MC		GENMASK(2, 0)
13*e5a9c1c9SDimitri Fedrau #define TJA1145_MODE_CTRL_STBY		BIT(2)
14*e5a9c1c9SDimitri Fedrau #define TJA1145_MODE_CTRL_NORMAL	TJA1145_MODE_CTRL_MC
15*e5a9c1c9SDimitri Fedrau 
16*e5a9c1c9SDimitri Fedrau #define TJA1145_CAN_CTRL		0x20
17*e5a9c1c9SDimitri Fedrau #define TJA1145_CAN_CTRL_CMC		GENMASK(1, 0)
18*e5a9c1c9SDimitri Fedrau #define TJA1145_CAN_CTRL_ACTIVE		BIT(1)
19*e5a9c1c9SDimitri Fedrau 
20*e5a9c1c9SDimitri Fedrau #define TJA1145_IDENT			0x7e
21*e5a9c1c9SDimitri Fedrau #define TJA1145_IDENT_TJA1145T		0x70
22*e5a9c1c9SDimitri Fedrau 
23*e5a9c1c9SDimitri Fedrau #define TJA1145_SPI_READ_BIT		BIT(0)
24*e5a9c1c9SDimitri Fedrau #define TJA1145T_MAX_BITRATE		1000000
25*e5a9c1c9SDimitri Fedrau 
26*e5a9c1c9SDimitri Fedrau static int tja1145_phy_power_on(struct phy *phy)
27*e5a9c1c9SDimitri Fedrau {
28*e5a9c1c9SDimitri Fedrau 	struct regmap *map = phy_get_drvdata(phy);
29*e5a9c1c9SDimitri Fedrau 	int ret;
30*e5a9c1c9SDimitri Fedrau 
31*e5a9c1c9SDimitri Fedrau 	/*
32*e5a9c1c9SDimitri Fedrau 	 * Switch operating mode to normal which is the active operating mode.
33*e5a9c1c9SDimitri Fedrau 	 * In this mode, the device is fully operational.
34*e5a9c1c9SDimitri Fedrau 	 */
35*e5a9c1c9SDimitri Fedrau 	ret = regmap_update_bits(map, TJA1145_MODE_CTRL, TJA1145_MODE_CTRL_MC,
36*e5a9c1c9SDimitri Fedrau 				 TJA1145_MODE_CTRL_NORMAL);
37*e5a9c1c9SDimitri Fedrau 	if (ret)
38*e5a9c1c9SDimitri Fedrau 		return ret;
39*e5a9c1c9SDimitri Fedrau 
40*e5a9c1c9SDimitri Fedrau 	/*
41*e5a9c1c9SDimitri Fedrau 	 * Switch to CAN operating mode active where the PHY can transmit and
42*e5a9c1c9SDimitri Fedrau 	 * receive data.
43*e5a9c1c9SDimitri Fedrau 	 */
44*e5a9c1c9SDimitri Fedrau 	return regmap_update_bits(map, TJA1145_CAN_CTRL, TJA1145_CAN_CTRL_CMC,
45*e5a9c1c9SDimitri Fedrau 				  TJA1145_CAN_CTRL_ACTIVE);
46*e5a9c1c9SDimitri Fedrau }
47*e5a9c1c9SDimitri Fedrau 
48*e5a9c1c9SDimitri Fedrau static int tja1145_phy_power_off(struct phy *phy)
49*e5a9c1c9SDimitri Fedrau {
50*e5a9c1c9SDimitri Fedrau 	struct regmap *map = phy_get_drvdata(phy);
51*e5a9c1c9SDimitri Fedrau 
52*e5a9c1c9SDimitri Fedrau 	/*
53*e5a9c1c9SDimitri Fedrau 	 * Switch to operating mode standby, the PHY is unable to transmit or
54*e5a9c1c9SDimitri Fedrau 	 * receive data in standby mode.
55*e5a9c1c9SDimitri Fedrau 	 */
56*e5a9c1c9SDimitri Fedrau 	return regmap_update_bits(map, TJA1145_MODE_CTRL, TJA1145_MODE_CTRL_MC,
57*e5a9c1c9SDimitri Fedrau 				  TJA1145_MODE_CTRL_STBY);
58*e5a9c1c9SDimitri Fedrau }
59*e5a9c1c9SDimitri Fedrau 
60*e5a9c1c9SDimitri Fedrau static const struct phy_ops tja1145_phy_ops = {
61*e5a9c1c9SDimitri Fedrau 	.power_on = tja1145_phy_power_on,
62*e5a9c1c9SDimitri Fedrau 	.power_off = tja1145_phy_power_off,
63*e5a9c1c9SDimitri Fedrau 	.owner = THIS_MODULE,
64*e5a9c1c9SDimitri Fedrau };
65*e5a9c1c9SDimitri Fedrau 
66*e5a9c1c9SDimitri Fedrau static const struct regmap_range tja1145_wr_holes_ranges[] = {
67*e5a9c1c9SDimitri Fedrau 	regmap_reg_range(0x00, 0x00),
68*e5a9c1c9SDimitri Fedrau 	regmap_reg_range(0x02, 0x03),
69*e5a9c1c9SDimitri Fedrau 	regmap_reg_range(0x05, 0x05),
70*e5a9c1c9SDimitri Fedrau 	regmap_reg_range(0x0b, 0x1f),
71*e5a9c1c9SDimitri Fedrau 	regmap_reg_range(0x21, 0x22),
72*e5a9c1c9SDimitri Fedrau 	regmap_reg_range(0x24, 0x25),
73*e5a9c1c9SDimitri Fedrau 	regmap_reg_range(0x30, 0x4b),
74*e5a9c1c9SDimitri Fedrau 	regmap_reg_range(0x4d, 0x60),
75*e5a9c1c9SDimitri Fedrau 	regmap_reg_range(0x62, 0x62),
76*e5a9c1c9SDimitri Fedrau 	regmap_reg_range(0x65, 0x67),
77*e5a9c1c9SDimitri Fedrau 	regmap_reg_range(0x70, 0xff),
78*e5a9c1c9SDimitri Fedrau };
79*e5a9c1c9SDimitri Fedrau 
80*e5a9c1c9SDimitri Fedrau static const struct regmap_access_table tja1145_wr_table = {
81*e5a9c1c9SDimitri Fedrau 	.no_ranges = tja1145_wr_holes_ranges,
82*e5a9c1c9SDimitri Fedrau 	.n_no_ranges = ARRAY_SIZE(tja1145_wr_holes_ranges),
83*e5a9c1c9SDimitri Fedrau };
84*e5a9c1c9SDimitri Fedrau 
85*e5a9c1c9SDimitri Fedrau static const struct regmap_range tja1145_rd_holes_ranges[] = {
86*e5a9c1c9SDimitri Fedrau 	regmap_reg_range(0x00, 0x00),
87*e5a9c1c9SDimitri Fedrau 	regmap_reg_range(0x02, 0x02),
88*e5a9c1c9SDimitri Fedrau 	regmap_reg_range(0x05, 0x05),
89*e5a9c1c9SDimitri Fedrau 	regmap_reg_range(0x0b, 0x1f),
90*e5a9c1c9SDimitri Fedrau 	regmap_reg_range(0x21, 0x21),
91*e5a9c1c9SDimitri Fedrau 	regmap_reg_range(0x24, 0x25),
92*e5a9c1c9SDimitri Fedrau 	regmap_reg_range(0x30, 0x4a),
93*e5a9c1c9SDimitri Fedrau 	regmap_reg_range(0x4d, 0x5f),
94*e5a9c1c9SDimitri Fedrau 	regmap_reg_range(0x62, 0x62),
95*e5a9c1c9SDimitri Fedrau 	regmap_reg_range(0x65, 0x67),
96*e5a9c1c9SDimitri Fedrau 	regmap_reg_range(0x70, 0x7d),
97*e5a9c1c9SDimitri Fedrau 	regmap_reg_range(0x7f, 0xff),
98*e5a9c1c9SDimitri Fedrau };
99*e5a9c1c9SDimitri Fedrau 
100*e5a9c1c9SDimitri Fedrau static const struct regmap_access_table tja1145_rd_table = {
101*e5a9c1c9SDimitri Fedrau 	.no_ranges = tja1145_rd_holes_ranges,
102*e5a9c1c9SDimitri Fedrau 	.n_no_ranges = ARRAY_SIZE(tja1145_rd_holes_ranges),
103*e5a9c1c9SDimitri Fedrau };
104*e5a9c1c9SDimitri Fedrau 
105*e5a9c1c9SDimitri Fedrau static const struct regmap_config tja1145_regmap_config = {
106*e5a9c1c9SDimitri Fedrau 	.reg_bits = 8,
107*e5a9c1c9SDimitri Fedrau 	.reg_shift = -1,
108*e5a9c1c9SDimitri Fedrau 	.val_bits = 8,
109*e5a9c1c9SDimitri Fedrau 	.wr_table = &tja1145_wr_table,
110*e5a9c1c9SDimitri Fedrau 	.rd_table = &tja1145_rd_table,
111*e5a9c1c9SDimitri Fedrau 	.read_flag_mask = TJA1145_SPI_READ_BIT,
112*e5a9c1c9SDimitri Fedrau 	.max_register = TJA1145_IDENT,
113*e5a9c1c9SDimitri Fedrau };
114*e5a9c1c9SDimitri Fedrau 
115*e5a9c1c9SDimitri Fedrau static int tja1145_check_ident(struct device *dev, struct regmap *map)
116*e5a9c1c9SDimitri Fedrau {
117*e5a9c1c9SDimitri Fedrau 	unsigned int val;
118*e5a9c1c9SDimitri Fedrau 	int ret;
119*e5a9c1c9SDimitri Fedrau 
120*e5a9c1c9SDimitri Fedrau 	ret = regmap_read(map, TJA1145_IDENT, &val);
121*e5a9c1c9SDimitri Fedrau 	if (ret)
122*e5a9c1c9SDimitri Fedrau 		return ret;
123*e5a9c1c9SDimitri Fedrau 
124*e5a9c1c9SDimitri Fedrau 	if (val != TJA1145_IDENT_TJA1145T) {
125*e5a9c1c9SDimitri Fedrau 		dev_err(dev, "Expected device id: 0x%02x, got: 0x%02x\n",
126*e5a9c1c9SDimitri Fedrau 			TJA1145_IDENT_TJA1145T, val);
127*e5a9c1c9SDimitri Fedrau 		return -ENODEV;
128*e5a9c1c9SDimitri Fedrau 	}
129*e5a9c1c9SDimitri Fedrau 
130*e5a9c1c9SDimitri Fedrau 	return 0;
131*e5a9c1c9SDimitri Fedrau }
132*e5a9c1c9SDimitri Fedrau 
133*e5a9c1c9SDimitri Fedrau static int tja1145_probe(struct spi_device *spi)
134*e5a9c1c9SDimitri Fedrau {
135*e5a9c1c9SDimitri Fedrau 	struct phy_provider *phy_provider;
136*e5a9c1c9SDimitri Fedrau 	struct device *dev = &spi->dev;
137*e5a9c1c9SDimitri Fedrau 	struct regmap *map;
138*e5a9c1c9SDimitri Fedrau 	struct phy *phy;
139*e5a9c1c9SDimitri Fedrau 	int ret;
140*e5a9c1c9SDimitri Fedrau 
141*e5a9c1c9SDimitri Fedrau 	map = devm_regmap_init_spi(spi, &tja1145_regmap_config);
142*e5a9c1c9SDimitri Fedrau 	if (IS_ERR(map))
143*e5a9c1c9SDimitri Fedrau 		return dev_err_probe(dev, PTR_ERR(map), "failed to init regmap\n");
144*e5a9c1c9SDimitri Fedrau 
145*e5a9c1c9SDimitri Fedrau 	ret = tja1145_check_ident(dev, map);
146*e5a9c1c9SDimitri Fedrau 	if (ret)
147*e5a9c1c9SDimitri Fedrau 		return dev_err_probe(dev, ret, "failed to identify device\n");
148*e5a9c1c9SDimitri Fedrau 
149*e5a9c1c9SDimitri Fedrau 	phy = devm_phy_create(dev, dev->of_node, &tja1145_phy_ops);
150*e5a9c1c9SDimitri Fedrau 	if (IS_ERR(phy))
151*e5a9c1c9SDimitri Fedrau 		return dev_err_probe(dev, PTR_ERR(phy), "failed to create PHY\n");
152*e5a9c1c9SDimitri Fedrau 
153*e5a9c1c9SDimitri Fedrau 	phy->attrs.max_link_rate = TJA1145T_MAX_BITRATE;
154*e5a9c1c9SDimitri Fedrau 	phy_set_drvdata(phy, map);
155*e5a9c1c9SDimitri Fedrau 	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
156*e5a9c1c9SDimitri Fedrau 
157*e5a9c1c9SDimitri Fedrau 	return PTR_ERR_OR_ZERO(phy_provider);
158*e5a9c1c9SDimitri Fedrau }
159*e5a9c1c9SDimitri Fedrau 
160*e5a9c1c9SDimitri Fedrau static const struct spi_device_id tja1145_spi_id[] = {
161*e5a9c1c9SDimitri Fedrau 	{ "tja1145" },
162*e5a9c1c9SDimitri Fedrau 	{ }
163*e5a9c1c9SDimitri Fedrau };
164*e5a9c1c9SDimitri Fedrau MODULE_DEVICE_TABLE(spi, tja1145_spi_id);
165*e5a9c1c9SDimitri Fedrau 
166*e5a9c1c9SDimitri Fedrau static const struct of_device_id tja1145_of_match[] = {
167*e5a9c1c9SDimitri Fedrau 	{ .compatible = "nxp,tja1145" },
168*e5a9c1c9SDimitri Fedrau 	{ }
169*e5a9c1c9SDimitri Fedrau };
170*e5a9c1c9SDimitri Fedrau MODULE_DEVICE_TABLE(of, tja1145_of_match);
171*e5a9c1c9SDimitri Fedrau 
172*e5a9c1c9SDimitri Fedrau static struct spi_driver tja1145_driver = {
173*e5a9c1c9SDimitri Fedrau 	.driver = {
174*e5a9c1c9SDimitri Fedrau 		.name = "tja1145",
175*e5a9c1c9SDimitri Fedrau 		.of_match_table = tja1145_of_match,
176*e5a9c1c9SDimitri Fedrau 	},
177*e5a9c1c9SDimitri Fedrau 	.probe = tja1145_probe,
178*e5a9c1c9SDimitri Fedrau 	.id_table = tja1145_spi_id,
179*e5a9c1c9SDimitri Fedrau };
180*e5a9c1c9SDimitri Fedrau module_spi_driver(tja1145_driver);
181*e5a9c1c9SDimitri Fedrau 
182*e5a9c1c9SDimitri Fedrau MODULE_DESCRIPTION("NXP TJA1145 CAN transceiver PHY driver");
183*e5a9c1c9SDimitri Fedrau MODULE_AUTHOR("Dimitri Fedrau <dimitri.fedrau@liebherr.com>");
184*e5a9c1c9SDimitri Fedrau MODULE_LICENSE("GPL");
185