Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.3-rc5 522 lines 13 kB view raw
1/* 2 * Driver for the Diolan u2c-12 USB-I2C adapter 3 * 4 * Copyright (c) 2010-2011 Ericsson AB 5 * 6 * Derived from: 7 * i2c-tiny-usb.c 8 * Copyright (C) 2006-2007 Till Harbaum (Till@Harbaum.org) 9 * 10 * This program is free software; you can redistribute it and/or 11 * modify it under the terms of the GNU General Public License as 12 * published by the Free Software Foundation, version 2. 13 */ 14 15#include <linux/kernel.h> 16#include <linux/errno.h> 17#include <linux/module.h> 18#include <linux/types.h> 19#include <linux/slab.h> 20#include <linux/usb.h> 21#include <linux/i2c.h> 22 23#define DRIVER_NAME "i2c-diolan-u2c" 24 25#define USB_VENDOR_ID_DIOLAN 0x0abf 26#define USB_DEVICE_ID_DIOLAN_U2C 0x3370 27 28#define DIOLAN_OUT_EP 0x02 29#define DIOLAN_IN_EP 0x84 30 31/* commands via USB, must match command ids in the firmware */ 32#define CMD_I2C_READ 0x01 33#define CMD_I2C_WRITE 0x02 34#define CMD_I2C_SCAN 0x03 /* Returns list of detected devices */ 35#define CMD_I2C_RELEASE_SDA 0x04 36#define CMD_I2C_RELEASE_SCL 0x05 37#define CMD_I2C_DROP_SDA 0x06 38#define CMD_I2C_DROP_SCL 0x07 39#define CMD_I2C_READ_SDA 0x08 40#define CMD_I2C_READ_SCL 0x09 41#define CMD_GET_FW_VERSION 0x0a 42#define CMD_GET_SERIAL 0x0b 43#define CMD_I2C_START 0x0c 44#define CMD_I2C_STOP 0x0d 45#define CMD_I2C_REPEATED_START 0x0e 46#define CMD_I2C_PUT_BYTE 0x0f 47#define CMD_I2C_GET_BYTE 0x10 48#define CMD_I2C_PUT_ACK 0x11 49#define CMD_I2C_GET_ACK 0x12 50#define CMD_I2C_PUT_BYTE_ACK 0x13 51#define CMD_I2C_GET_BYTE_ACK 0x14 52#define CMD_I2C_SET_SPEED 0x1b 53#define CMD_I2C_GET_SPEED 0x1c 54#define CMD_I2C_SET_CLK_SYNC 0x24 55#define CMD_I2C_GET_CLK_SYNC 0x25 56#define CMD_I2C_SET_CLK_SYNC_TO 0x26 57#define CMD_I2C_GET_CLK_SYNC_TO 0x27 58 59#define RESP_OK 0x00 60#define RESP_FAILED 0x01 61#define RESP_BAD_MEMADDR 0x04 62#define RESP_DATA_ERR 0x05 63#define RESP_NOT_IMPLEMENTED 0x06 64#define RESP_NACK 0x07 65#define RESP_TIMEOUT 0x09 66 67#define U2C_I2C_SPEED_FAST 0 /* 400 kHz */ 68#define U2C_I2C_SPEED_STD 1 /* 100 kHz */ 69#define U2C_I2C_SPEED_2KHZ 242 /* 2 kHz, minimum speed */ 70#define U2C_I2C_SPEED(f) ((DIV_ROUND_UP(1000000, (f)) - 10) / 2 + 1) 71 72#define U2C_I2C_FREQ_FAST 400000 73#define U2C_I2C_FREQ_STD 100000 74#define U2C_I2C_FREQ(s) (1000000 / (2 * (s - 1) + 10)) 75 76#define DIOLAN_USB_TIMEOUT 100 /* in ms */ 77#define DIOLAN_SYNC_TIMEOUT 20 /* in ms */ 78 79#define DIOLAN_OUTBUF_LEN 128 80#define DIOLAN_FLUSH_LEN (DIOLAN_OUTBUF_LEN - 4) 81#define DIOLAN_INBUF_LEN 256 /* Maximum supported receive length */ 82 83/* Structure to hold all of our device specific stuff */ 84struct i2c_diolan_u2c { 85 u8 obuffer[DIOLAN_OUTBUF_LEN]; /* output buffer */ 86 u8 ibuffer[DIOLAN_INBUF_LEN]; /* input buffer */ 87 struct usb_device *usb_dev; /* the usb device for this device */ 88 struct usb_interface *interface;/* the interface for this device */ 89 struct i2c_adapter adapter; /* i2c related things */ 90 int olen; /* Output buffer length */ 91 int ocount; /* Number of enqueued messages */ 92}; 93 94static uint frequency = U2C_I2C_FREQ_STD; /* I2C clock frequency in Hz */ 95 96module_param(frequency, uint, S_IRUGO | S_IWUSR); 97MODULE_PARM_DESC(frequency, "I2C clock frequency in hertz"); 98 99/* usb layer */ 100 101/* Send command to device, and get response. */ 102static int diolan_usb_transfer(struct i2c_diolan_u2c *dev) 103{ 104 int ret = 0; 105 int actual; 106 int i; 107 108 if (!dev->olen || !dev->ocount) 109 return -EINVAL; 110 111 ret = usb_bulk_msg(dev->usb_dev, 112 usb_sndbulkpipe(dev->usb_dev, DIOLAN_OUT_EP), 113 dev->obuffer, dev->olen, &actual, 114 DIOLAN_USB_TIMEOUT); 115 if (!ret) { 116 for (i = 0; i < dev->ocount; i++) { 117 int tmpret; 118 119 tmpret = usb_bulk_msg(dev->usb_dev, 120 usb_rcvbulkpipe(dev->usb_dev, 121 DIOLAN_IN_EP), 122 dev->ibuffer, 123 sizeof(dev->ibuffer), &actual, 124 DIOLAN_USB_TIMEOUT); 125 /* 126 * Stop command processing if a previous command 127 * returned an error. 128 * Note that we still need to retrieve all messages. 129 */ 130 if (ret < 0) 131 continue; 132 ret = tmpret; 133 if (ret == 0 && actual > 0) { 134 switch (dev->ibuffer[actual - 1]) { 135 case RESP_NACK: 136 /* 137 * Return ENXIO if NACK was received as 138 * response to the address phase, 139 * EIO otherwise 140 */ 141 ret = i == 1 ? -ENXIO : -EIO; 142 break; 143 case RESP_TIMEOUT: 144 ret = -ETIMEDOUT; 145 break; 146 case RESP_OK: 147 /* strip off return code */ 148 ret = actual - 1; 149 break; 150 default: 151 ret = -EIO; 152 break; 153 } 154 } 155 } 156 } 157 dev->olen = 0; 158 dev->ocount = 0; 159 return ret; 160} 161 162static int diolan_write_cmd(struct i2c_diolan_u2c *dev, bool flush) 163{ 164 if (flush || dev->olen >= DIOLAN_FLUSH_LEN) 165 return diolan_usb_transfer(dev); 166 return 0; 167} 168 169/* Send command (no data) */ 170static int diolan_usb_cmd(struct i2c_diolan_u2c *dev, u8 command, bool flush) 171{ 172 dev->obuffer[dev->olen++] = command; 173 dev->ocount++; 174 return diolan_write_cmd(dev, flush); 175} 176 177/* Send command with one byte of data */ 178static int diolan_usb_cmd_data(struct i2c_diolan_u2c *dev, u8 command, u8 data, 179 bool flush) 180{ 181 dev->obuffer[dev->olen++] = command; 182 dev->obuffer[dev->olen++] = data; 183 dev->ocount++; 184 return diolan_write_cmd(dev, flush); 185} 186 187/* Send command with two bytes of data */ 188static int diolan_usb_cmd_data2(struct i2c_diolan_u2c *dev, u8 command, u8 d1, 189 u8 d2, bool flush) 190{ 191 dev->obuffer[dev->olen++] = command; 192 dev->obuffer[dev->olen++] = d1; 193 dev->obuffer[dev->olen++] = d2; 194 dev->ocount++; 195 return diolan_write_cmd(dev, flush); 196} 197 198/* 199 * Flush input queue. 200 * If we don't do this at startup and the controller has queued up 201 * messages which were not retrieved, it will stop responding 202 * at some point. 203 */ 204static void diolan_flush_input(struct i2c_diolan_u2c *dev) 205{ 206 int i; 207 208 for (i = 0; i < 10; i++) { 209 int actual = 0; 210 int ret; 211 212 ret = usb_bulk_msg(dev->usb_dev, 213 usb_rcvbulkpipe(dev->usb_dev, DIOLAN_IN_EP), 214 dev->ibuffer, sizeof(dev->ibuffer), &actual, 215 DIOLAN_USB_TIMEOUT); 216 if (ret < 0 || actual == 0) 217 break; 218 } 219 if (i == 10) 220 dev_err(&dev->interface->dev, "Failed to flush input buffer\n"); 221} 222 223static int diolan_i2c_start(struct i2c_diolan_u2c *dev) 224{ 225 return diolan_usb_cmd(dev, CMD_I2C_START, false); 226} 227 228static int diolan_i2c_repeated_start(struct i2c_diolan_u2c *dev) 229{ 230 return diolan_usb_cmd(dev, CMD_I2C_REPEATED_START, false); 231} 232 233static int diolan_i2c_stop(struct i2c_diolan_u2c *dev) 234{ 235 return diolan_usb_cmd(dev, CMD_I2C_STOP, true); 236} 237 238static int diolan_i2c_get_byte_ack(struct i2c_diolan_u2c *dev, bool ack, 239 u8 *byte) 240{ 241 int ret; 242 243 ret = diolan_usb_cmd_data(dev, CMD_I2C_GET_BYTE_ACK, ack, true); 244 if (ret > 0) 245 *byte = dev->ibuffer[0]; 246 else if (ret == 0) 247 ret = -EIO; 248 249 return ret; 250} 251 252static int diolan_i2c_put_byte_ack(struct i2c_diolan_u2c *dev, u8 byte) 253{ 254 return diolan_usb_cmd_data(dev, CMD_I2C_PUT_BYTE_ACK, byte, false); 255} 256 257static int diolan_set_speed(struct i2c_diolan_u2c *dev, u8 speed) 258{ 259 return diolan_usb_cmd_data(dev, CMD_I2C_SET_SPEED, speed, true); 260} 261 262/* Enable or disable clock synchronization (stretching) */ 263static int diolan_set_clock_synch(struct i2c_diolan_u2c *dev, bool enable) 264{ 265 return diolan_usb_cmd_data(dev, CMD_I2C_SET_CLK_SYNC, enable, true); 266} 267 268/* Set clock synchronization timeout in ms */ 269static int diolan_set_clock_synch_timeout(struct i2c_diolan_u2c *dev, int ms) 270{ 271 int to_val = ms * 10; 272 273 return diolan_usb_cmd_data2(dev, CMD_I2C_SET_CLK_SYNC_TO, 274 to_val & 0xff, (to_val >> 8) & 0xff, true); 275} 276 277static void diolan_fw_version(struct i2c_diolan_u2c *dev) 278{ 279 int ret; 280 281 ret = diolan_usb_cmd(dev, CMD_GET_FW_VERSION, true); 282 if (ret >= 2) 283 dev_info(&dev->interface->dev, 284 "Diolan U2C firmware version %u.%u\n", 285 (unsigned int)dev->ibuffer[0], 286 (unsigned int)dev->ibuffer[1]); 287} 288 289static void diolan_get_serial(struct i2c_diolan_u2c *dev) 290{ 291 int ret; 292 u32 serial; 293 294 ret = diolan_usb_cmd(dev, CMD_GET_SERIAL, true); 295 if (ret >= 4) { 296 serial = le32_to_cpu(*(u32 *)dev->ibuffer); 297 dev_info(&dev->interface->dev, 298 "Diolan U2C serial number %u\n", serial); 299 } 300} 301 302static int diolan_init(struct i2c_diolan_u2c *dev) 303{ 304 int speed, ret; 305 306 if (frequency >= 200000) { 307 speed = U2C_I2C_SPEED_FAST; 308 frequency = U2C_I2C_FREQ_FAST; 309 } else if (frequency >= 100000 || frequency == 0) { 310 speed = U2C_I2C_SPEED_STD; 311 frequency = U2C_I2C_FREQ_STD; 312 } else { 313 speed = U2C_I2C_SPEED(frequency); 314 if (speed > U2C_I2C_SPEED_2KHZ) 315 speed = U2C_I2C_SPEED_2KHZ; 316 frequency = U2C_I2C_FREQ(speed); 317 } 318 319 dev_info(&dev->interface->dev, 320 "Diolan U2C at USB bus %03d address %03d speed %d Hz\n", 321 dev->usb_dev->bus->busnum, dev->usb_dev->devnum, frequency); 322 323 diolan_flush_input(dev); 324 diolan_fw_version(dev); 325 diolan_get_serial(dev); 326 327 /* Set I2C speed */ 328 ret = diolan_set_speed(dev, speed); 329 if (ret < 0) 330 return ret; 331 332 /* Configure I2C clock synchronization */ 333 ret = diolan_set_clock_synch(dev, speed != U2C_I2C_SPEED_FAST); 334 if (ret < 0) 335 return ret; 336 337 if (speed != U2C_I2C_SPEED_FAST) 338 ret = diolan_set_clock_synch_timeout(dev, DIOLAN_SYNC_TIMEOUT); 339 340 return ret; 341} 342 343/* i2c layer */ 344 345static int diolan_usb_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, 346 int num) 347{ 348 struct i2c_diolan_u2c *dev = i2c_get_adapdata(adapter); 349 struct i2c_msg *pmsg; 350 int i, j; 351 int ret, sret; 352 353 ret = diolan_i2c_start(dev); 354 if (ret < 0) 355 return ret; 356 357 for (i = 0; i < num; i++) { 358 pmsg = &msgs[i]; 359 if (i) { 360 ret = diolan_i2c_repeated_start(dev); 361 if (ret < 0) 362 goto abort; 363 } 364 if (pmsg->flags & I2C_M_RD) { 365 ret = 366 diolan_i2c_put_byte_ack(dev, (pmsg->addr << 1) | 1); 367 if (ret < 0) 368 goto abort; 369 for (j = 0; j < pmsg->len; j++) { 370 u8 byte; 371 bool ack = j < pmsg->len - 1; 372 373 /* 374 * Don't send NACK if this is the first byte 375 * of a SMBUS_BLOCK message. 376 */ 377 if (j == 0 && (pmsg->flags & I2C_M_RECV_LEN)) 378 ack = true; 379 380 ret = diolan_i2c_get_byte_ack(dev, ack, &byte); 381 if (ret < 0) 382 goto abort; 383 /* 384 * Adjust count if first received byte is length 385 */ 386 if (j == 0 && (pmsg->flags & I2C_M_RECV_LEN)) { 387 if (byte == 0 388 || byte > I2C_SMBUS_BLOCK_MAX) { 389 ret = -EPROTO; 390 goto abort; 391 } 392 pmsg->len += byte; 393 } 394 pmsg->buf[j] = byte; 395 } 396 } else { 397 ret = diolan_i2c_put_byte_ack(dev, pmsg->addr << 1); 398 if (ret < 0) 399 goto abort; 400 for (j = 0; j < pmsg->len; j++) { 401 ret = diolan_i2c_put_byte_ack(dev, 402 pmsg->buf[j]); 403 if (ret < 0) 404 goto abort; 405 } 406 } 407 } 408abort: 409 sret = diolan_i2c_stop(dev); 410 if (sret < 0 && ret >= 0) 411 ret = sret; 412 return ret; 413} 414 415/* 416 * Return list of supported functionality. 417 */ 418static u32 diolan_usb_func(struct i2c_adapter *a) 419{ 420 return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | 421 I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL; 422} 423 424static const struct i2c_algorithm diolan_usb_algorithm = { 425 .master_xfer = diolan_usb_xfer, 426 .functionality = diolan_usb_func, 427}; 428 429/* device layer */ 430 431static const struct usb_device_id diolan_u2c_table[] = { 432 { USB_DEVICE(USB_VENDOR_ID_DIOLAN, USB_DEVICE_ID_DIOLAN_U2C) }, 433 { } 434}; 435 436MODULE_DEVICE_TABLE(usb, diolan_u2c_table); 437 438static void diolan_u2c_free(struct i2c_diolan_u2c *dev) 439{ 440 usb_put_dev(dev->usb_dev); 441 kfree(dev); 442} 443 444static int diolan_u2c_probe(struct usb_interface *interface, 445 const struct usb_device_id *id) 446{ 447 struct i2c_diolan_u2c *dev; 448 int ret; 449 450 /* allocate memory for our device state and initialize it */ 451 dev = kzalloc(sizeof(*dev), GFP_KERNEL); 452 if (dev == NULL) { 453 dev_err(&interface->dev, "no memory for device state\n"); 454 ret = -ENOMEM; 455 goto error; 456 } 457 458 dev->usb_dev = usb_get_dev(interface_to_usbdev(interface)); 459 dev->interface = interface; 460 461 /* save our data pointer in this interface device */ 462 usb_set_intfdata(interface, dev); 463 464 /* setup i2c adapter description */ 465 dev->adapter.owner = THIS_MODULE; 466 dev->adapter.class = I2C_CLASS_HWMON; 467 dev->adapter.algo = &diolan_usb_algorithm; 468 i2c_set_adapdata(&dev->adapter, dev); 469 snprintf(dev->adapter.name, sizeof(dev->adapter.name), 470 DRIVER_NAME " at bus %03d device %03d", 471 dev->usb_dev->bus->busnum, dev->usb_dev->devnum); 472 473 dev->adapter.dev.parent = &dev->interface->dev; 474 475 /* initialize diolan i2c interface */ 476 ret = diolan_init(dev); 477 if (ret < 0) { 478 dev_err(&interface->dev, "failed to initialize adapter\n"); 479 goto error_free; 480 } 481 482 /* and finally attach to i2c layer */ 483 ret = i2c_add_adapter(&dev->adapter); 484 if (ret < 0) { 485 dev_err(&interface->dev, "failed to add I2C adapter\n"); 486 goto error_free; 487 } 488 489 dev_dbg(&interface->dev, "connected " DRIVER_NAME "\n"); 490 491 return 0; 492 493error_free: 494 usb_set_intfdata(interface, NULL); 495 diolan_u2c_free(dev); 496error: 497 return ret; 498} 499 500static void diolan_u2c_disconnect(struct usb_interface *interface) 501{ 502 struct i2c_diolan_u2c *dev = usb_get_intfdata(interface); 503 504 i2c_del_adapter(&dev->adapter); 505 usb_set_intfdata(interface, NULL); 506 diolan_u2c_free(dev); 507 508 dev_dbg(&interface->dev, "disconnected\n"); 509} 510 511static struct usb_driver diolan_u2c_driver = { 512 .name = DRIVER_NAME, 513 .probe = diolan_u2c_probe, 514 .disconnect = diolan_u2c_disconnect, 515 .id_table = diolan_u2c_table, 516}; 517 518module_usb_driver(diolan_u2c_driver); 519 520MODULE_AUTHOR("Guenter Roeck <guenter.roeck@ericsson.com>"); 521MODULE_DESCRIPTION(DRIVER_NAME " driver"); 522MODULE_LICENSE("GPL");