xref: /aosp_15_r20/external/coreboot/src/drivers/i2c/pi608gp/pi608gp.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include <commonlib/endian.h>
4 #include <console/console.h>
5 #include <device/device.h>
6 #include <device/smbus.h>
7 
8 #include "pi608gp.h"
9 #include "chip.h"
10 
11 #define PI608GP_CMD_BLK_RD_INIT	0xba
12 #define PI608GP_CMD_BLK_RD	0xbd
13 #define PI608GP_CMD_BLK_WR	0xbe
14 #define PI608GP_SUBCMD_RD	0x04
15 #define PI608GP_SUBCMD_WR	0x03
16 
17 /*
18  * Only some of the available registers are implemented.
19  * For a full list, see the PI7C9X2G608GP datasheet.
20  */
21 #define PI608GP_REG_SW_OPMODE	0x74
22 #define PI608GP_REG_PHY_PAR1	0x78
23 #define PI608GP_REG_TL_CSR	0x8c
24 
25 #define PI608GP_EN_4B		0x0f	/* Enable all 4 data bytes in SMBus messages. */
26 #define PI608GP_ENCODE_ERR	0xff
27 
pi608gp_encode_amp_lvl(uint32_t level_mv)28 static uint8_t pi608gp_encode_amp_lvl(uint32_t level_mv)
29 {
30 	/* Allowed drive amplitude levels are in units of mV in range 0 to 475 mV with 25 mV
31 	   steps, based on Table 6-6 from the PI7C9X2G608GP datasheet. */
32 	if (level_mv > 475) {
33 		printk(BIOS_ERR, "PI608GP: Drive level %u mV out of range 0 to 475 mV!",
34 		       level_mv);
35 		return PI608GP_ENCODE_ERR;
36 	}
37 	if (level_mv % 25 != 0) {
38 		printk(BIOS_ERR, "PI608GP: Drive level %u mV not a multiple of 25!\n",
39 		       level_mv);
40 		return PI608GP_ENCODE_ERR;
41 	}
42 
43 	/* The encoded value is a 5-bit number representing 25 mV steps. */
44 	return (level_mv / 25) & 0x1f;
45 }
46 
pi608gp_encode_deemph_lvl(struct deemph_lvl level_mv)47 static uint8_t pi608gp_encode_deemph_lvl(struct deemph_lvl level_mv)
48 {
49 	/* Table of allowed fixed-point millivolt values, based on Table 6-8 from the
50 	   PI7C9X2G608GP datasheet. */
51 	const struct deemph_lvl allowed[] = {
52 		{  0, 0}, {  6, 0}, { 12, 5}, { 19, 0}, { 25, 0}, { 31, 0}, { 37, 5}, { 44, 0},
53 		{ 50, 0}, { 56, 0}, { 62, 5}, { 69, 0}, { 75, 0}, { 81, 0}, { 87, 0}, { 94, 0},
54 		{100, 0}, {106, 0}, {112, 5}, {119, 0}, {125, 0}, {131, 0}, {137, 5}, {144, 0},
55 		{150, 0}, {156, 0}, {162, 5}, {169, 0}, {175, 0}, {181, 0}, {187, 5}, {194, 0},
56 	};
57 
58 	for (int i = 0; i < ARRAY_SIZE(allowed); i++) {
59 		if (allowed[i].lvl == level_mv.lvl && allowed[i].lvl_10 == level_mv.lvl_10)
60 			/* When found, the encoded value is a 5-bit number that corresponds to
61 			   the index in the table of allowed values above. */
62 			return i & 0x1f;
63 	}
64 
65 	printk(BIOS_ERR, "PI608GP: Requested unsupported de-emphasis level value: %u.%u mV!\n",
66 			level_mv.lvl, level_mv.lvl_10);
67 	return PI608GP_ENCODE_ERR;
68 }
69 
70 static enum cb_err
pi608gp_reg_read(struct device * dev,uint8_t port,uint32_t reg_addr,uint32_t * val)71 pi608gp_reg_read(struct device *dev, uint8_t port, uint32_t reg_addr, uint32_t *val)
72 {
73 	int ret;
74 
75 	/*
76 	 * Compose the SMBus message for register read init operation (from MSB to LSB):
77 	 * Byte 1: 7:3 = Rsvd., 2:0 = Command,
78 	 * Byte 2: 7:4 = Rsvd., 3:0 = Port Select[4:1],
79 	 * Byte 3: 7 = Port Select[0], 6 = Rsvd., 5:2 = Byte Enable, 1:0 = Reg. Addr. [11:10],
80 	 * Byte 4: 7:0 = Reg. Addr.[9:2] (Reg. Addr. [1:0] is fixed to 0).
81 	 */
82 	uint8_t buf[4] = {
83 		PI608GP_SUBCMD_RD,
84 		(port >> 1) & 0xf,
85 		((port & 0x1) << 7) | (PI608GP_EN_4B << 2) | ((reg_addr >> 10) & 0x3),
86 		(reg_addr >> 2) & 0xff,
87 	};
88 
89 	/* Initialize register read operation */
90 	ret = smbus_block_write(dev, PI608GP_CMD_BLK_RD_INIT, sizeof(buf), buf);
91 	if (ret != sizeof(buf)) {
92 		printk(BIOS_ERR, "PI608GP: Unable to initiate register read!\n");
93 		return CB_ERR;
94 	}
95 
96 	/* Perform the register read */
97 	ret = smbus_block_read(dev, PI608GP_CMD_BLK_RD, sizeof(buf), buf);
98 	if (ret != sizeof(buf)) {
99 		printk(BIOS_ERR, "PI608GP: Error reading register 0x%x (port %d)\n",
100 				reg_addr, port);
101 		return CB_ERR;
102 	}
103 
104 	/* Retrieve back the value from the received SMBus packet in big endian order. */
105 	*val = read_be32((void *)buf);
106 
107 	return CB_SUCCESS;
108 }
109 
110 static enum cb_err
pi608gp_reg_write(struct device * dev,uint8_t port,uint32_t reg_addr,uint32_t val)111 pi608gp_reg_write(struct device *dev, uint8_t port, uint32_t reg_addr, uint32_t val)
112 {
113 	int ret;
114 
115 	/* Assemble register write command header, the same way as with read but add extra 4
116 	   bytes for the value. */
117 	uint8_t buf[8] = {
118 		PI608GP_SUBCMD_WR,
119 		(port >> 1) & 0xf,
120 		((port & 0x1) << 7) | (PI608GP_EN_4B << 2) | ((reg_addr >> 10) & 0x3),
121 		(reg_addr >> 2) & 0xff,
122 	};
123 
124 	/* Insert register value to write in BE order after the header. */
125 	write_be32((void *)&buf[4], val);
126 
127 	/* Perform the register write */
128 	ret = smbus_block_write(dev, PI608GP_CMD_BLK_WR, sizeof(buf), buf);
129 	if (ret != sizeof(buf)) {
130 		printk(BIOS_ERR, "PI608GP: Unable to write register 0x%x\n", reg_addr);
131 		return CB_ERR;
132 	}
133 
134 	return CB_SUCCESS;
135 }
136 
137 static enum cb_err
pi608gp_reg_update(struct device * dev,uint8_t port,uint32_t reg_addr,uint32_t and_mask,uint32_t or_mask)138 pi608gp_reg_update(struct device *dev, uint8_t port, uint32_t reg_addr, uint32_t and_mask,
139 		   uint32_t or_mask)
140 {
141 	uint32_t val;
142 
143 	if (pi608gp_reg_read(dev, port, reg_addr, &val))
144 		return CB_ERR;
145 
146 	val &= and_mask;
147 	val |= or_mask;
148 
149 	if (pi608gp_reg_write(dev, port, reg_addr, val))
150 		return CB_ERR;
151 
152 	return CB_SUCCESS;
153 }
154 
pi608gp_init(struct device * dev)155 static void pi608gp_init(struct device *dev)
156 {
157 	const uint8_t port = 0; /* Only the upstream port is being configured */
158 	struct drivers_i2c_pi608gp_config *config = dev->chip_info;
159 	uint8_t amp_lvl, deemph_lvl;
160 
161 	/* The register values need to be encoded in a more complex way for the hardware. */
162 	amp_lvl = pi608gp_encode_amp_lvl(config->gen2_3p5_amp);
163 	deemph_lvl = pi608gp_encode_deemph_lvl(config->gen2_3p5_deemph);
164 
165 	/* When the de-emphasis option isn't enabled or the values incorrectly encoded,
166 	   don't do anything. */
167 	if (!config->gen2_3p5_enable || amp_lvl == PI608GP_ENCODE_ERR ||
168 	    deemph_lvl == PI608GP_ENCODE_ERR)
169 		return;
170 
171 	/* Enable -3,5 dB de-emphasis option (P35_GEN2_MODE). */
172 	if (pi608gp_reg_update(dev, port, PI608GP_REG_TL_CSR, ~0, 1 << 31))
173 		return;
174 
175 	/* Set drive amplitude level for -3,5 dB de-emphasis (bits 20:16). */
176 	if (pi608gp_reg_update(dev, port, PI608GP_REG_SW_OPMODE, ~(0x1f << 16), amp_lvl << 16))
177 		return;
178 
179 	/* Set drive de-emphasis for -3,5 dB on Gen 2 (bits 25:21). */
180 	if (pi608gp_reg_update(dev, port, PI608GP_REG_PHY_PAR1, ~(0x1f << 21), deemph_lvl << 21))
181 		return;
182 }
183 
184 struct device_operations pi608gp_ops = {
185 	.read_resources		= noop_read_resources,
186 	.set_resources		= noop_set_resources,
187 	.init			= pi608gp_init,
188 };
189 
190 struct chip_operations drivers_i2c_pi608gp_ops = {
191 	.name = "PI7C9X2G608GP",
192 };
193