diff options
Diffstat (limited to 'drivers/i2c')
-rw-r--r-- | drivers/i2c/busses/Kconfig | 12 | ||||
-rw-r--r-- | drivers/i2c/busses/Makefile | 1 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-mv64xxx.c | 328 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-opal.c | 294 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-sh_mobile.c | 112 |
5 files changed, 558 insertions, 189 deletions
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index c1351d9fb35b..31e8308ba899 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -753,6 +753,7 @@ config I2C_SH7760 config I2C_SH_MOBILE tristate "SuperH Mobile I2C Controller" + depends on HAS_DMA depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST help If you say yes to this option, support will be included for the @@ -1072,4 +1073,15 @@ config SCx200_ACB This support is also available as a module. If so, the module will be called scx200_acb. +config I2C_OPAL + tristate "IBM OPAL I2C driver" + depends on PPC_POWERNV + default y + help + This exposes the PowerNV platform i2c busses to the linux i2c layer, + the driver is based on the OPAL interfaces. + + This driver can also be built as a module. If so, the module will be + called as i2c-opal. + endmenu diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 5e6c8223719e..56388f658d2f 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -102,6 +102,7 @@ obj-$(CONFIG_I2C_ACORN) += i2c-acorn.o obj-$(CONFIG_I2C_BCM_KONA) += i2c-bcm-kona.o obj-$(CONFIG_I2C_CROS_EC_TUNNEL) += i2c-cros-ec-tunnel.o obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o +obj-$(CONFIG_I2C_OPAL) += i2c-opal.o obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o obj-$(CONFIG_I2C_SIBYTE) += i2c-sibyte.o obj-$(CONFIG_SCx200_ACB) += scx200_acb.o diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c index 373f6d4e4080..30059c1df2a3 100644 --- a/drivers/i2c/busses/i2c-mv64xxx.c +++ b/drivers/i2c/busses/i2c-mv64xxx.c @@ -30,12 +30,12 @@ #define MV64XXX_I2C_BAUD_DIV_N(val) (val & 0x7) #define MV64XXX_I2C_BAUD_DIV_M(val) ((val & 0xf) << 3) -#define MV64XXX_I2C_REG_CONTROL_ACK 0x00000004 -#define MV64XXX_I2C_REG_CONTROL_IFLG 0x00000008 -#define MV64XXX_I2C_REG_CONTROL_STOP 0x00000010 -#define MV64XXX_I2C_REG_CONTROL_START 0x00000020 -#define MV64XXX_I2C_REG_CONTROL_TWSIEN 0x00000040 -#define MV64XXX_I2C_REG_CONTROL_INTEN 0x00000080 +#define MV64XXX_I2C_REG_CONTROL_ACK BIT(2) +#define MV64XXX_I2C_REG_CONTROL_IFLG BIT(3) +#define MV64XXX_I2C_REG_CONTROL_STOP BIT(4) +#define MV64XXX_I2C_REG_CONTROL_START BIT(5) +#define MV64XXX_I2C_REG_CONTROL_TWSIEN BIT(6) +#define MV64XXX_I2C_REG_CONTROL_INTEN BIT(7) /* Ctlr status values */ #define MV64XXX_I2C_STATUS_BUS_ERR 0x00 @@ -68,19 +68,17 @@ #define MV64XXX_I2C_REG_BRIDGE_TIMING 0xe0 /* Bridge Control values */ -#define MV64XXX_I2C_BRIDGE_CONTROL_WR 0x00000001 -#define MV64XXX_I2C_BRIDGE_CONTROL_RD 0x00000002 +#define MV64XXX_I2C_BRIDGE_CONTROL_WR BIT(0) +#define MV64XXX_I2C_BRIDGE_CONTROL_RD BIT(1) #define MV64XXX_I2C_BRIDGE_CONTROL_ADDR_SHIFT 2 -#define MV64XXX_I2C_BRIDGE_CONTROL_ADDR_EXT 0x00001000 +#define MV64XXX_I2C_BRIDGE_CONTROL_ADDR_EXT BIT(12) #define MV64XXX_I2C_BRIDGE_CONTROL_TX_SIZE_SHIFT 13 #define MV64XXX_I2C_BRIDGE_CONTROL_RX_SIZE_SHIFT 16 -#define MV64XXX_I2C_BRIDGE_CONTROL_ENABLE 0x00080000 +#define MV64XXX_I2C_BRIDGE_CONTROL_ENABLE BIT(19) +#define MV64XXX_I2C_BRIDGE_CONTROL_REPEATED_START BIT(20) /* Bridge Status values */ -#define MV64XXX_I2C_BRIDGE_STATUS_ERROR 0x00000001 -#define MV64XXX_I2C_STATUS_OFFLOAD_ERROR 0xf0000001 -#define MV64XXX_I2C_STATUS_OFFLOAD_OK 0xf0000000 - +#define MV64XXX_I2C_BRIDGE_STATUS_ERROR BIT(0) /* Driver states */ enum { @@ -99,14 +97,12 @@ enum { MV64XXX_I2C_ACTION_INVALID, MV64XXX_I2C_ACTION_CONTINUE, MV64XXX_I2C_ACTION_SEND_RESTART, - MV64XXX_I2C_ACTION_OFFLOAD_RESTART, MV64XXX_I2C_ACTION_SEND_ADDR_1, MV64XXX_I2C_ACTION_SEND_ADDR_2, MV64XXX_I2C_ACTION_SEND_DATA, MV64XXX_I2C_ACTION_RCV_DATA, MV64XXX_I2C_ACTION_RCV_DATA_STOP, MV64XXX_I2C_ACTION_SEND_STOP, - MV64XXX_I2C_ACTION_OFFLOAD_SEND_STOP, }; struct mv64xxx_i2c_regs { @@ -193,75 +189,6 @@ mv64xxx_i2c_prepare_for_io(struct mv64xxx_i2c_data *drv_data, } } -static int mv64xxx_i2c_offload_msg(struct mv64xxx_i2c_data *drv_data) -{ - unsigned long data_reg_hi = 0; - unsigned long data_reg_lo = 0; - unsigned long ctrl_reg; - struct i2c_msg *msg = drv_data->msgs; - - if (!drv_data->offload_enabled) - return -EOPNOTSUPP; - - /* Only regular transactions can be offloaded */ - if ((msg->flags & ~(I2C_M_TEN | I2C_M_RD)) != 0) - return -EINVAL; - - /* Only 1-8 byte transfers can be offloaded */ - if (msg->len < 1 || msg->len > 8) - return -EINVAL; - - /* Build transaction */ - ctrl_reg = MV64XXX_I2C_BRIDGE_CONTROL_ENABLE | - (msg->addr << MV64XXX_I2C_BRIDGE_CONTROL_ADDR_SHIFT); - - if ((msg->flags & I2C_M_TEN) != 0) - ctrl_reg |= MV64XXX_I2C_BRIDGE_CONTROL_ADDR_EXT; - - if ((msg->flags & I2C_M_RD) == 0) { - u8 local_buf[8] = { 0 }; - - memcpy(local_buf, msg->buf, msg->len); - data_reg_lo = cpu_to_le32(*((u32 *)local_buf)); - data_reg_hi = cpu_to_le32(*((u32 *)(local_buf+4))); - - ctrl_reg |= MV64XXX_I2C_BRIDGE_CONTROL_WR | - (msg->len - 1) << MV64XXX_I2C_BRIDGE_CONTROL_TX_SIZE_SHIFT; - - writel(data_reg_lo, - drv_data->reg_base + MV64XXX_I2C_REG_TX_DATA_LO); - writel(data_reg_hi, - drv_data->reg_base + MV64XXX_I2C_REG_TX_DATA_HI); - - } else { - ctrl_reg |= MV64XXX_I2C_BRIDGE_CONTROL_RD | - (msg->len - 1) << MV64XXX_I2C_BRIDGE_CONTROL_RX_SIZE_SHIFT; - } - - /* Execute transaction */ - writel(ctrl_reg, drv_data->reg_base + MV64XXX_I2C_REG_BRIDGE_CONTROL); - - return 0; -} - -static void -mv64xxx_i2c_update_offload_data(struct mv64xxx_i2c_data *drv_data) -{ - struct i2c_msg *msg = drv_data->msg; - - if (msg->flags & I2C_M_RD) { - u32 data_reg_lo = readl(drv_data->reg_base + - MV64XXX_I2C_REG_RX_DATA_LO); - u32 data_reg_hi = readl(drv_data->reg_base + - MV64XXX_I2C_REG_RX_DATA_HI); - u8 local_buf[8] = { 0 }; - - *((u32 *)local_buf) = le32_to_cpu(data_reg_lo); - *((u32 *)(local_buf+4)) = le32_to_cpu(data_reg_hi); - memcpy(msg->buf, local_buf, msg->len); - } - -} /* ***************************************************************************** * @@ -389,16 +316,6 @@ mv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 status) drv_data->rc = -ENXIO; break; - case MV64XXX_I2C_STATUS_OFFLOAD_OK: - if (drv_data->send_stop || drv_data->aborting) { - drv_data->action = MV64XXX_I2C_ACTION_OFFLOAD_SEND_STOP; - drv_data->state = MV64XXX_I2C_STATE_IDLE; - } else { - drv_data->action = MV64XXX_I2C_ACTION_OFFLOAD_RESTART; - drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_RESTART; - } - break; - default: dev_err(&drv_data->adapter.dev, "mv64xxx_i2c_fsm: Ctlr Error -- state: 0x%x, " @@ -419,25 +336,15 @@ static void mv64xxx_i2c_send_start(struct mv64xxx_i2c_data *drv_data) drv_data->aborting = 0; drv_data->rc = 0; - /* Can we offload this msg ? */ - if (mv64xxx_i2c_offload_msg(drv_data) < 0) { - /* No, switch to standard path */ - mv64xxx_i2c_prepare_for_io(drv_data, drv_data->msgs); - writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_START, - drv_data->reg_base + drv_data->reg_offsets.control); - } + mv64xxx_i2c_prepare_for_io(drv_data, drv_data->msgs); + writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_START, + drv_data->reg_base + drv_data->reg_offsets.control); } static void mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data) { switch(drv_data->action) { - case MV64XXX_I2C_ACTION_OFFLOAD_RESTART: - mv64xxx_i2c_update_offload_data(drv_data); - writel(0, drv_data->reg_base + MV64XXX_I2C_REG_BRIDGE_CONTROL); - writel(0, drv_data->reg_base + - MV64XXX_I2C_REG_BRIDGE_INTR_CAUSE); - /* FALLTHRU */ case MV64XXX_I2C_ACTION_SEND_RESTART: /* We should only get here if we have further messages */ BUG_ON(drv_data->num_msgs == 0); @@ -518,16 +425,71 @@ mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data) drv_data->block = 0; wake_up(&drv_data->waitq); break; + } +} - case MV64XXX_I2C_ACTION_OFFLOAD_SEND_STOP: - mv64xxx_i2c_update_offload_data(drv_data); - writel(0, drv_data->reg_base + MV64XXX_I2C_REG_BRIDGE_CONTROL); - writel(0, drv_data->reg_base + - MV64XXX_I2C_REG_BRIDGE_INTR_CAUSE); - drv_data->block = 0; - wake_up(&drv_data->waitq); - break; +static void +mv64xxx_i2c_read_offload_rx_data(struct mv64xxx_i2c_data *drv_data, + struct i2c_msg *msg) +{ + u32 buf[2]; + + buf[0] = readl(drv_data->reg_base + MV64XXX_I2C_REG_RX_DATA_LO); + buf[1] = readl(drv_data->reg_base + MV64XXX_I2C_REG_RX_DATA_HI); + + memcpy(msg->buf, buf, msg->len); +} + +static int +mv64xxx_i2c_intr_offload(struct mv64xxx_i2c_data *drv_data) +{ + u32 cause, status; + + cause = readl(drv_data->reg_base + + MV64XXX_I2C_REG_BRIDGE_INTR_CAUSE); + if (!cause) + return IRQ_NONE; + + status = readl(drv_data->reg_base + + MV64XXX_I2C_REG_BRIDGE_STATUS); + + if (status & MV64XXX_I2C_BRIDGE_STATUS_ERROR) { + drv_data->rc = -EIO; + goto out; + } + + drv_data->rc = 0; + + /* + * Transaction is a one message read transaction, read data + * for this message. + */ + if (drv_data->num_msgs == 1 && drv_data->msgs[0].flags & I2C_M_RD) { + mv64xxx_i2c_read_offload_rx_data(drv_data, drv_data->msgs); + drv_data->msgs++; + drv_data->num_msgs--; + } + /* + * Transaction is a two messages write/read transaction, read + * data for the second (read) message. + */ + else if (drv_data->num_msgs == 2 && + !(drv_data->msgs[0].flags & I2C_M_RD) && + drv_data->msgs[1].flags & I2C_M_RD) { + mv64xxx_i2c_read_offload_rx_data(drv_data, drv_data->msgs + 1); + drv_data->msgs += 2; + drv_data->num_msgs -= 2; } + +out: + writel(0, drv_data->reg_base + MV64XXX_I2C_REG_BRIDGE_CONTROL); + writel(0, drv_data->reg_base + + MV64XXX_I2C_REG_BRIDGE_INTR_CAUSE); + drv_data->block = 0; + + wake_up(&drv_data->waitq); + + return IRQ_HANDLED; } static irqreturn_t @@ -540,20 +502,9 @@ mv64xxx_i2c_intr(int irq, void *dev_id) spin_lock_irqsave(&drv_data->lock, flags); - if (drv_data->offload_enabled) { - while (readl(drv_data->reg_base + - MV64XXX_I2C_REG_BRIDGE_INTR_CAUSE)) { - int reg_status = readl(drv_data->reg_base + - MV64XXX_I2C_REG_BRIDGE_STATUS); - if (reg_status & MV64XXX_I2C_BRIDGE_STATUS_ERROR) - status = MV64XXX_I2C_STATUS_OFFLOAD_ERROR; - else - status = MV64XXX_I2C_STATUS_OFFLOAD_OK; - mv64xxx_i2c_fsm(drv_data, status); - mv64xxx_i2c_do_action(drv_data); - rc = IRQ_HANDLED; - } - } + if (drv_data->offload_enabled) + rc = mv64xxx_i2c_intr_offload(drv_data); + while (readl(drv_data->reg_base + drv_data->reg_offsets.control) & MV64XXX_I2C_REG_CONTROL_IFLG) { status = readl(drv_data->reg_base + drv_data->reg_offsets.status); @@ -635,6 +586,117 @@ mv64xxx_i2c_execute_msg(struct mv64xxx_i2c_data *drv_data, struct i2c_msg *msg, return drv_data->rc; } +static void +mv64xxx_i2c_prepare_tx(struct mv64xxx_i2c_data *drv_data) +{ + struct i2c_msg *msg = drv_data->msgs; + u32 buf[2]; + + memcpy(buf, msg->buf, msg->len); + + writel(buf[0], drv_data->reg_base + MV64XXX_I2C_REG_TX_DATA_LO); + writel(buf[1], drv_data->reg_base + MV64XXX_I2C_REG_TX_DATA_HI); +} + +static int +mv64xxx_i2c_offload_xfer(struct mv64xxx_i2c_data *drv_data) +{ + struct i2c_msg *msgs = drv_data->msgs; + int num = drv_data->num_msgs; + unsigned long ctrl_reg; + unsigned long flags; + + spin_lock_irqsave(&drv_data->lock, flags); + + /* Build transaction */ + ctrl_reg = MV64XXX_I2C_BRIDGE_CONTROL_ENABLE | + (msgs[0].addr << MV64XXX_I2C_BRIDGE_CONTROL_ADDR_SHIFT); + + if (msgs[0].flags & I2C_M_TEN) + ctrl_reg |= MV64XXX_I2C_BRIDGE_CONTROL_ADDR_EXT; + + /* Single write message transaction */ + if (num == 1 && !(msgs[0].flags & I2C_M_RD)) { + size_t len = msgs[0].len - 1; + + ctrl_reg |= MV64XXX_I2C_BRIDGE_CONTROL_WR | + (len << MV64XXX_I2C_BRIDGE_CONTROL_TX_SIZE_SHIFT); + mv64xxx_i2c_prepare_tx(drv_data); + } + /* Single read message transaction */ + else if (num == 1 && msgs[0].flags & I2C_M_RD) { + size_t len = msgs[0].len - 1; + + ctrl_reg |= MV64XXX_I2C_BRIDGE_CONTROL_RD | + (len << MV64XXX_I2C_BRIDGE_CONTROL_RX_SIZE_SHIFT); + } + /* + * Transaction with one write and one read message. This is + * guaranteed by the mv64xx_i2c_can_offload() checks. + */ + else if (num == 2) { + size_t lentx = msgs[0].len - 1; + size_t lenrx = msgs[1].len - 1; + + ctrl_reg |= + MV64XXX_I2C_BRIDGE_CONTROL_RD | + MV64XXX_I2C_BRIDGE_CONTROL_WR | + (lentx << MV64XXX_I2C_BRIDGE_CONTROL_TX_SIZE_SHIFT) | + (lenrx << MV64XXX_I2C_BRIDGE_CONTROL_RX_SIZE_SHIFT) | + MV64XXX_I2C_BRIDGE_CONTROL_REPEATED_START; + mv64xxx_i2c_prepare_tx(drv_data); + } + + /* Execute transaction */ + drv_data->block = 1; + writel(ctrl_reg, drv_data->reg_base + MV64XXX_I2C_REG_BRIDGE_CONTROL); + spin_unlock_irqrestore(&drv_data->lock, flags); + + mv64xxx_i2c_wait_for_completion(drv_data); + + return drv_data->rc; +} + +static bool +mv64xxx_i2c_valid_offload_sz(struct i2c_msg *msg) +{ + return msg->len <= 8 && msg->len >= 1; +} + +static bool +mv64xxx_i2c_can_offload(struct mv64xxx_i2c_data *drv_data) +{ + struct i2c_msg *msgs = drv_data->msgs; + int num = drv_data->num_msgs; + + return false; + + if (!drv_data->offload_enabled) + return false; + + /* + * We can offload a transaction consisting of a single + * message, as long as the message has a length between 1 and + * 8 bytes. + */ + if (num == 1 && mv64xxx_i2c_valid_offload_sz(msgs)) + return true; + + /* + * We can offload a transaction consisting of two messages, if + * the first is a write and a second is a read, and both have + * a length between 1 and 8 bytes. + */ + if (num == 2 && + mv64xxx_i2c_valid_offload_sz(msgs) && + mv64xxx_i2c_valid_offload_sz(msgs + 1) && + !(msgs[0].flags & I2C_M_RD) && + msgs[1].flags & I2C_M_RD) + return true; + + return false; +} + /* ***************************************************************************** * @@ -658,7 +720,11 @@ mv64xxx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) drv_data->msgs = msgs; drv_data->num_msgs = num; - rc = mv64xxx_i2c_execute_msg(drv_data, &msgs[0], num == 1); + if (mv64xxx_i2c_can_offload(drv_data)) + rc = mv64xxx_i2c_offload_xfer(drv_data); + else + rc = mv64xxx_i2c_execute_msg(drv_data, &msgs[0], num == 1); + if (rc < 0) ret = rc; diff --git a/drivers/i2c/busses/i2c-opal.c b/drivers/i2c/busses/i2c-opal.c new file mode 100644 index 000000000000..16f90b1a7508 --- /dev/null +++ b/drivers/i2c/busses/i2c-opal.c @@ -0,0 +1,294 @@ +/* + * IBM OPAL I2C driver + * Copyright (C) 2014 IBM + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + */ + +#include <linux/device.h> +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include <asm/firmware.h> +#include <asm/opal.h> + +static int i2c_opal_translate_error(int rc) +{ + switch (rc) { + case OPAL_NO_MEM: + return -ENOMEM; + case OPAL_PARAMETER: + return -EINVAL; + case OPAL_I2C_ARBT_LOST: + return -EAGAIN; + case OPAL_I2C_TIMEOUT: + return -ETIMEDOUT; + case OPAL_I2C_NACK_RCVD: + return -ENXIO; + case OPAL_I2C_STOP_ERR: + return -EBUSY; + default: + return -EIO; + } +} + +static int i2c_opal_send_request(u32 bus_id, struct opal_i2c_request *req) +{ + struct opal_msg msg; + int token, rc; + + token = opal_async_get_token_interruptible(); + if (token < 0) { + if (token != -ERESTARTSYS) + pr_err("Failed to get the async token\n"); + + return token; + } + + rc = opal_i2c_request(token, bus_id, req); + if (rc != OPAL_ASYNC_COMPLETION) { + rc = i2c_opal_translate_error(rc); + goto exit; + } + + rc = opal_async_wait_response(token, &msg); + if (rc) + goto exit; + + rc = be64_to_cpu(msg.params[1]); + if (rc != OPAL_SUCCESS) { + rc = i2c_opal_translate_error(rc); + goto exit; + } + +exit: + opal_async_release_token(token); + return rc; +} + +static int i2c_opal_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, + int num) +{ + unsigned long opal_id = (unsigned long)adap->algo_data; + struct opal_i2c_request req; + int rc, i; + + /* We only support fairly simple combinations here of one + * or two messages + */ + memset(&req, 0, sizeof(req)); + switch(num) { + case 0: + return 0; + case 1: + req.type = (msgs[0].flags & I2C_M_RD) ? + OPAL_I2C_RAW_READ : OPAL_I2C_RAW_WRITE; + req.addr = cpu_to_be16(msgs[0].addr); + req.size = cpu_to_be32(msgs[0].len); + req.buffer_ra = cpu_to_be64(__pa(msgs[0].buf)); + break; + case 2: + /* For two messages, we basically support only simple + * smbus transactions of a write plus a read. We might + * want to allow also two writes but we'd have to bounce + * the data into a single buffer. + */ + if ((msgs[0].flags & I2C_M_RD) || !(msgs[1].flags & I2C_M_RD)) + return -EOPNOTSUPP; + if (msgs[0].len > 4) + return -EOPNOTSUPP; + if (msgs[0].addr != msgs[1].addr) + return -EOPNOTSUPP; + req.type = OPAL_I2C_SM_READ; + req.addr = cpu_to_be16(msgs[0].addr); + req.subaddr_sz = msgs[0].len; + for (i = 0; i < msgs[0].len; i++) + req.subaddr = (req.subaddr << 8) | msgs[0].buf[i]; + req.subaddr = cpu_to_be32(req.subaddr); + req.size = cpu_to_be32(msgs[1].len); + req.buffer_ra = cpu_to_be64(__pa(msgs[1].buf)); + break; + default: + return -EOPNOTSUPP; + } + + rc = i2c_opal_send_request(opal_id, &req); + if (rc) + return rc; + + return num; +} + +static int i2c_opal_smbus_xfer(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data) +{ + unsigned long opal_id = (unsigned long)adap->algo_data; + struct opal_i2c_request req; + u8 local[2]; + int rc; + + memset(&req, 0, sizeof(req)); + + req.addr = cpu_to_be16(addr); + switch (size) { + case I2C_SMBUS_BYTE: + req.buffer_ra = cpu_to_be64(__pa(&data->byte)); + req.size = cpu_to_be32(1); + /* Fall through */ + case I2C_SMBUS_QUICK: + req.type = (read_write == I2C_SMBUS_READ) ? + OPAL_I2C_RAW_READ : OPAL_I2C_RAW_WRITE; + break; + case I2C_SMBUS_BYTE_DATA: + req.buffer_ra = cpu_to_be64(__pa(&data->byte)); + req.size = cpu_to_be32(1); + req.subaddr = cpu_to_be32(command); + req.subaddr_sz = 1; + req.type = (read_write == I2C_SMBUS_READ) ? + OPAL_I2C_SM_READ : OPAL_I2C_SM_WRITE; + break; + case I2C_SMBUS_WORD_DATA: + if (!read_write) { + local[0] = data->word & 0xff; + local[1] = (data->word >> 8) & 0xff; + } + req.buffer_ra = cpu_to_be64(__pa(local)); + req.size = cpu_to_be32(2); + req.subaddr = cpu_to_be32(command); + req.subaddr_sz = 1; + req.type = (read_write == I2C_SMBUS_READ) ? + OPAL_I2C_SM_READ : OPAL_I2C_SM_WRITE; + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + req.buffer_ra = cpu_to_be64(__pa(&data->block[1])); + req.size = cpu_to_be32(data->block[0]); + req.subaddr = cpu_to_be32(command); + req.subaddr_sz = 1; + req.type = (read_write == I2C_SMBUS_READ) ? + OPAL_I2C_SM_READ : OPAL_I2C_SM_WRITE; + break; + default: + return -EINVAL; + } + + rc = i2c_opal_send_request(opal_id, &req); + if (!rc && read_write && size == I2C_SMBUS_WORD_DATA) { + data->word = ((u16)local[1]) << 8; + data->word |= local[0]; + } + + return rc; +} + +static u32 i2c_opal_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK; +} + +static const struct i2c_algorithm i2c_opal_algo = { + .master_xfer = i2c_opal_master_xfer, + .smbus_xfer = i2c_opal_smbus_xfer, + .functionality = i2c_opal_func, +}; + +static int i2c_opal_probe(struct platform_device *pdev) +{ + struct i2c_adapter *adapter; + const char *pname; + u32 opal_id; + int rc; + + if (!pdev->dev.of_node) + return -ENODEV; + + rc = of_property_read_u32(pdev->dev.of_node, "ibm,opal-id", &opal_id); + if (rc) { + dev_err(&pdev->dev, "Missing ibm,opal-id property !\n"); + return -EIO; + } + + adapter = devm_kzalloc(&pdev->dev, sizeof(*adapter), GFP_KERNEL); + if (!adapter) + return -ENOMEM; + + adapter->algo = &i2c_opal_algo; + adapter->algo_data = (void *)(unsigned long)opal_id; + adapter->dev.parent = &pdev->dev; + adapter->dev.of_node = of_node_get(pdev->dev.of_node); + pname = of_get_property(pdev->dev.of_node, "ibm,port-name", NULL); + if (pname) + strlcpy(adapter->name, pname, sizeof(adapter->name)); + else + strlcpy(adapter->name, "opal", sizeof(adapter->name)); + + platform_set_drvdata(pdev, adapter); + rc = i2c_add_adapter(adapter); + if (rc) + dev_err(&pdev->dev, "Failed to register the i2c adapter\n"); + + return rc; +} + +static int i2c_opal_remove(struct platform_device *pdev) +{ + struct i2c_adapter *adapter = platform_get_drvdata(pdev); + + i2c_del_adapter(adapter); + + return 0; +} + +static const struct of_device_id i2c_opal_of_match[] = { + { + .compatible = "ibm,opal-i2c", + }, + { } +}; +MODULE_DEVICE_TABLE(of, i2c_opal_of_match); + +static struct platform_driver i2c_opal_driver = { + .probe = i2c_opal_probe, + .remove = i2c_opal_remove, + .driver = { + .name = "i2c-opal", + .of_match_table = i2c_opal_of_match, + }, +}; + +static int __init i2c_opal_init(void) +{ + if (!firmware_has_feature(FW_FEATURE_OPAL)) + return -ENODEV; + + return platform_driver_register(&i2c_opal_driver); +} +module_init(i2c_opal_init); + +static void __exit i2c_opal_exit(void) +{ + return platform_driver_unregister(&i2c_opal_driver); +} +module_exit(i2c_opal_exit); + +MODULE_AUTHOR("Neelesh Gupta <neelegup@linux.vnet.ibm.com>"); +MODULE_DESCRIPTION("IBM OPAL I2C driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index d7efaf44868b..440d5dbc8b5f 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -140,6 +140,7 @@ struct sh_mobile_i2c_data { int sr; bool send_stop; + struct resource *res; struct dma_chan *dma_tx; struct dma_chan *dma_rx; struct scatterlist sg; @@ -539,6 +540,42 @@ static void sh_mobile_i2c_dma_callback(void *data) iic_set_clr(pd, ICIC, 0, ICIC_TDMAE | ICIC_RDMAE); } +static struct dma_chan *sh_mobile_i2c_request_dma_chan(struct device *dev, + enum dma_transfer_direction dir, dma_addr_t port_addr) +{ + struct dma_chan *chan; + struct dma_slave_config cfg; + char *chan_name = dir == DMA_MEM_TO_DEV ? "tx" : "rx"; + int ret; + + chan = dma_request_slave_channel_reason(dev, chan_name); + if (IS_ERR(chan)) { + ret = PTR_ERR(chan); + dev_dbg(dev, "request_channel failed for %s (%d)\n", chan_name, ret); + return chan; + } + + memset(&cfg, 0, sizeof(cfg)); + cfg.direction = dir; + if (dir == DMA_MEM_TO_DEV) { + cfg.dst_addr = port_addr; + cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + } else { + cfg.src_addr = port_addr; + cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + } + + ret = dmaengine_slave_config(chan, &cfg); + if (ret) { + dev_dbg(dev, "slave_config failed for %s (%d)\n", chan_name, ret); + dma_release_channel(chan); + return ERR_PTR(ret); + } + + dev_dbg(dev, "got DMA channel for %s\n", chan_name); + return chan; +} + static void sh_mobile_i2c_xfer_dma(struct sh_mobile_i2c_data *pd) { bool read = pd->msg->flags & I2C_M_RD; @@ -548,7 +585,16 @@ static void sh_mobile_i2c_xfer_dma(struct sh_mobile_i2c_data *pd) dma_addr_t dma_addr; dma_cookie_t cookie; - if (!chan) + if (PTR_ERR(chan) == -EPROBE_DEFER) { + if (read) + chan = pd->dma_rx = sh_mobile_i2c_request_dma_chan(pd->dev, DMA_DEV_TO_MEM, + pd->res->start + ICDR); + else + chan = pd->dma_tx = sh_mobile_i2c_request_dma_chan(pd->dev, DMA_MEM_TO_DEV, + pd->res->start + ICDR); + } + + if (IS_ERR(chan)) return; dma_addr = dma_map_single(chan->device->dev, pd->msg->buf, pd->msg->len, dir); @@ -747,56 +793,16 @@ static const struct of_device_id sh_mobile_i2c_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, sh_mobile_i2c_dt_ids); -static int sh_mobile_i2c_request_dma_chan(struct device *dev, enum dma_transfer_direction dir, - dma_addr_t port_addr, struct dma_chan **chan_ptr) -{ - struct dma_chan *chan; - struct dma_slave_config cfg; - char *chan_name = dir == DMA_MEM_TO_DEV ? "tx" : "rx"; - int ret; - - *chan_ptr = NULL; - - chan = dma_request_slave_channel_reason(dev, chan_name); - if (IS_ERR(chan)) { - ret = PTR_ERR(chan); - dev_dbg(dev, "request_channel failed for %s (%d)\n", chan_name, ret); - return ret; - } - - memset(&cfg, 0, sizeof(cfg)); - cfg.direction = dir; - if (dir == DMA_MEM_TO_DEV) { - cfg.dst_addr = port_addr; - cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; - } else { - cfg.src_addr = port_addr; - cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; - } - - ret = dmaengine_slave_config(chan, &cfg); - if (ret) { - dev_dbg(dev, "slave_config failed for %s (%d)\n", chan_name, ret); - dma_release_channel(chan); - return ret; - } - - *chan_ptr = chan; - - dev_dbg(dev, "got DMA channel for %s\n", chan_name); - return 0; -} - static void sh_mobile_i2c_release_dma(struct sh_mobile_i2c_data *pd) { - if (pd->dma_tx) { + if (!IS_ERR(pd->dma_tx)) { dma_release_channel(pd->dma_tx); - pd->dma_tx = NULL; + pd->dma_tx = ERR_PTR(-EPROBE_DEFER); } - if (pd->dma_rx) { + if (!IS_ERR(pd->dma_rx)) { dma_release_channel(pd->dma_rx); - pd->dma_rx = NULL; + pd->dma_rx = ERR_PTR(-EPROBE_DEFER); } } @@ -849,6 +855,7 @@ static int sh_mobile_i2c_probe(struct platform_device *dev) res = platform_get_resource(dev, IORESOURCE_MEM, 0); + pd->res = res; pd->reg = devm_ioremap_resource(&dev->dev, res); if (IS_ERR(pd->reg)) return PTR_ERR(pd->reg); @@ -889,17 +896,7 @@ static int sh_mobile_i2c_probe(struct platform_device *dev) /* Init DMA */ sg_init_table(&pd->sg, 1); pd->dma_direction = DMA_NONE; - ret = sh_mobile_i2c_request_dma_chan(pd->dev, DMA_DEV_TO_MEM, - res->start + ICDR, &pd->dma_rx); - if (ret == -EPROBE_DEFER) - return ret; - - ret = sh_mobile_i2c_request_dma_chan(pd->dev, DMA_MEM_TO_DEV, - res->start + ICDR, &pd->dma_tx); - if (ret == -EPROBE_DEFER) { - sh_mobile_i2c_release_dma(pd); - return ret; - } + pd->dma_rx = pd->dma_tx = ERR_PTR(-EPROBE_DEFER); /* Enable Runtime PM for this device. * @@ -937,8 +934,7 @@ static int sh_mobile_i2c_probe(struct platform_device *dev) return ret; } - dev_info(&dev->dev, "I2C adapter %d, bus speed %lu Hz, DMA=%c\n", - adap->nr, pd->bus_speed, (pd->dma_rx || pd->dma_tx) ? 'y' : 'n'); + dev_info(&dev->dev, "I2C adapter %d, bus speed %lu Hz\n", adap->nr, pd->bus_speed); return 0; } |