Blame view
drivers/i2c/at91_i2c.c
6.84 KB
83d290c56 SPDX: Convert all... |
1 |
// SPDX-License-Identifier: GPL-2.0+ |
8800e0fa2 i2c: atmel: add i... |
2 3 4 5 |
/* * Atmel I2C driver. * * (C) Copyright 2016 Songjun Wu <songjun.wu@atmel.com> |
8800e0fa2 i2c: atmel: add i... |
6 7 8 9 |
*/ #include <asm/io.h> #include <common.h> |
76062b9cd i2c: at91_i2c: Fi... |
10 |
#include <clk.h> |
8800e0fa2 i2c: atmel: add i... |
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
#include <dm.h> #include <errno.h> #include <fdtdec.h> #include <i2c.h> #include <linux/bitops.h> #include <mach/clk.h> #include "at91_i2c.h" DECLARE_GLOBAL_DATA_PTR; #define I2C_TIMEOUT_MS 100 static int at91_wait_for_xfer(struct at91_i2c_bus *bus, u32 status) { struct at91_i2c_regs *reg = bus->regs; ulong start_time = get_timer(0); u32 sr; bus->status = 0; do { sr = readl(®->sr); bus->status |= sr; if (sr & TWI_SR_NACK) return -EREMOTEIO; else if (sr & status) return 0; } while (get_timer(start_time) < I2C_TIMEOUT_MS); return -ETIMEDOUT; } static int at91_i2c_xfer_msg(struct at91_i2c_bus *bus, struct i2c_msg *msg) { struct at91_i2c_regs *reg = bus->regs; bool is_read = msg->flags & I2C_M_RD; u32 i; int ret = 0; readl(®->sr); if (is_read) { writel(TWI_CR_START, ®->cr); for (i = 0; !ret && i < (msg->len - 1); i++) { ret = at91_wait_for_xfer(bus, TWI_SR_RXRDY); msg->buf[i] = readl(®->rhr); } if (ret) goto error; writel(TWI_CR_STOP, ®->cr); ret = at91_wait_for_xfer(bus, TWI_SR_RXRDY); if (ret) goto error; msg->buf[i] = readl(®->rhr); } else { writel(msg->buf[0], ®->thr); |
0afbb0e1c i2c: at91_i2c: Wa... |
74 |
ret = at91_wait_for_xfer(bus, TWI_SR_TXRDY); |
8800e0fa2 i2c: atmel: add i... |
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
for (i = 1; !ret && (i < msg->len); i++) { writel(msg->buf[i], ®->thr); ret = at91_wait_for_xfer(bus, TWI_SR_TXRDY); } if (ret) goto error; writel(TWI_CR_STOP, ®->cr); } if (!ret) ret = at91_wait_for_xfer(bus, TWI_SR_TXCOMP); if (ret) goto error; if (bus->status & (TWI_SR_OVRE | TWI_SR_UNRE | TWI_SR_LOCK)) { ret = -EIO; goto error; } return 0; error: if (bus->status & TWI_SR_LOCK) writel(TWI_CR_LOCKCLR, ®->cr); return ret; } static int at91_i2c_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs) { struct at91_i2c_bus *bus = dev_get_priv(dev); struct at91_i2c_regs *reg = bus->regs; struct i2c_msg *m_start = msg; bool is_read; u32 int_addr_flag = 0; int ret = 0; if (nmsgs == 2) { int internal_address = 0; int i; /* 1st msg is put into the internal address, start with 2nd */ m_start = &msg[1]; /* the max length of internal address is 3 bytes */ if (msg->len > 3) return -EFAULT; for (i = 0; i < msg->len; ++i) { const unsigned addr = msg->buf[msg->len - 1 - i]; internal_address |= addr << (8 * i); int_addr_flag += TWI_MMR_IADRSZ_1; } writel(internal_address, ®->iadr); } is_read = m_start->flags & I2C_M_RD; writel((m_start->addr << 16) | int_addr_flag | (is_read ? TWI_MMR_MREAD : 0), ®->mmr); ret = at91_i2c_xfer_msg(bus, m_start); return ret; } /* * Calculate symmetric clock as stated in datasheet: * twi_clk = F_MAIN / (2 * (cdiv * (1 << ckdiv) + offset)) */ static void at91_calc_i2c_clock(struct udevice *dev, int i2c_clk) { struct at91_i2c_bus *bus = dev_get_priv(dev); const struct at91_i2c_pdata *pdata = bus->pdata; int offset = pdata->clk_offset; int max_ckdiv = pdata->clk_max_div; int ckdiv, cdiv, div; unsigned long src_rate; src_rate = bus->bus_clk_rate; div = max(0, (int)DIV_ROUND_UP(src_rate, 2 * i2c_clk) - offset); ckdiv = fls(div >> 8); cdiv = div >> ckdiv; if (ckdiv > max_ckdiv) { ckdiv = max_ckdiv; cdiv = 255; } bus->speed = DIV_ROUND_UP(src_rate, (cdiv * (1 << ckdiv) + offset) * 2); bus->cwgr_val = (ckdiv << 16) | (cdiv << 8) | cdiv; } static int at91_i2c_enable_clk(struct udevice *dev) { struct at91_i2c_bus *bus = dev_get_priv(dev); |
8800e0fa2 i2c: atmel: add i... |
179 180 |
struct clk clk; ulong clk_rate; |
8800e0fa2 i2c: atmel: add i... |
181 182 183 184 185 |
int ret; ret = clk_get_by_index(dev, 0, &clk); if (ret) return -EINVAL; |
8800e0fa2 i2c: atmel: add i... |
186 187 188 |
ret = clk_enable(&clk); if (ret) return ret; |
8800e0fa2 i2c: atmel: add i... |
189 190 |
clk_rate = clk_get_rate(&clk); if (!clk_rate) |
52f37333b i2c: at91_i2c: Ch... |
191 |
return -EINVAL; |
8800e0fa2 i2c: atmel: add i... |
192 193 194 195 196 197 198 |
bus->bus_clk_rate = clk_rate; clk_free(&clk); return 0; } |
8800e0fa2 i2c: atmel: add i... |
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 |
static int at91_i2c_set_bus_speed(struct udevice *dev, unsigned int speed) { struct at91_i2c_bus *bus = dev_get_priv(dev); at91_calc_i2c_clock(dev, speed); writel(bus->cwgr_val, &bus->regs->cwgr); return 0; } int at91_i2c_get_bus_speed(struct udevice *dev) { struct at91_i2c_bus *bus = dev_get_priv(dev); return bus->speed; } static int at91_i2c_ofdata_to_platdata(struct udevice *dev) { const void *blob = gd->fdt_blob; struct at91_i2c_bus *bus = dev_get_priv(dev); |
e160f7d43 dm: core: Replace... |
221 |
int node = dev_of_offset(dev); |
8800e0fa2 i2c: atmel: add i... |
222 |
|
a821c4af7 dm: Rename dev_ad... |
223 |
bus->regs = (struct at91_i2c_regs *)devfdt_get_addr(dev); |
8800e0fa2 i2c: atmel: add i... |
224 225 226 227 228 229 230 231 232 |
bus->pdata = (struct at91_i2c_pdata *)dev_get_driver_data(dev); bus->clock_frequency = fdtdec_get_int(blob, node, "clock-frequency", 100000); return 0; } static const struct dm_i2c_ops at91_i2c_ops = { .xfer = at91_i2c_xfer, |
8800e0fa2 i2c: atmel: add i... |
233 234 235 |
.set_bus_speed = at91_i2c_set_bus_speed, .get_bus_speed = at91_i2c_get_bus_speed, }; |
0bc8f640a i2c: at91: Add mi... |
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 |
static int at91_i2c_probe(struct udevice *dev) { struct at91_i2c_bus *bus = dev_get_priv(dev); struct at91_i2c_regs *reg = bus->regs; int ret; ret = at91_i2c_enable_clk(dev); if (ret) return ret; writel(TWI_CR_SWRST, ®->cr); at91_calc_i2c_clock(dev, bus->clock_frequency); writel(bus->cwgr_val, ®->cwgr); writel(TWI_CR_MSEN, ®->cr); writel(TWI_CR_SVDIS, ®->cr); return 0; } |
8800e0fa2 i2c: atmel: add i... |
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 |
static const struct at91_i2c_pdata at91rm9200_config = { .clk_max_div = 5, .clk_offset = 3, }; static const struct at91_i2c_pdata at91sam9261_config = { .clk_max_div = 5, .clk_offset = 4, }; static const struct at91_i2c_pdata at91sam9260_config = { .clk_max_div = 7, .clk_offset = 4, }; static const struct at91_i2c_pdata at91sam9g20_config = { .clk_max_div = 7, .clk_offset = 4, }; static const struct at91_i2c_pdata at91sam9g10_config = { .clk_max_div = 7, .clk_offset = 4, }; static const struct at91_i2c_pdata at91sam9x5_config = { .clk_max_div = 7, .clk_offset = 4, }; static const struct at91_i2c_pdata sama5d4_config = { .clk_max_div = 7, .clk_offset = 4, }; static const struct at91_i2c_pdata sama5d2_config = { .clk_max_div = 7, .clk_offset = 3, }; static const struct udevice_id at91_i2c_ids[] = { { .compatible = "atmel,at91rm9200-i2c", .data = (long)&at91rm9200_config }, { .compatible = "atmel,at91sam9260-i2c", .data = (long)&at91sam9260_config }, { .compatible = "atmel,at91sam9261-i2c", .data = (long)&at91sam9261_config }, { .compatible = "atmel,at91sam9g20-i2c", .data = (long)&at91sam9g20_config }, { .compatible = "atmel,at91sam9g10-i2c", .data = (long)&at91sam9g10_config }, { .compatible = "atmel,at91sam9x5-i2c", .data = (long)&at91sam9x5_config }, { .compatible = "atmel,sama5d4-i2c", .data = (long)&sama5d4_config }, { .compatible = "atmel,sama5d2-i2c", .data = (long)&sama5d2_config }, { } }; U_BOOT_DRIVER(i2c_at91) = { .name = "i2c_at91", .id = UCLASS_I2C, .of_match = at91_i2c_ids, |
0bc8f640a i2c: at91: Add mi... |
312 |
.probe = at91_i2c_probe, |
8800e0fa2 i2c: atmel: add i... |
313 314 315 316 317 |
.ofdata_to_platdata = at91_i2c_ofdata_to_platdata, .per_child_auto_alloc_size = sizeof(struct dm_i2c_chip), .priv_auto_alloc_size = sizeof(struct at91_i2c_bus), .ops = &at91_i2c_ops, }; |