| /* Copyright 2013-2014 IBM Corp. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
| * implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #undef DEBUG |
| |
| #include <opal.h> |
| #include <skiboot.h> |
| #include <mem_region-malloc.h> |
| #include <lock.h> |
| #include <chip.h> |
| #include <i2c.h> |
| #include <xscom.h> |
| #include <timebase.h> |
| #include <timer.h> |
| #include <opal-msg.h> |
| #include <errorlog.h> |
| #include <centaur.h> |
| |
| DEFINE_LOG_ENTRY(OPAL_RC_I2C_INIT, OPAL_PLATFORM_ERR_EVT, OPAL_I2C, |
| OPAL_IO_SUBSYSTEM, OPAL_PREDICTIVE_ERR_DEGRADED_PERF, |
| OPAL_NA); |
| DEFINE_LOG_ENTRY(OPAL_RC_I2C_START_REQ, OPAL_INPUT_OUTPUT_ERR_EVT, OPAL_I2C, |
| OPAL_IO_SUBSYSTEM, OPAL_INFO, OPAL_NA); |
| DEFINE_LOG_ENTRY(OPAL_RC_I2C_TIMEOUT, OPAL_INPUT_OUTPUT_ERR_EVT, OPAL_I2C, |
| OPAL_IO_SUBSYSTEM, OPAL_INFO, OPAL_NA); |
| DEFINE_LOG_ENTRY(OPAL_RC_I2C_TRANSFER, OPAL_INPUT_OUTPUT_ERR_EVT, OPAL_I2C, |
| OPAL_IO_SUBSYSTEM, OPAL_INFO, OPAL_NA); |
| DEFINE_LOG_ENTRY(OPAL_RC_I2C_RESET, OPAL_INPUT_OUTPUT_ERR_EVT, OPAL_I2C, |
| OPAL_IO_SUBSYSTEM, OPAL_INFO, OPAL_NA); |
| |
| #ifdef DEBUG |
| #define DBG(fmt...) prlog(PR_ERR, "I2C: " fmt) |
| #define I2C_TIMEOUT_IRQ_MS 100 /* 100ms/byte timeout */ |
| #define I2C_TIMEOUT_POLL_MS 4000 /* 4s/byte timeout */ |
| #else |
| #define DBG(fmt...) prlog(PR_TRACE, "I2C: " fmt) |
| #define I2C_TIMEOUT_IRQ_MS 1 /* 1ms/byte timeout */ |
| #define I2C_TIMEOUT_POLL_MS 4000 /* 4s/byte timeout */ |
| #endif |
| |
| /* How long to keep the sensor cache disabled after an access |
| * in milliseconds |
| */ |
| #define SENSOR_CACHE_EN_DELAY 10 |
| |
| #define USEC_PER_SEC 1000000 |
| #define USEC_PER_MSEC 1000 |
| #define I2C_RESET_DELAY_MS 5 /* 5 msecs */ |
| #define I2C_FIFO_HI_LVL 4 |
| #define I2C_FIFO_LO_LVL 4 |
| |
| /* |
| * I2C registers set. |
| * Below is the offset of registers from base which is stored in the |
| * 'struct p8_i2c_master' |
| */ |
| |
| /* I2C FIFO register */ |
| #define I2C_FIFO_REG 0x4 |
| #define I2C_FIFO PPC_BITMASK(0, 7) |
| |
| /* I2C command register */ |
| #define I2C_CMD_REG 0x5 |
| #define I2C_CMD_WITH_START PPC_BIT(0) |
| #define I2C_CMD_WITH_ADDR PPC_BIT(1) |
| #define I2C_CMD_READ_CONT PPC_BIT(2) |
| #define I2C_CMD_WITH_STOP PPC_BIT(3) |
| #define I2C_CMD_INTR_STEERING PPC_BITMASK(6,7) /* P9 */ |
| #define I2C_CMD_INTR_STEER_HOST 1 |
| #define I2C_CMD_INTR_STEER_OCC 2 |
| #define I2C_CMD_DEV_ADDR PPC_BITMASK(8, 14) |
| #define I2C_CMD_READ_NOT_WRITE PPC_BIT(15) |
| #define I2C_CMD_LEN_BYTES PPC_BITMASK(16, 31) |
| #define I2C_MAX_TFR_LEN 0xfff0ull |
| |
| /* I2C mode register */ |
| #define I2C_MODE_REG 0x6 |
| #define I2C_MODE_BIT_RATE_DIV PPC_BITMASK(0, 15) |
| #define I2C_MODE_PORT_NUM PPC_BITMASK(16, 21) |
| #define I2C_MODE_ENHANCED PPC_BIT(28) |
| #define I2C_MODE_DIAGNOSTIC PPC_BIT(29) |
| #define I2C_MODE_PACING_ALLOW PPC_BIT(30) |
| #define I2C_MODE_WRAP PPC_BIT(31) |
| |
| /* I2C watermark register */ |
| #define I2C_WATERMARK_REG 0x7 |
| #define I2C_WATERMARK_HIGH PPC_BITMASK(16, 19) |
| #define I2C_WATERMARK_LOW PPC_BITMASK(24, 27) |
| |
| /* I2C interrupt mask, condition and interrupt registers */ |
| #define I2C_INTR_MASK_REG 0x8 |
| #define I2C_INTR_COND_REG 0x9 |
| #define I2C_INTR_REG 0xa |
| #define I2C_INTR_ALL PPC_BITMASK(16, 31) |
| #define I2C_INTR_INVALID_CMD PPC_BIT(16) |
| #define I2C_INTR_LBUS_PARITY_ERR PPC_BIT(17) |
| #define I2C_INTR_BKEND_OVERRUN_ERR PPC_BIT(18) |
| #define I2C_INTR_BKEND_ACCESS_ERR PPC_BIT(19) |
| #define I2C_INTR_ARBT_LOST_ERR PPC_BIT(20) |
| #define I2C_INTR_NACK_RCVD_ERR PPC_BIT(21) |
| #define I2C_INTR_DATA_REQ PPC_BIT(22) |
| #define I2C_INTR_CMD_COMP PPC_BIT(23) |
| #define I2C_INTR_STOP_ERR PPC_BIT(24) |
| #define I2C_INTR_I2C_BUSY PPC_BIT(25) |
| #define I2C_INTR_NOT_I2C_BUSY PPC_BIT(26) |
| #define I2C_INTR_SCL_EQ_1 PPC_BIT(28) |
| #define I2C_INTR_SCL_EQ_0 PPC_BIT(29) |
| #define I2C_INTR_SDA_EQ_1 PPC_BIT(30) |
| #define I2C_INTR_SDA_EQ_0 PPC_BIT(31) |
| |
| /* I2C status register */ |
| #define I2C_RESET_I2C_REG 0xb |
| #define I2C_RESET_ERRORS 0xc |
| #define I2C_STAT_REG 0xb |
| #define I2C_STAT_INVALID_CMD PPC_BIT(0) |
| #define I2C_STAT_LBUS_PARITY_ERR PPC_BIT(1) |
| #define I2C_STAT_BKEND_OVERRUN_ERR PPC_BIT(2) |
| #define I2C_STAT_BKEND_ACCESS_ERR PPC_BIT(3) |
| #define I2C_STAT_ARBT_LOST_ERR PPC_BIT(4) |
| #define I2C_STAT_NACK_RCVD_ERR PPC_BIT(5) |
| #define I2C_STAT_DATA_REQ PPC_BIT(6) |
| #define I2C_STAT_CMD_COMP PPC_BIT(7) |
| #define I2C_STAT_STOP_ERR PPC_BIT(8) |
| #define I2C_STAT_UPPER_THRS PPC_BITMASK(9, 15) |
| #define I2C_STAT_ANY_I2C_INTR PPC_BIT(16) |
| #define I2C_STAT_PORT_HISTORY_BUSY PPC_BIT(19) |
| #define I2C_STAT_SCL_INPUT_LEVEL PPC_BIT(20) |
| #define I2C_STAT_SDA_INPUT_LEVEL PPC_BIT(21) |
| #define I2C_STAT_PORT_BUSY PPC_BIT(22) |
| #define I2C_STAT_INTERFACE_BUSY PPC_BIT(23) |
| #define I2C_STAT_FIFO_ENTRY_COUNT PPC_BITMASK(24, 31) |
| |
| #define I2C_STAT_ANY_ERR (I2C_STAT_INVALID_CMD | I2C_STAT_LBUS_PARITY_ERR | \ |
| I2C_STAT_BKEND_OVERRUN_ERR | \ |
| I2C_STAT_BKEND_ACCESS_ERR | I2C_STAT_ARBT_LOST_ERR | \ |
| I2C_STAT_NACK_RCVD_ERR | I2C_STAT_STOP_ERR) |
| |
| /* Pseudo-status used for timeouts */ |
| #define I2C_STAT_PSEUDO_TIMEOUT PPC_BIT(63) |
| |
| |
| /* I2C extended status register */ |
| #define I2C_EXTD_STAT_REG 0xc |
| #define I2C_EXTD_STAT_FIFO_SIZE PPC_BITMASK(0, 7) |
| #define I2C_EXTD_STAT_MSM_CURSTATE PPC_BITMASK(11, 15) |
| #define I2C_EXTD_STAT_SCL_IN_SYNC PPC_BIT(16) |
| #define I2C_EXTD_STAT_SDA_IN_SYNC PPC_BIT(17) |
| #define I2C_EXTD_STAT_S_SCL PPC_BIT(18) |
| #define I2C_EXTD_STAT_S_SDA PPC_BIT(19) |
| #define I2C_EXTD_STAT_M_SCL PPC_BIT(20) |
| #define I2C_EXTD_STAT_M_SDA PPC_BIT(21) |
| #define I2C_EXTD_STAT_HIGH_WATER PPC_BIT(22) |
| #define I2C_EXTD_STAT_LOW_WATER PPC_BIT(23) |
| #define I2C_EXTD_STAT_I2C_BUSY PPC_BIT(24) |
| #define I2C_EXTD_STAT_SELF_BUSY PPC_BIT(25) |
| #define I2C_EXTD_STAT_I2C_VERSION PPC_BITMASK(27, 31) |
| |
| /* I2C residual front end/back end length */ |
| #define I2C_RESIDUAL_LEN_REG 0xd |
| #define I2C_RESIDUAL_FRONT_END PPC_BITMASK(0, 15) |
| #define I2C_RESIDUAL_BACK_END PPC_BITMASK(16, 31) |
| |
| /* Port busy register */ |
| #define I2C_PORT_BUSY_REG 0xe |
| #define I2C_SET_S_SCL_REG 0xd |
| #define I2C_RESET_S_SCL_REG 0xf |
| #define I2C_SET_S_SDA_REG 0x10 |
| #define I2C_RESET_S_SDA_REG 0x11 |
| |
| enum p8_i2c_master_type { |
| I2C_POWER8, |
| I2C_CENTAUR, |
| MAX_I2C_TYPE, |
| }; |
| |
| struct p8_i2c_master { |
| struct lock lock; /* Lock to guard the members */ |
| enum p8_i2c_master_type type; /* P8 vs. Centaur */ |
| uint64_t start_time; /* Request start time */ |
| uint64_t poll_interval; /* Polling interval */ |
| uint64_t byte_timeout; /* Timeout per byte */ |
| uint64_t xscom_base; /* xscom base of i2cm */ |
| uint32_t fifo_size; /* Maximum size of FIFO */ |
| uint32_t chip_id; /* Chip the i2cm sits on */ |
| uint32_t engine_id; /* Engine# on chip */ |
| uint8_t obuf[4]; /* Offset buffer */ |
| uint32_t bytes_sent; |
| bool irq_ok; /* Interrupt working ? */ |
| bool occ_cache_dis; /* I have disabled the cache */ |
| enum request_state { |
| state_idle, |
| state_occache_dis, |
| state_offset, |
| state_data, |
| state_error, |
| state_recovery, |
| } state; |
| struct list_head req_list; /* Request queue head */ |
| struct timer poller; |
| struct timer timeout; |
| struct timer recovery; |
| struct timer sensor_cache; |
| uint8_t recovery_pass; |
| struct list_node link; |
| struct list_head ports; |
| }; |
| |
| struct p8_i2c_master_port { |
| struct i2c_bus bus; /* Abstract bus struct for the client */ |
| struct p8_i2c_master *master; |
| uint32_t port_num; |
| uint32_t bit_rate_div; /* Divisor to set bus speed*/ |
| struct list_node link; |
| }; |
| |
| struct p8_i2c_request { |
| struct i2c_request req; |
| uint32_t port_num; |
| uint64_t timeout; |
| }; |
| |
| static void p8_i2c_print_debug_info(struct p8_i2c_master_port *port, |
| struct i2c_request *req, uint64_t end_time) |
| { |
| struct p8_i2c_master *master = port->master; |
| uint64_t cmd, mode, stat, estat, intm, intc; |
| int rc; |
| |
| /* Print master and request structure bits */ |
| log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER), |
| "I2C: Chip %08x Eng. %d Port %d--\n" |
| " xscom_base=0x%016llx\tstate=%d\tbytes_sent=%d\n", |
| master->chip_id, master->engine_id, port->port_num, |
| master->xscom_base, master->state, master->bytes_sent); |
| |
| log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER), "I2C: Request info--\n" |
| " addr=0x%04x\toffset_bytes=%d\toffset=%d\tlen=%d\n", |
| req->dev_addr, req->offset_bytes, req->offset, |
| req->rw_len); |
| |
| log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER), "I2C: " |
| " start_time=%016llx end_time=%016llx (duration=%016llx)\n", |
| master->start_time, end_time, end_time - master->start_time); |
| |
| /* Dump the current state of i2c registers */ |
| rc = xscom_read(master->chip_id, master->xscom_base + I2C_CMD_REG, |
| &cmd); |
| if (rc) { |
| prlog(PR_DEBUG, "I2C: Failed to read CMD_REG\n"); |
| cmd = 0ull; |
| } |
| |
| rc = xscom_read(master->chip_id, master->xscom_base + I2C_MODE_REG, |
| &mode); |
| if (rc) { |
| prlog(PR_DEBUG, "I2C: Failed to read MODE_REG\n"); |
| mode = 0ull; |
| } |
| |
| rc = xscom_read(master->chip_id, master->xscom_base + I2C_STAT_REG, |
| &stat); |
| if (rc) { |
| prlog(PR_DEBUG, "I2C: Failed to read STAT_REG\n"); |
| stat = 0ull; |
| } |
| |
| rc = xscom_read(master->chip_id, master->xscom_base + I2C_EXTD_STAT_REG, |
| &estat); |
| if (rc) { |
| prlog(PR_DEBUG, "I2C: Failed to read EXTD_STAT_REG\n"); |
| estat = 0ull; |
| } |
| |
| rc = xscom_read(master->chip_id, master->xscom_base + I2C_INTR_MASK_REG, |
| &intm); |
| if (rc) { |
| prlog(PR_DEBUG, "I2C: Failed to read INTR_MASK_REG\n"); |
| intm = 0ull; |
| } |
| |
| rc = xscom_read(master->chip_id, master->xscom_base + I2C_INTR_COND_REG, |
| &intc); |
| if (rc) { |
| prlog(PR_DEBUG, "I2C: Failed to read INTR_COND_REG\n"); |
| intc = 0ull; |
| } |
| |
| log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER), "I2C: Register dump--\n" |
| " cmd:0x%016llx\tmode:0x%016llx\tstat:0x%016llx\n" |
| " estat:0x%016llx\tintm:0x%016llx\tintc:0x%016llx\n", |
| cmd, mode, stat, estat, intm, intc); |
| } |
| |
| static bool p8_i2c_has_irqs(struct p8_i2c_master *master) |
| { |
| struct proc_chip *chip; |
| |
| /* Centaur I2C doesn't have interrupts */ |
| if (master->type == I2C_CENTAUR) |
| return false; |
| |
| chip = get_chip(master->chip_id); |
| |
| /* The i2c interrupts was only added to Murano DD2.1 and Venice |
| * DD2.0. When operating without interrupts, we need to bump the |
| * timeouts as we rely solely on the polls from Linux which can |
| * be up to 2s apart ! |
| * |
| * Also we don't have interrupts for the Centaur i2c. |
| */ |
| switch (chip->type) { |
| case PROC_CHIP_P8_MURANO: |
| return chip->ec_level >= 0x21; |
| case PROC_CHIP_P8_VENICE: |
| return chip->ec_level >= 0x20; |
| case PROC_CHIP_P8_NAPLES: |
| case PROC_CHIP_P9_NIMBUS: |
| case PROC_CHIP_P9_CUMULUS: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| static int p8_i2c_enable_irqs(struct p8_i2c_master *master) |
| { |
| int rc; |
| |
| /* Enable the interrupts */ |
| rc = xscom_write(master->chip_id, master->xscom_base + |
| I2C_INTR_COND_REG, I2C_STAT_ANY_ERR >> 16 | |
| I2C_INTR_CMD_COMP | I2C_INTR_DATA_REQ); |
| if (rc) |
| prlog(PR_ERR, "I2C: Failed to enable the interrupts\n"); |
| |
| return rc; |
| } |
| |
| static int p8_i2c_prog_watermark(struct p8_i2c_master *master) |
| { |
| uint64_t watermark; |
| int rc; |
| |
| rc = xscom_read(master->chip_id, master->xscom_base + I2C_WATERMARK_REG, |
| &watermark); |
| if (rc) { |
| prlog(PR_ERR, "I2C: Failed to read the WATERMARK_REG\n"); |
| return rc; |
| } |
| |
| /* Set the high/low watermark */ |
| watermark = SETFIELD(I2C_WATERMARK_HIGH, watermark, I2C_FIFO_HI_LVL); |
| watermark = SETFIELD(I2C_WATERMARK_LOW, watermark, I2C_FIFO_LO_LVL); |
| rc = xscom_write(master->chip_id, master->xscom_base + |
| I2C_WATERMARK_REG, watermark); |
| if (rc) |
| prlog(PR_ERR, "I2C: Failed to set high/low watermark level\n"); |
| |
| return rc; |
| } |
| |
| static int p8_i2c_prog_mode(struct p8_i2c_master_port *port, bool enhanced_mode) |
| { |
| struct p8_i2c_master *master = port->master; |
| struct i2c_request *req = list_top(&master->req_list, |
| struct i2c_request, link); |
| struct p8_i2c_request *request = |
| container_of(req, struct p8_i2c_request, req); |
| uint64_t mode, omode; |
| int rc; |
| |
| rc = xscom_read(master->chip_id, master->xscom_base + |
| I2C_MODE_REG, &mode); |
| if (rc) { |
| prlog(PR_ERR, "I2C: Failed to read the MODE_REG\n"); |
| return rc; |
| } |
| omode = mode; |
| mode = SETFIELD(I2C_MODE_PORT_NUM, mode, request->port_num); |
| mode = SETFIELD(I2C_MODE_BIT_RATE_DIV, mode, port->bit_rate_div); |
| if (enhanced_mode) |
| mode |= I2C_MODE_ENHANCED; |
| else |
| mode &= ~I2C_MODE_ENHANCED; |
| if (mode == omode) |
| return 0; |
| |
| rc = xscom_write(master->chip_id, master->xscom_base + I2C_MODE_REG, |
| mode); |
| if (rc) |
| prlog(PR_ERR, "I2C: Failed to write the MODE_REG\n"); |
| |
| return rc; |
| } |
| |
| static void p8_i2c_complete_request(struct p8_i2c_master *master, |
| struct i2c_request *req, int ret) |
| { |
| struct p8_i2c_request *request = |
| container_of(req, struct p8_i2c_request, req); |
| |
| /* We only complete the current top level request */ |
| assert(req == list_top(&master->req_list, struct i2c_request, link)); |
| |
| cancel_timer_async(&master->timeout); |
| request->timeout = 0ul; |
| |
| list_del(&req->link); |
| master->state = state_idle; |
| req->result = ret; |
| |
| /* Schedule re-enabling of sensor cache */ |
| if (master->occ_cache_dis) |
| schedule_timer(&master->sensor_cache, |
| msecs_to_tb(SENSOR_CACHE_EN_DELAY)); |
| |
| unlock(&master->lock); |
| if (req->completion) |
| req->completion(ret, req); |
| /* req might have been freed at this point */ |
| lock(&master->lock); |
| } |
| |
| |
| static int p8_i2c_engine_reset(struct p8_i2c_master_port *port) |
| { |
| struct p8_i2c_master *master = port->master; |
| int rc; |
| |
| /* Reset the i2c engine */ |
| rc = xscom_write(master->chip_id, master->xscom_base + |
| I2C_RESET_I2C_REG, 0); |
| if (rc) { |
| log_simple_error(&e_info(OPAL_RC_I2C_RESET), "I2C: Failed " |
| "to reset the i2c engine\n"); |
| return rc; |
| } |
| |
| /* Reprogram the watermark and mode */ |
| rc = p8_i2c_prog_watermark(port->master); |
| if (rc) { |
| log_simple_error(&e_info(OPAL_RC_I2C_RESET), "I2C: Failed to" |
| "program the WATERMARK_REG\n"); |
| return rc; |
| } |
| |
| rc = p8_i2c_prog_mode(port, false); |
| if (rc) |
| log_simple_error(&e_info(OPAL_RC_I2C_RESET), "I2C: Failed to" |
| "program the MODE_REG\n"); |
| |
| return rc; |
| } |
| |
| static void p8_i2c_translate_error(struct i2c_request *req, uint64_t status) |
| { |
| /* Assuming there are not more than one type of error simultaneously */ |
| if (status & I2C_STAT_NACK_RCVD_ERR) |
| req->result = OPAL_I2C_NACK_RCVD; |
| else if (status & I2C_STAT_INVALID_CMD) |
| req->result = OPAL_I2C_INVALID_CMD; |
| else if (status & I2C_STAT_LBUS_PARITY_ERR) |
| req->result = OPAL_I2C_LBUS_PARITY; |
| else if (status & I2C_STAT_BKEND_OVERRUN_ERR) |
| req->result = OPAL_I2C_BKEND_OVERRUN; |
| else if (status & I2C_STAT_BKEND_ACCESS_ERR) |
| req->result = OPAL_I2C_BKEND_ACCESS; |
| else if (status & I2C_STAT_ARBT_LOST_ERR) |
| req->result = OPAL_I2C_ARBT_LOST; |
| else if (status & I2C_STAT_STOP_ERR) |
| req->result = OPAL_I2C_STOP_ERR; |
| else if (status & I2C_STAT_PSEUDO_TIMEOUT) |
| req->result = OPAL_I2C_TIMEOUT; |
| } |
| |
| static void p8_i2c_force_reset(struct p8_i2c_master *master) |
| { |
| struct p8_i2c_master_port *p; |
| uint64_t mode; |
| int rc; |
| |
| /* Reset the i2c engine */ |
| rc = xscom_write(master->chip_id, master->xscom_base + |
| I2C_RESET_I2C_REG, 0); |
| if (rc) { |
| log_simple_error(&e_info(OPAL_RC_I2C_RESET), "I2C: Failed " |
| "to reset the i2c engine\n"); |
| return; |
| } |
| time_wait_us_nopoll(10); |
| /* Reset port busy */ |
| rc = xscom_write(master->chip_id, master->xscom_base + |
| I2C_PORT_BUSY_REG, 0x8000000000000000ULL); |
| if (rc) { |
| log_simple_error(&e_info(OPAL_RC_I2C_RESET), "I2C: Failed " |
| "to reset port busy on i2c engine\n"); |
| return; |
| } |
| time_wait_us_nopoll(10); |
| list_for_each(&master->ports, p, link) { |
| mode = 0; |
| mode = SETFIELD(I2C_MODE_PORT_NUM, mode, p->port_num); |
| mode = SETFIELD(I2C_MODE_BIT_RATE_DIV, mode, p->bit_rate_div); |
| mode |= I2C_MODE_DIAGNOSTIC; |
| rc = xscom_write(master->chip_id, |
| master->xscom_base + I2C_MODE_REG, |
| mode); |
| if (rc) |
| prlog(PR_ERR, "I2C: Failed to write the MODE_REG\n"); |
| |
| time_wait_us_nopoll(10); |
| rc = xscom_write(master->chip_id, |
| master->xscom_base + I2C_RESET_S_SCL_REG, |
| 0); |
| if (rc) |
| prlog(PR_ERR, "I2C: Failed to reset S_SCL\n"); |
| |
| time_wait_us_nopoll(10); |
| rc = xscom_write(master->chip_id, |
| master->xscom_base + I2C_SET_S_SCL_REG, |
| 0); |
| if (rc) |
| prlog(PR_ERR, "I2C: Failed to set S_SCL\n"); |
| |
| /* Manually reset */ |
| time_wait_us_nopoll(10); |
| rc = xscom_write(master->chip_id, |
| master->xscom_base + I2C_RESET_S_SCL_REG, |
| 0); |
| if (rc) |
| prlog(PR_ERR, "I2C: sendStop: fail reset S_SCL\n"); |
| |
| time_wait_us_nopoll(10); |
| rc = xscom_write(master->chip_id, |
| master->xscom_base + I2C_RESET_S_SDA_REG, |
| 0); |
| if (rc) |
| prlog(PR_ERR, "I2C: sendStop: fail reset S_SDA\n"); |
| |
| time_wait_us_nopoll(10); |
| rc = xscom_write(master->chip_id, |
| master->xscom_base + I2C_SET_S_SCL_REG, |
| 0); |
| if (rc) |
| prlog(PR_ERR, "I2C: sendStop: fail set S_SCL\n"); |
| |
| time_wait_us_nopoll(10); |
| rc = xscom_write(master->chip_id, |
| master->xscom_base + I2C_SET_S_SDA_REG, |
| 0); |
| if (rc) |
| prlog(PR_ERR, "I2C: sendStop: fail set 2 S_SDA\n"); |
| |
| mode ^= I2C_MODE_DIAGNOSTIC; |
| time_wait_us_nopoll(10); |
| rc = xscom_write(master->chip_id, |
| master->xscom_base + I2C_MODE_REG, |
| mode); |
| if (rc) |
| prlog(PR_ERR, "I2C: Failed to write the MODE_REG\n"); |
| } |
| } |
| |
| static int p8_i2c_reset_engine(struct p8_i2c_master *master) |
| { |
| struct p8_i2c_master_port *p; |
| int reset_loops; |
| int rc; |
| uint64_t status; |
| |
| list_for_each(&master->ports, p, link) { |
| /* |
| * Reset each port by issuing a STOP command to slave. |
| * |
| * Reprogram the mode register with 'enhanced bit' set |
| */ |
| rc = p8_i2c_prog_mode(p, true); |
| if (rc) { |
| log_simple_error(&e_info(OPAL_RC_I2C_RESET), |
| "I2C: Failed to program the MODE_REG\n"); |
| return -1; |
| } |
| |
| /* Send an immediate stop */ |
| master->state = state_error; |
| rc = xscom_write(master->chip_id, master->xscom_base + |
| I2C_CMD_REG, I2C_CMD_WITH_STOP); |
| if (rc) { |
| log_simple_error(&e_info(OPAL_RC_I2C_RESET), |
| "I2C: Failed to issue immediate STOP\n"); |
| return -1; |
| } |
| |
| /* Wait for COMMAND COMPLETE */ |
| reset_loops = 0; |
| do { |
| rc = xscom_read(master->chip_id, |
| master->xscom_base + I2C_STAT_REG, |
| &status); |
| if (rc) { |
| log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER), |
| "I2C: Failed to read the STAT_REG\n"); |
| return -1; |
| } |
| if (! (status & I2C_STAT_CMD_COMP)) { |
| time_wait_ms(10); |
| if (reset_loops++ == 5) { |
| prlog(PR_WARNING, "I2C: Retrying reset, with force!\n"); |
| p8_i2c_force_reset(master); |
| continue; |
| } |
| if (reset_loops == 10) { |
| log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER), |
| "I2C: Failed to recover i2c engine\n"); |
| break; |
| } |
| } |
| } while (! (status & I2C_STAT_CMD_COMP)); |
| } |
| return 0; |
| } |
| |
| static void p8_i2c_status_error(struct p8_i2c_master_port *port, |
| struct i2c_request *req, |
| uint64_t status, uint64_t end_time) |
| { |
| struct p8_i2c_master *master = port->master; |
| int rc; |
| |
| /* Display any error other than I2C_INTR_NACK_RCVD_ERR or |
| * timeout since getting NACK's is normal if Linux is probing |
| * the bus and timeouts will have already logged something. |
| */ |
| if (!(status & (I2C_STAT_NACK_RCVD_ERR | I2C_STAT_PSEUDO_TIMEOUT))) { |
| log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER), |
| "I2C: Transfer error occurred\n"); |
| p8_i2c_print_debug_info(port, req, end_time); |
| } |
| |
| p8_i2c_translate_error(req, status); |
| |
| rc = p8_i2c_engine_reset(port); |
| if (rc) |
| goto exit; |
| |
| if (status & (I2C_STAT_LBUS_PARITY_ERR | I2C_STAT_ARBT_LOST_ERR | |
| I2C_STAT_STOP_ERR)) { |
| /* |
| * Don't bother issuing a STOP command for those errors |
| * just get rid of the current request and start off with |
| * the fresh one in the list |
| */ |
| p8_i2c_complete_request(master, req, req->result); |
| } else { |
| if (p8_i2c_reset_engine(master)) |
| goto exit; |
| /* Enable the interrupt */ |
| p8_i2c_enable_irqs(master); |
| } |
| return; |
| |
| exit: |
| p8_i2c_complete_request(master, req, req->result); |
| } |
| |
| static int p8_i2c_fifo_read(struct p8_i2c_master *master, |
| uint8_t *buf, uint32_t count) |
| { |
| uint64_t fifo; |
| uint32_t i; |
| int rc = 0; |
| |
| for (i = 0; i < count; i++, buf++) { |
| rc = xscom_read(master->chip_id, master->xscom_base + |
| I2C_FIFO_REG, &fifo); |
| if (rc) { |
| log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER), |
| "I2C: Failed to read the fifo\n"); |
| break; |
| } |
| |
| *buf = GETFIELD(I2C_FIFO, fifo); |
| } |
| return rc; |
| } |
| |
| static int p8_i2c_fifo_write(struct p8_i2c_master *master, |
| uint8_t *buf, uint32_t count) |
| { |
| uint64_t fifo; |
| uint32_t i; |
| int rc = 0; |
| |
| for (i = 0; i < count; i++, buf++) { |
| fifo = SETFIELD(I2C_FIFO, 0ull, *buf); |
| rc = xscom_write(master->chip_id, master->xscom_base + |
| I2C_FIFO_REG, fifo); |
| if (rc) { |
| log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER), |
| "I2C: Failed to write the fifo\n"); |
| break; |
| } |
| } |
| return rc; |
| } |
| |
| static void p8_i2c_status_data_request(struct p8_i2c_master *master, |
| struct i2c_request *req, |
| uint64_t status) |
| { |
| uint32_t fifo_count, fifo_free, count; |
| uint8_t *buf; |
| int rc = 0; |
| |
| fifo_count = GETFIELD(I2C_STAT_FIFO_ENTRY_COUNT, status); |
| fifo_free = master->fifo_size - fifo_count; |
| |
| DBG("Data request, state=%d fifo_count=%d/%d bytes_sent=%d\n", |
| master->state, fifo_count, master->fifo_size, master->bytes_sent); |
| |
| switch(master->state) { |
| case state_offset: |
| /* We assume the offset can always be written in one go */ |
| if (fifo_free < req->offset_bytes) { |
| log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER), |
| "I2C: Fifo too small for offset !\n"); |
| rc = OPAL_HARDWARE; |
| } else { |
| rc = p8_i2c_fifo_write(master, master->obuf, |
| req->offset_bytes); |
| } |
| |
| /* For read, wait address phase to complete */ |
| if (rc || req->op != SMBUS_WRITE) |
| break; |
| |
| /* For writes, transition to data phase now */ |
| master->state = state_data; |
| fifo_free -= req->offset_bytes; |
| /* Fall through */ |
| case state_data: |
| /* Sanity check */ |
| if (master->bytes_sent >= req->rw_len) { |
| log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER), "I2C: " |
| "Data req with no data to send sent=%d " |
| "req=%d\n", master->bytes_sent, |
| req->rw_len); |
| rc = OPAL_HARDWARE; |
| break; |
| } |
| |
| /* Get next chunk */ |
| buf = req->rw_buf + master->bytes_sent; |
| count = req->rw_len - master->bytes_sent; |
| |
| /* Check direction */ |
| if (req->op == I2C_READ || req->op == SMBUS_READ) { |
| if (count > fifo_count) |
| count = fifo_count; |
| rc = p8_i2c_fifo_read(master, buf, count); |
| } else { |
| if (count > fifo_free) |
| count = fifo_free; |
| rc = p8_i2c_fifo_write(master, buf, count); |
| } |
| if (rc == 0) |
| master->bytes_sent += count; |
| break; |
| default: |
| log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER), "I2C: Invalid " |
| "state %d in data req !\n", master->state); |
| rc = OPAL_WRONG_STATE; |
| } |
| if (rc) |
| p8_i2c_complete_request(master, req, rc); |
| else |
| p8_i2c_enable_irqs(master); |
| } |
| |
| static void p8_i2c_complete_offset(struct p8_i2c_master *master, |
| struct i2c_request *req) |
| { |
| uint64_t cmd; |
| int rc = 0; |
| |
| DBG("Completing offset phase\n"); |
| |
| /* If it's a write, we should only get here for empty |
| * write commands |
| */ |
| if (req->op == SMBUS_WRITE && req->rw_len != 0) { |
| log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER), "I2C: Write " |
| "completion in offset state !\n"); |
| rc = OPAL_HARDWARE; |
| goto complete; |
| } |
| |
| /* Switch to data phase */ |
| master->state = state_data; |
| |
| /* If it's not a read command, or there are no data to read, |
| * then we complete the command |
| */ |
| if (req->op != SMBUS_READ || req->rw_len == 0) |
| goto complete; |
| |
| /* Otherwise, let's start the data phase */ |
| cmd = I2C_CMD_WITH_START | I2C_CMD_WITH_ADDR | |
| I2C_CMD_WITH_STOP | I2C_CMD_READ_NOT_WRITE; |
| cmd = SETFIELD(I2C_CMD_DEV_ADDR, cmd, req->dev_addr); |
| cmd = SETFIELD(I2C_CMD_LEN_BYTES, cmd, req->rw_len); |
| cmd = SETFIELD(I2C_CMD_INTR_STEERING, cmd, I2C_CMD_INTR_STEER_HOST); |
| |
| DBG("Command: %016llx, state: %d\n", cmd, master->state); |
| |
| /* Send command */ |
| rc = xscom_write(master->chip_id, master->xscom_base + I2C_CMD_REG, |
| cmd); |
| if (rc) { |
| log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER), "I2C: Failed " |
| "to write the CMD_REG\n"); |
| goto complete; |
| } |
| |
| /* Enable the interrupts */ |
| p8_i2c_enable_irqs(master); |
| return; |
| |
| complete: |
| p8_i2c_complete_request(master, req, rc); |
| } |
| |
| static void p8_i2c_status_cmd_completion(struct p8_i2c_master *master, |
| struct i2c_request *req, |
| uint64_t end_time __unused) |
| { |
| int rc; |
| |
| DBG("Command completion, state=%d bytes_sent=%d\n", |
| master->state, master->bytes_sent); |
| DBG(" start_time=%016llx end_time=%016llx (duration=%016llx)\n", |
| master->start_time, end_time, end_time - master->start_time); |
| |
| /* If we complete an offset, we probably need to transition |
| * do a data read, check if that all makes sense |
| */ |
| if (master->state == state_offset) { |
| p8_i2c_complete_offset(master, req); |
| return; |
| } |
| |
| /* If we are not already in error state, check if we have |
| * completed our data transfer properly |
| */ |
| if (master->state != state_error && master->bytes_sent != req->rw_len) { |
| log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER), "I2C: Request " |
| "complete with residual data req=%d done=%d\n", |
| req->rw_len, master->bytes_sent); |
| /* Should we error out here ? */ |
| } |
| rc = master->state == state_error ? req->result : OPAL_SUCCESS; |
| p8_i2c_complete_request(master, req, rc); |
| } |
| |
| static void p8_i2c_check_status(struct p8_i2c_master *master) |
| { |
| struct p8_i2c_master_port *port; |
| struct i2c_request *req; |
| uint64_t status, now = mftb(); |
| int rc; |
| |
| /* If we are idle, just return, we'll catch error conditions |
| * when we next try to enqueue a request |
| */ |
| if (master->state == state_idle) |
| return; |
| |
| /* Read status register */ |
| rc = xscom_read(master->chip_id, master->xscom_base + I2C_STAT_REG, |
| &status); |
| if (rc) { |
| log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER), "I2C: Failed " |
| "to read the STAT_REG\n"); |
| return; |
| } |
| |
| /* Nothing happened ? Go back */ |
| if (!(status & (I2C_STAT_ANY_ERR | I2C_STAT_DATA_REQ | |
| I2C_STAT_CMD_COMP))) |
| return; |
| |
| DBG("Non-0 status: %016llx\n", status); |
| |
| /* Mask the interrupts for this engine */ |
| rc = xscom_write(master->chip_id, master->xscom_base + I2C_INTR_REG, |
| ~I2C_INTR_ALL); |
| if (rc) { |
| log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER), "I2C: Failed " |
| "to disable the interrupts\n"); |
| return; |
| } |
| |
| /* No request ? That's not normal ! Bail out without re-enabling |
| * the interrupt |
| */ |
| req = list_top(&master->req_list, struct i2c_request, link); |
| if (req == NULL) { |
| log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER), |
| "I2C: Interrupt with no request" |
| ", status=0x%016llx\n", status); |
| return; |
| } |
| |
| /* Get port for current request */ |
| port = container_of(req->bus, struct p8_i2c_master_port, bus); |
| |
| /* Handle the status in that order: errors, data requests and |
| * command completion. |
| */ |
| if (status & I2C_STAT_ANY_ERR) { |
| /* Mask status to avoid some unrelated bit overwriting |
| * our pseudo-status "timeout" bit 63 |
| */ |
| p8_i2c_status_error(port, req, status & I2C_STAT_ANY_ERR, now); |
| } else if (status & I2C_STAT_DATA_REQ) |
| p8_i2c_status_data_request(master, req, status); |
| else if (status & I2C_STAT_CMD_COMP) |
| p8_i2c_status_cmd_completion(master, req, now); |
| } |
| |
| static int p8_i2c_check_initial_status(struct p8_i2c_master_port *port) |
| { |
| struct p8_i2c_master *master = port->master; |
| uint64_t status, estat; |
| int rc; |
| |
| master->recovery_pass++; |
| |
| /* Read status register */ |
| rc = xscom_read(master->chip_id, master->xscom_base + I2C_STAT_REG, |
| &status); |
| if (rc) { |
| log_simple_error(&e_info(OPAL_RC_I2C_START_REQ), "I2C: Failed " |
| "to read the STAT_REG\n"); |
| return rc; |
| } |
| |
| rc = xscom_read(master->chip_id, |
| master->xscom_base + I2C_EXTD_STAT_REG, |
| &estat); |
| if (rc) { |
| log_simple_error(&e_info(OPAL_RC_I2C_START_REQ), "I2C: Failed " |
| "to read the EXTD_STAT_REG\n"); |
| return rc; |
| } |
| if (estat & (I2C_EXTD_STAT_I2C_BUSY | I2C_EXTD_STAT_SELF_BUSY)) { |
| DBG("Initial estat busy ! %016llx\n", estat); |
| /* Just a warning for now */ |
| } |
| |
| /* Nothing happened ? Go back */ |
| if (status & I2C_STAT_ANY_ERR) { |
| log_simple_error(&e_info(OPAL_RC_I2C_START_REQ), "I2C: " |
| "Initial error status 0x%016llx\n", status); |
| |
| if (master->recovery_pass > 1) { |
| log_simple_error(&e_info(OPAL_RC_I2C_START_REQ), "I2C: " |
| "Error stuck, aborting !!\n"); |
| return OPAL_HARDWARE; |
| } |
| |
| /* Mark state as "recovery" to block any other activity */ |
| master->state = state_recovery; |
| |
| /* Reset the engine */ |
| p8_i2c_engine_reset(port); |
| |
| /* Delay 5ms for bus to settle */ |
| schedule_timer(&master->recovery, msecs_to_tb(5)); |
| unlock(&master->lock); |
| return OPAL_BUSY; |
| } |
| |
| /* Still busy ? */ |
| if (!(status & I2C_STAT_CMD_COMP)) { |
| log_simple_error(&e_info(OPAL_RC_I2C_START_REQ), "I2C: Initial " |
| "command complete not set\n"); |
| |
| if (master->recovery_pass > 5) { |
| log_simple_error(&e_info(OPAL_RC_I2C_START_REQ), "I2C: " |
| "Command stuck, aborting !!\n"); |
| return OPAL_HARDWARE; |
| } |
| |
| |
| master->state = state_recovery; |
| |
| /* Delay 5ms for bus to settle */ |
| schedule_timer(&master->recovery, msecs_to_tb(5)); |
| unlock(&master->lock); |
| return OPAL_BUSY; |
| } |
| |
| master->recovery_pass = 0; |
| return 0; |
| } |
| |
| static int p8_i2c_start_request(struct p8_i2c_master *master, |
| struct i2c_request *req) |
| { |
| struct p8_i2c_master_port *port; |
| struct p8_i2c_request *request = |
| container_of(req, struct p8_i2c_request, req); |
| uint64_t cmd, now, poll_interval; |
| int64_t rc, tbytes; |
| |
| DBG("Starting req %d len=%d addr=%02x (offset=%x)\n", |
| req->op, req->rw_len, req->dev_addr, req->offset); |
| |
| /* Get port */ |
| port = container_of(req->bus, struct p8_i2c_master_port, bus); |
| |
| /* Check if we need to disable the OCC cache first */ |
| if (master->type == I2C_CENTAUR && !master->occ_cache_dis) { |
| DBG("Disabling OCC cache...\n"); |
| rc = centaur_disable_sensor_cache(master->chip_id); |
| if (rc < 0) { |
| log_simple_error(&e_info(OPAL_RC_I2C_START_REQ), |
| "I2C: Failed " |
| "to disable the sensor cache\n"); |
| return rc; |
| } |
| master->occ_cache_dis = true; |
| |
| /* Do we need to wait ? */ |
| if (rc > 0) { |
| DBG("Waiting %lld\n", rc); |
| master->state = state_occache_dis; |
| schedule_timer(&master->recovery, rc); |
| return 0; |
| } |
| } |
| |
| /* Convert the offset if needed */ |
| if (req->offset_bytes) { |
| int i; |
| |
| for (i = 0; i < req->offset_bytes; i++) { |
| uint8_t b; |
| |
| b = req->offset >> (8 * (req->offset_bytes - i - 1)); |
| master->obuf[i] = b; |
| } |
| DBG("Offset %d bytes: %02x %02x %02x %02x\n", |
| req->offset_bytes, master->obuf[0], master->obuf[1], |
| master->obuf[2], master->obuf[3]); |
| } |
| |
| /* Program mode register */ |
| rc = p8_i2c_prog_mode(port, false); |
| if (rc) { |
| log_simple_error(&e_info(OPAL_RC_I2C_START_REQ), "I2C: Failed " |
| "to program the MODE_REG\n"); |
| return rc; |
| } |
| |
| /* Check status */ |
| rc = p8_i2c_check_initial_status(port); |
| if (rc != OPAL_BUSY) |
| master->recovery_pass = 0; |
| if (rc) |
| return rc; |
| |
| /* Initialize bytes_sent */ |
| master->bytes_sent = 0; |
| |
| /* Set up the command register */ |
| cmd = I2C_CMD_WITH_START | I2C_CMD_WITH_ADDR; |
| cmd = SETFIELD(I2C_CMD_DEV_ADDR, cmd, req->dev_addr); |
| cmd = SETFIELD(I2C_CMD_INTR_STEERING, cmd, I2C_CMD_INTR_STEER_HOST); |
| switch (req->op) { |
| case I2C_READ: |
| cmd |= I2C_CMD_READ_NOT_WRITE; |
| /* Fall through */ |
| case I2C_WRITE: |
| cmd |= I2C_CMD_WITH_STOP; |
| cmd = SETFIELD(I2C_CMD_LEN_BYTES, cmd, req->rw_len); |
| master->state = state_data; |
| break; |
| case SMBUS_READ: |
| cmd = SETFIELD(I2C_CMD_LEN_BYTES, cmd, req->offset_bytes); |
| master->state = state_offset; |
| break; |
| case SMBUS_WRITE: |
| cmd |= I2C_CMD_WITH_STOP; |
| cmd = SETFIELD(I2C_CMD_LEN_BYTES, cmd, |
| req->rw_len + req->offset_bytes); |
| master->state = state_offset; |
| break; |
| default: |
| return OPAL_PARAMETER; |
| } |
| DBG("Command: %016llx, state: %d\n", cmd, master->state); |
| |
| master->start_time = mftb(); |
| |
| /* Send command */ |
| rc = xscom_write(master->chip_id, master->xscom_base + I2C_CMD_REG, |
| cmd); |
| if (rc) { |
| log_simple_error(&e_info(OPAL_RC_I2C_START_REQ), "I2C: Failed " |
| "to write the CMD_REG\n"); |
| return rc; |
| } |
| |
| /* Enable the interrupts */ |
| p8_i2c_enable_irqs(master); |
| |
| /* Run a poll timer for boot cases or non-working interrupts |
| * cases |
| */ |
| if (!opal_booting() && master->irq_ok) |
| poll_interval = TIMER_POLL; |
| else |
| poll_interval = master->poll_interval; |
| now = schedule_timer(&master->poller, poll_interval); |
| |
| /* Calculate and start timeout */ |
| if (request->timeout) { |
| request->timeout += now; |
| } else { |
| tbytes = req->rw_len + req->offset_bytes + 2; |
| request->timeout = now + tbytes * master->byte_timeout; |
| } |
| |
| /* Start the timeout */ |
| schedule_timer_at(&master->timeout, request->timeout); |
| |
| return OPAL_SUCCESS; |
| } |
| |
| static void p8_i2c_check_work(struct p8_i2c_master *master) |
| { |
| struct i2c_request *req; |
| int rc; |
| |
| while (master->state == state_idle && !list_empty(&master->req_list)) { |
| req = list_top(&master->req_list, struct i2c_request, link); |
| rc = p8_i2c_start_request(master, req); |
| if (rc && rc != OPAL_BUSY) |
| p8_i2c_complete_request(master, req, rc); |
| } |
| } |
| |
| static int p8_i2c_queue_request(struct i2c_request *req) |
| { |
| struct i2c_bus *bus = req->bus; |
| struct p8_i2c_master_port *port = |
| container_of(bus, struct p8_i2c_master_port, bus); |
| struct p8_i2c_master *master = port->master; |
| int rc = 0; |
| |
| /* Parameter check */ |
| if (req->rw_len > I2C_MAX_TFR_LEN) { |
| prlog(PR_ERR, "I2C: Too large transfer %d bytes\n", req->rw_len); |
| return OPAL_PARAMETER; |
| } |
| |
| if (req->offset_bytes > 4) { |
| prlog(PR_ERR, "I2C: Invalid offset size %d\n", req->offset_bytes); |
| return OPAL_PARAMETER; |
| } |
| lock(&master->lock); |
| list_add_tail(&master->req_list, &req->link); |
| p8_i2c_check_work(master); |
| unlock(&master->lock); |
| |
| return rc; |
| } |
| |
| static struct i2c_request *p8_i2c_alloc_request(struct i2c_bus *bus) |
| { |
| struct p8_i2c_master_port *port = |
| container_of(bus, struct p8_i2c_master_port, bus); |
| struct p8_i2c_request *request; |
| |
| request = zalloc(sizeof(*request)); |
| if (!request) { |
| prlog(PR_ERR, "I2C: Failed to allocate i2c request\n"); |
| return NULL; |
| } |
| |
| request->port_num = port->port_num; |
| request->req.bus = bus; |
| |
| return &request->req; |
| } |
| |
| static void p8_i2c_free_request(struct i2c_request *req) |
| { |
| struct p8_i2c_request *request = |
| container_of(req, struct p8_i2c_request, req); |
| free(request); |
| } |
| |
| static void p8_i2c_set_request_timeout(struct i2c_request *req, |
| uint64_t duration) |
| { |
| struct p8_i2c_request *request = |
| container_of(req, struct p8_i2c_request, req); |
| |
| request->timeout = msecs_to_tb(duration); |
| } |
| |
| static uint64_t p8_i2c_run_request(struct i2c_request *req) |
| { |
| struct i2c_bus *bus = req->bus; |
| struct p8_i2c_master_port *port = |
| container_of(bus, struct p8_i2c_master_port, bus); |
| struct p8_i2c_master *master = port->master; |
| uint64_t poll_interval = 0; |
| |
| lock(&master->lock); |
| p8_i2c_check_status(master); |
| p8_i2c_check_work(master); |
| poll_interval = master->poll_interval; |
| unlock(&master->lock); |
| |
| return poll_interval; |
| } |
| |
| static inline uint32_t p8_i2c_get_bit_rate_divisor(uint32_t lb_freq, |
| uint32_t bus_speed) |
| { |
| assert(bus_speed > 0); |
| return (((lb_freq / bus_speed) - 1) / 4); |
| } |
| |
| static inline uint64_t p8_i2c_get_poll_interval(uint32_t bus_speed) |
| { |
| uint64_t usec; |
| |
| assert(bus_speed > 0); |
| |
| /* Polling Interval = 8 * (1/bus_speed) * (1/10) -> convert to uSec */ |
| usec = ((8 * USEC_PER_SEC) / (10 * bus_speed)); |
| return usecs_to_tb(usec); |
| } |
| |
| static void p8_i2c_timeout(struct timer *t __unused, void *data, uint64_t now) |
| { |
| struct p8_i2c_master_port *port; |
| struct p8_i2c_master *master = data; |
| struct p8_i2c_request *request; |
| struct i2c_request *req; |
| |
| lock(&master->lock); |
| |
| /* This could be spurrious ... */ |
| if (master->state == state_idle) { |
| DBG("I2C: Timeout in idle state\n"); |
| goto exit; |
| } |
| |
| /* We might still be spurrious timer, we need to ensure that the |
| * head request is indeed old enough to be the one timing out |
| */ |
| req = list_top(&master->req_list, struct i2c_request, link); |
| if (req == NULL) { |
| DBG("I2C: Timeout with no" |
| " pending request state=%d\n", master->state); |
| goto exit; |
| } |
| request = container_of(req, struct p8_i2c_request, req); |
| if (tb_compare(now, request->timeout) == TB_ABEFOREB) { |
| DBG("I2C: Timeout with request not expired\n"); |
| goto exit; |
| } |
| |
| request->timeout = 0ul; |
| port = container_of(req->bus, struct p8_i2c_master_port, bus); |
| |
| /* Allright, we have a request and it has timed out ... */ |
| log_simple_error(&e_info(OPAL_RC_I2C_TIMEOUT), |
| "I2C: Request timeout !\n"); |
| p8_i2c_print_debug_info(port, req, now); |
| |
| /* Use the standard error path */ |
| p8_i2c_status_error(port, req, I2C_STAT_PSEUDO_TIMEOUT, now); |
| exit: |
| unlock(&master->lock); |
| } |
| |
| static void p8_i2c_recover(struct timer *t __unused, void *data, |
| uint64_t now __unused) |
| { |
| struct p8_i2c_master *master = data; |
| |
| lock(&master->lock); |
| assert(master->state == state_recovery || |
| master->state == state_occache_dis); |
| master->state = state_idle; |
| |
| /* We may or may not still have work pending, re-enable the sensor cache |
| * immediately if we don't (we just waited the recovery time so there is |
| * little point waiting longer). |
| */ |
| if (master->occ_cache_dis && list_empty(&master->req_list)) { |
| DBG("Re-enabling OCC cache after recovery\n"); |
| centaur_enable_sensor_cache(master->chip_id); |
| master->occ_cache_dis = false; |
| } |
| |
| /* Re-check for new work */ |
| p8_i2c_check_work(master); |
| unlock(&master->lock); |
| } |
| |
| static void p8_i2c_enable_scache(struct timer *t __unused, void *data, |
| uint64_t now __unused) |
| { |
| struct p8_i2c_master *master = data; |
| |
| lock(&master->lock); |
| |
| /* Check if we are still idle */ |
| if (master->state == state_idle && master->occ_cache_dis) { |
| DBG("Re-enabling OCC cache\n"); |
| centaur_enable_sensor_cache(master->chip_id); |
| master->occ_cache_dis = false; |
| } |
| unlock(&master->lock); |
| } |
| |
| static void p8_i2c_poll(struct timer *t __unused, void *data, uint64_t now) |
| { |
| struct p8_i2c_master *master = data; |
| |
| /* |
| * This is called when the interrupt isn't functional or |
| * generally from the opal pollers, so fast while booting |
| * and slowly when Linux is up. |
| */ |
| |
| /* Lockless fast bailout */ |
| if (master->state == state_idle) |
| return; |
| |
| lock(&master->lock); |
| p8_i2c_check_status(master); |
| if (master->state != state_idle) |
| schedule_timer_at(&master->poller, now + master->poll_interval); |
| p8_i2c_check_work(master); |
| unlock(&master->lock); |
| } |
| |
| void p8_i2c_interrupt(uint32_t chip_id) |
| { |
| struct proc_chip *chip = get_chip(chip_id); |
| struct p8_i2c_master *master = NULL; |
| |
| assert(chip); |
| list_for_each(&chip->i2cms, master, link) { |
| |
| /* Lockless fast bailout (shared interrupt) */ |
| if (master->state == state_idle) |
| continue; |
| |
| lock(&master->lock); |
| |
| /* Run the state machine */ |
| p8_i2c_check_status(master); |
| |
| /* Check for new work */ |
| p8_i2c_check_work(master); |
| |
| unlock(&master->lock); |
| } |
| } |
| |
| static const char *compat[] = { |
| "ibm,power8-i2cm", |
| "ibm,centaur-i2cm" |
| }; |
| |
| static void p8_i2c_add_bus_prop(struct p8_i2c_master_port *port) |
| { |
| const struct dt_property *c, *p; |
| struct dt_node *np = port->bus.dt_node; |
| char name[32]; |
| |
| c = dt_find_property(np, "compatible"); |
| p = dt_find_property(np, "ibm,port-name"); |
| |
| if (!c) { |
| if (port->master->type == I2C_POWER8) |
| dt_add_property_strings(np, "compatible", |
| "ibm,power8-i2c-port", |
| "ibm,opal-i2c"); |
| else if (port->master->type == I2C_CENTAUR) |
| dt_add_property_strings(np, "compatible", |
| "ibm,centaur-i2c-port", |
| "ibm,opal-i2c"); |
| } |
| |
| if (!p) { |
| if (port->master->type == I2C_POWER8) |
| snprintf(name, sizeof(name), "p8_%08x_e%dp%d", |
| port->master->chip_id, port->master->engine_id, |
| port->port_num); |
| else if (port->master->type == I2C_CENTAUR) |
| snprintf(name, sizeof(name), "cen_%08x_e%dp%d", |
| port->master->chip_id, port->master->engine_id, |
| port->port_num); |
| |
| dt_add_property_string(np, "ibm,port-name", name); |
| } |
| } |
| |
| static void p8_i2c_init_one(struct dt_node *i2cm, enum p8_i2c_master_type type) |
| { |
| struct p8_i2c_master_port *port; |
| uint32_t lb_freq, count, max_bus_speed; |
| struct dt_node *i2cm_port; |
| struct p8_i2c_master *master; |
| struct list_head *chip_list; |
| uint64_t ex_stat; |
| static bool irq_printed; |
| int64_t rc; |
| |
| master = zalloc(sizeof(*master)); |
| if (!master) { |
| log_simple_error(&e_info(OPAL_RC_I2C_INIT), |
| "I2C: Failed to allocate master " |
| "structure\n"); |
| return; |
| } |
| master->type = type; |
| |
| /* Local bus speed in Hz */ |
| lb_freq = dt_prop_get_u32(i2cm, "clock-frequency"); |
| |
| /* Initialise the i2c master structure */ |
| master->state = state_idle; |
| master->chip_id = dt_get_chip_id(i2cm); |
| master->engine_id = dt_prop_get_u32(i2cm, "chip-engine#"); |
| master->xscom_base = dt_get_address(i2cm, 0, NULL); |
| if (master->type == I2C_CENTAUR) { |
| struct centaur_chip *centaur = get_centaur(master->chip_id); |
| if (centaur == NULL) { |
| log_simple_error(&e_info(OPAL_RC_I2C_INIT), |
| "I2C: Failed to get centaur 0x%x ", |
| master->chip_id); |
| free(master); |
| return; |
| } |
| chip_list = ¢aur->i2cms; |
| |
| /* Detect bad device-tree from HostBoot giving us bogus |
| * i2c masters |
| */ |
| if (master->engine_id > 0) { |
| prlog(PR_ERR, "I2C: Skipping Centaur Master #1\n"); |
| free(master); |
| return; |
| } |
| } else { |
| struct proc_chip *chip = get_chip(master->chip_id); |
| assert(chip); |
| chip_list = &chip->i2cms; |
| } |
| init_timer(&master->timeout, p8_i2c_timeout, master); |
| init_timer(&master->poller, p8_i2c_poll, master); |
| init_timer(&master->recovery, p8_i2c_recover, master); |
| init_timer(&master->sensor_cache, p8_i2c_enable_scache, master); |
| |
| prlog(PR_INFO, "I2C: Chip %08x Eng. %d Clock %d Mhz\n", |
| master->chip_id, master->engine_id, lb_freq / 1000000); |
| |
| /* Disable OCC cache during inits */ |
| if (master->type == I2C_CENTAUR) { |
| rc = centaur_disable_sensor_cache(master->chip_id); |
| if (rc < 0) { |
| log_simple_error(&e_info(OPAL_RC_I2C_INIT), "I2C: " |
| "Error %lld disabling sensor cache\n", |
| rc); |
| /* Ignore error and move on ... */ |
| } else |
| time_wait(rc); |
| } |
| rc = xscom_read(master->chip_id, master->xscom_base + |
| I2C_EXTD_STAT_REG, &ex_stat); |
| if (rc) { |
| log_simple_error(&e_info(OPAL_RC_I2C_INIT), "I2C: " |
| "Failed to read EXTD_STAT_REG\n"); |
| if (master->type == I2C_CENTAUR) |
| centaur_enable_sensor_cache(master->chip_id); |
| free(master); |
| return; |
| } |
| |
| master->fifo_size = GETFIELD(I2C_EXTD_STAT_FIFO_SIZE, ex_stat); |
| list_head_init(&master->req_list); |
| list_head_init(&master->ports); |
| |
| /* Check if interrupt is usable */ |
| master->irq_ok = p8_i2c_has_irqs(master); |
| if (!irq_printed) { |
| irq_printed = true; |
| prlog(PR_INFO, "I2C: Interrupts %sfunctional\n", |
| master->irq_ok ? "" : "non-"); |
| } |
| |
| /* Program the watermark register */ |
| rc = p8_i2c_prog_watermark(master); |
| |
| /* Re-enable the sensor cache, we aren't touching HW anymore */ |
| if (master->type == I2C_CENTAUR) |
| centaur_enable_sensor_cache(master->chip_id); |
| |
| /* Handle errors from p8_i2c_prog_watermark */ |
| if (rc) { |
| log_simple_error(&e_info(OPAL_RC_I2C_INIT), |
| "I2C: Failed to program the " |
| "WATERMARK_REG\n"); |
| free(master); |
| return; |
| } |
| |
| /* Allocate ports driven by this master */ |
| count = 0; |
| dt_for_each_child(i2cm, i2cm_port) |
| count++; |
| |
| port = zalloc(sizeof(*port) * count); |
| if (!port) { |
| log_simple_error(&e_info(OPAL_RC_I2C_INIT), |
| "I2C: Insufficient memory\n"); |
| free(master); |
| return; |
| } |
| |
| /* Add master to chip's list */ |
| list_add_tail(chip_list, &master->link); |
| max_bus_speed = 0; |
| |
| dt_for_each_child(i2cm, i2cm_port) { |
| uint32_t speed; |
| |
| port->port_num = dt_prop_get_u32(i2cm_port, "reg"); |
| port->master = master; |
| speed = dt_prop_get_u32(i2cm_port, "bus-frequency"); |
| if (speed > max_bus_speed) |
| max_bus_speed = speed; |
| port->bit_rate_div = |
| p8_i2c_get_bit_rate_divisor(lb_freq, speed); |
| port->bus.dt_node = i2cm_port; |
| port->bus.queue_req = p8_i2c_queue_request; |
| port->bus.alloc_req = p8_i2c_alloc_request; |
| port->bus.free_req = p8_i2c_free_request; |
| port->bus.set_req_timeout = p8_i2c_set_request_timeout; |
| port->bus.run_req = p8_i2c_run_request; |
| i2c_add_bus(&port->bus); |
| list_add_tail(&master->ports, &port->link); |
| |
| /* Add OPAL properties to the bus node */ |
| p8_i2c_add_bus_prop(port); |
| prlog(PR_INFO, " P%d: <%s> %d kHz\n", |
| port->port_num, |
| (char *)dt_prop_get(i2cm_port, |
| "ibm,port-name"), speed/1000); |
| port++; |
| } |
| |
| /* When at runtime and we have the i2c irq, we just use it |
| * (see p8_i2c_start_request), but in the situation where |
| * one of those isn't the case (e.g. during boot), we need |
| * a better poll interval to efficiently crank the i2c machine. |
| * poll_interval is that interval. |
| */ |
| master->poll_interval = (max_bus_speed) ? p8_i2c_get_poll_interval(max_bus_speed) : TIMER_POLL; |
| |
| master->byte_timeout = master->irq_ok ? |
| msecs_to_tb(I2C_TIMEOUT_IRQ_MS) : |
| msecs_to_tb(I2C_TIMEOUT_POLL_MS); |
| } |
| |
| void p8_i2c_init(void) |
| { |
| struct dt_node *i2cm; |
| int i; |
| |
| for (i = 0; i < MAX_I2C_TYPE; i++) { |
| dt_for_each_compatible(dt_root, i2cm, compat[i]) |
| p8_i2c_init_one(i2cm, i); |
| } |
| } |