/* * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /* * Copyright (c) 2012 Intel Corporation. All rights reserved. */ #include "drmP.h" #define mutex_lock_nested(a,b) mutex_enter(a) #define I2C_HI_CLOCK(adap, ret) \ do { \ ret = i2c_setscl(adap); \ if (ret) { \ /* Other master keeps the clock low. */ \ /* Free the bus. */ \ i2c_setsda(adap); \ i2c_udelay(adap); \ return ret; \ } \ } while (*"\0"); static inline void i2c_udelay(struct i2c_adapter *adap) { udelay((adap->udelay + 1) >> 1); } static inline int i2c_getsda(struct i2c_adapter *adap) { return adap->getsda(adap->data) ? 1 : 0; } static inline void i2c_clrsda(struct i2c_adapter *adap) { adap->setsda(adap->data, 0); } static inline void i2c_setsda(struct i2c_adapter *adap) { adap->setsda(adap->data, 1); } static inline int i2c_getscl(struct i2c_adapter *adap) { return adap->getscl(adap->data) ? 1 : 0; } static inline void i2c_clrscl(struct i2c_adapter *adap) { adap->setscl(adap->data, 0); } static int i2c_setscl(struct i2c_adapter *adap) { clock_t start; adap->setscl(adap->data, 1); /* Clock Synchronization */ start = ddi_get_lbolt(); while (!i2c_getscl(adap)) { /* FIXME: Does ddi_get_lbolt() return negative * value? If so, leave me. */ if ((ddi_get_lbolt() - start) > adap->timeout) return -ETIMEDOUT; } return 0; } static int i2c_start(struct i2c_adapter *adap) { int ret = 0; /* Step 1: free the bus. */ i2c_setsda(adap); i2c_udelay(adap); ret = i2c_setscl(adap); if (ret) { /* Other master keeps the clock low. * The bus is busy. */ return ret; } if (!i2c_getsda(adap)) { /* The bus is busy. */ return -EBUSY; } i2c_udelay(adap); /* Step 2: (S/Sr) condition. */ i2c_clrsda(adap); i2c_udelay(adap); /* Step 3: free the clock. */ i2c_clrscl(adap); i2c_udelay(adap); return 0; } static int i2c_stop(struct i2c_adapter *adap) { int ret; if (i2c_getscl(adap)) { /* Stop() must be called after start() or any * transfer routines, which all free the clock * before returning. */ return -ENOTSUP; } /* Step 1: Free the data */ i2c_clrsda(adap); i2c_udelay(adap); /* Step 2: Hold the clock */ I2C_HI_CLOCK(adap, ret); i2c_udelay(adap); /* Step 3: (P) condition */ i2c_setsda(adap); i2c_udelay(adap); if (!i2c_getsda(adap)) { /* Other master keeps the data low. * The bus is busy. */ return -EBUSY; } return 0; } static int i2c_write_byte(struct i2c_adapter *adap, unsigned char c) { int needARB = 0; int ret = 0, i; if (i2c_getscl(adap)) { /* Write() must be called after start() or any * transfer routines, which all free the clock * before returning. */ return -ENOTSUP; } for (i = 7; i >= 0; i--) { /* Step 1: set data. */ if (c & (1 << i)) { needARB = 1; i2c_setsda(adap); } else { needARB = 0; i2c_clrsda(adap); } i2c_udelay(adap); /* Step 2: hold the clock. */ I2C_HI_CLOCK(adap, ret); if (needARB && !i2c_getsda(adap)) { /* Do arbitration: lose the bus. */ return -EBUSY; } /* Double delay. */ i2c_udelay(adap); i2c_udelay(adap); if (needARB && !i2c_getsda(adap)) { /* Do arbitration: someone performs (S) condition. */ return -EBUSY; } /* Step 3: free the clock. */ i2c_clrscl(adap); i2c_udelay(adap); } return 0; } static int i2c_read_byte(struct i2c_adapter *adap, unsigned char *cp) { int ret, r, i; if (i2c_getscl(adap)) { /* Read() must be called after start() or any * transfer routines, which all free the clock * before returning. */ return -ENOTSUP; } i2c_setsda(adap); i2c_udelay(adap); *cp = 0; for (i = 7; i >= 0; i--) { /* Step 1: hold the clock. */ I2C_HI_CLOCK(adap, ret); r = i2c_getsda(adap); i2c_udelay(adap); /* Step 2: read the data. */ if (r != i2c_getsda(adap)) { /* Do arbitration: someone performs (S/Sr/P) condition. */ return -EBUSY; } if (r) *cp |= (1 << i); i2c_udelay(adap); /* Step 3: free the clock */ i2c_clrscl(adap); i2c_udelay(adap); } return 0; } static int i2c_ack(struct i2c_adapter *adap) { int ret; if (i2c_getscl(adap)) { /* Ack() must be called after start() or any * transfer routines, which all free the clock * before returning. */ return -ENOTSUP; } /* Step 1: free the data. */ i2c_clrsda(adap); i2c_udelay(adap); /* Step 2: hold the clock. */ I2C_HI_CLOCK(adap, ret); i2c_udelay(adap); /* Step 3: free the clock */ i2c_clrscl(adap); i2c_udelay(adap); return 0; } static int i2c_no_ack(struct i2c_adapter *adap) { int ret; if (i2c_getscl(adap)) { /* Nak() must be called after start() or any * transfer routines, which all free the clock * before returning. */ return -ENOTSUP; } /* Step 1: hold the data. */ i2c_setsda(adap); i2c_udelay(adap); /* Step 2: hold the clock. */ I2C_HI_CLOCK(adap, ret); if (!i2c_getsda(adap)) { /* Other master keeps the data low. */ return -EBUSY; } i2c_udelay(adap); if (!i2c_getsda(adap)) { /* Do arbitration: someone performs (S/Sr) condition. */ return -EBUSY; } /* Step 3: free the clock */ i2c_clrscl(adap); i2c_udelay(adap); return 0; } static int i2c_wait_ack(struct i2c_adapter *adap) { int ret; if (i2c_getscl(adap)) { /* Wack() must be called after start() or any * transfer routines, which all free the clock * before returning. */ return -ENOTSUP; } /* Step 1: hold the data. */ i2c_setsda(adap); i2c_udelay(adap); /* Step 2: hold the clock. */ I2C_HI_CLOCK(adap, ret); i2c_udelay(adap); /* Step 3: read the data. */ ret = i2c_getsda(adap) ? 0 : 1; /* Step 4: free the clock */ i2c_clrscl(adap); i2c_udelay(adap); return ret; } static int i2c_write_msg(struct i2c_adapter *adap, struct i2c_msg *msg) { int i, ret; for (i = 0; i < msg->len; i++) { ret = i2c_write_byte(adap, msg->buf[i]); if (ret) return ret; ret = i2c_wait_ack(adap); if (ret == 1) continue; else if (ret == 0) return -ENXIO; else return ret; } return 0; } static int i2c_read_msg(struct i2c_adapter *adap, struct i2c_msg *msg) { unsigned char c; int i, ret; for (i = 0; i < msg->len; i++) { ret = i2c_read_byte(adap, &c); if (ret) return ret; msg->buf[i] = c; if (i < msg->len - 1) ret = i2c_ack(adap); else ret = i2c_no_ack(adap); if (ret) return ret; } return 0; } static int i2c_address(struct i2c_adapter *adap, struct i2c_msg *msg) { unsigned char addr; int ret; addr = msg->addr << 1; if (msg->flags & I2C_M_RD) addr |= 1; ret = i2c_write_byte(adap, addr); if (ret) return ret; ret = i2c_wait_ack(adap); if (ret == 1) return 0; else if (ret == 0) return -ENXIO; return ret; } static int i2c_do_transfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) { struct i2c_msg *msg; int i, ret = 0; for (i = 0; i < num; i++) { msg = &msgs[i]; if (!(i && (msg->flags & I2C_M_NOSTART))) { ret = i2c_start(adap); if (ret) return ret; ret = i2c_address(adap, msg); if (ret) return ret; } if (msg->flags & I2C_M_RD) ret = i2c_read_msg(adap, msg); else ret = i2c_write_msg(adap, msg); if (ret) return ret; } ret = i2c_stop(adap); if (ret) return ret; return num; } struct i2c_algorithm i2c_bit_algo = { .master_xfer = i2c_do_transfer, .functionality = NULL, }; int i2c_bit_add_bus(struct i2c_adapter *adap) { if (!adap->setscl || !adap->getscl || !adap->setsda || !adap->getsda) return -EINVAL; adap->algo = (struct i2c_algorithm *)&i2c_bit_algo; adap->retries = 3; return 0; } int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { clock_t start; int i, ret = 0; mutex_enter(&adap->bus_lock); start = ddi_get_lbolt(); for (i = 0; i <= adap->retries; i++) { ret = adap->algo->master_xfer(adap, msgs, num); switch (ret) { case 0: case ETIMEDOUT: goto do_exit; default: break; } /* FIXME: Does ddi_get_lbolt() return negative * value? If so, leave me. */ if ((ddi_get_lbolt() - start) > adap->timeout) break; } do_exit: mutex_exit(&adap->bus_lock); return ret; }