1 /* 2 * Copyright 2021 Advanced Micro Devices, Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 * OTHER DEALINGS IN THE SOFTWARE. 21 * 22 */ 23 24 #include "amdgpu_eeprom.h" 25 #include "amdgpu.h" 26 27 /* AT24CM02 has a 256-byte write page size. 28 */ 29 #define EEPROM_PAGE_BITS 8 30 #define EEPROM_PAGE_SIZE (1U << EEPROM_PAGE_BITS) 31 #define EEPROM_PAGE_MASK (EEPROM_PAGE_SIZE - 1) 32 33 #define EEPROM_OFFSET_SIZE 2 34 35 static int __amdgpu_eeprom_xfer(struct i2c_adapter *i2c_adap, 36 u16 slave_addr, u16 eeprom_addr, 37 u8 *eeprom_buf, u16 buf_size, bool read) 38 { 39 u8 eeprom_offset_buf[EEPROM_OFFSET_SIZE]; 40 struct i2c_msg msgs[] = { 41 { 42 .addr = slave_addr, 43 .flags = 0, 44 .len = EEPROM_OFFSET_SIZE, 45 .buf = eeprom_offset_buf, 46 }, 47 { 48 .addr = slave_addr, 49 .flags = read ? I2C_M_RD : 0, 50 }, 51 }; 52 const u8 *p = eeprom_buf; 53 int r; 54 u16 len; 55 56 r = 0; 57 for ( ; buf_size > 0; 58 buf_size -= len, eeprom_addr += len, eeprom_buf += len) { 59 /* Set the EEPROM address we want to write to/read from. 60 */ 61 msgs[0].buf[0] = (eeprom_addr >> 8) & 0xff; 62 msgs[0].buf[1] = eeprom_addr & 0xff; 63 64 if (!read) { 65 /* Write the maximum amount of data, without 66 * crossing the device's page boundary, as per 67 * its spec. Partial page writes are allowed, 68 * starting at any location within the page, 69 * so long as the page boundary isn't crossed 70 * over (actually the page pointer rolls 71 * over). 72 * 73 * As per the AT24CM02 EEPROM spec, after 74 * writing into a page, the I2C driver MUST 75 * terminate the transfer, i.e. in 76 * "i2c_transfer()" below, with a STOP 77 * condition, so that the self-timed write 78 * cycle begins. This is implied for the 79 * "i2c_transfer()" abstraction. 80 */ 81 len = min(EEPROM_PAGE_SIZE - (eeprom_addr & 82 EEPROM_PAGE_MASK), 83 (u32)buf_size); 84 } else { 85 /* Reading from the EEPROM has no limitation 86 * on the number of bytes read from the EEPROM 87 * device--they are simply sequenced out. 88 */ 89 len = buf_size; 90 } 91 msgs[1].len = len; 92 msgs[1].buf = eeprom_buf; 93 94 r = i2c_transfer(i2c_adap, msgs, ARRAY_SIZE(msgs)); 95 if (r < ARRAY_SIZE(msgs)) 96 break; 97 98 if (!read) { 99 /* According to the AT24CM02 EEPROM spec the 100 * length of the self-writing cycle, tWR, is 101 * 10 ms. 102 * 103 * TODO Improve to wait for first ACK for slave address after 104 * internal write cycle done. 105 */ 106 msleep(10); 107 } 108 } 109 110 return r < 0 ? r : eeprom_buf - p; 111 } 112 113 /** 114 * amdgpu_eeprom_xfer -- Read/write from/to an I2C EEPROM device 115 * @i2c_adap: pointer to the I2C adapter to use 116 * @slave_addr: I2C address of the slave device 117 * @eeprom_addr: EEPROM address from which to read/write 118 * @eeprom_buf: pointer to data buffer to read into/write from 119 * @buf_size: the size of @eeprom_buf 120 * @read: True if reading from the EEPROM, false if writing 121 * 122 * Returns the number of bytes read/written; -errno on error. 123 */ 124 int amdgpu_eeprom_xfer(struct i2c_adapter *i2c_adap, 125 u16 slave_addr, u16 eeprom_addr, 126 u8 *eeprom_buf, u16 buf_size, bool read) 127 { 128 const struct i2c_adapter_quirks *quirks = i2c_adap->quirks; 129 u16 limit; 130 131 if (!quirks) 132 limit = 0; 133 else if (read) 134 limit = quirks->max_read_len; 135 else 136 limit = quirks->max_write_len; 137 138 if (limit == 0) { 139 return __amdgpu_eeprom_xfer(i2c_adap, slave_addr, eeprom_addr, 140 eeprom_buf, buf_size, read); 141 } else if (limit <= EEPROM_OFFSET_SIZE) { 142 dev_err_ratelimited(&i2c_adap->dev, 143 "maddr:0x%04X size:0x%02X:quirk max_%s_len must be > %d", 144 eeprom_addr, buf_size, 145 read ? "read" : "write", EEPROM_OFFSET_SIZE); 146 return -EINVAL; 147 } else { 148 u16 ps; /* Partial size */ 149 int res = 0, r; 150 151 /* The "limit" includes all data bytes sent/received, 152 * which would include the EEPROM_OFFSET_SIZE bytes. 153 * Account for them here. 154 */ 155 limit -= EEPROM_OFFSET_SIZE; 156 for ( ; buf_size > 0; 157 buf_size -= ps, eeprom_addr += ps, eeprom_buf += ps) { 158 ps = min(limit, buf_size); 159 160 r = __amdgpu_eeprom_xfer(i2c_adap, 161 slave_addr, eeprom_addr, 162 eeprom_buf, ps, read); 163 if (r < 0) 164 return r; 165 res += r; 166 } 167 168 return res; 169 } 170 } 171