| /* Copyright 2015 The Chromium OS Authors. All rights reserved. |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| /* PECI interface for Chrome EC */ |
| |
| #include "clock.h" |
| #include "hooks.h" |
| #include "peci.h" |
| #include "registers.h" |
| #include "util.h" |
| #include "timer.h" |
| #include "task.h" |
| |
| enum peci_status { |
| PECI_STATUS_NO_ERR = 0x00, |
| PECI_STATUS_HOBY = 0x01, |
| PECI_STATUS_FINISH = 0x02, |
| PECI_STATUS_RD_FCS_ERR = 0x04, |
| PECI_STATUS_WR_FCS_ERR = 0x08, |
| PECI_STATUS_EXTERR = 0x20, |
| PECI_STATUS_BUSERR = 0x40, |
| PECI_STATUS_RCV_ERRCODE = 0x80, |
| PECI_STATUS_ERR_NEED_RST = (PECI_STATUS_BUSERR | PECI_STATUS_EXTERR), |
| PECI_STATUS_ANY_ERR = (PECI_STATUS_RCV_ERRCODE | |
| PECI_STATUS_BUSERR | |
| PECI_STATUS_EXTERR | |
| PECI_STATUS_WR_FCS_ERR | |
| PECI_STATUS_RD_FCS_ERR), |
| PECI_STATUS_ANY_BIT = 0xFE, |
| PECI_STATUS_TIMEOUT = 0xFF, |
| }; |
| |
| static task_id_t peci_current_task; |
| |
| static void peci_init_vtt_freq(void) |
| { |
| /* |
| * bit2, enable the PECI interrupt generated by data valid event |
| * from PECI. |
| * |
| * bit[1-0], these bits are used to set PECI VTT level. |
| * 00b: 1.10v |
| * 01b: 1.05v |
| * 10b: 1.00v |
| */ |
| IT83XX_PECI_PADCTLR = 0x06; |
| |
| /* |
| * bit[2-0], these bits are used to set PECI host's optimal |
| * transfer rate. |
| * 000b: 2.0 MHz |
| * 001b: 1.0 MHz |
| * 100b: 1.6 MHz |
| */ |
| IT83XX_PECI_HOCTL2R = 0x01; |
| } |
| |
| static void peci_reset(void) |
| { |
| /* Reset PECI */ |
| IT83XX_GCTRL_RSTC4 |= 0x10; |
| |
| /* short delay */ |
| udelay(15); |
| |
| peci_init_vtt_freq(); |
| } |
| |
| /** |
| * Start a PECI transaction |
| * |
| * @param peci transaction data |
| * |
| * @return zero if successful, non-zero if error |
| */ |
| int peci_transaction(struct peci_data *peci) |
| { |
| uint8_t status; |
| int index; |
| |
| /* To enable PECI function pin */ |
| IT83XX_GPIO_GPCRF6 = 0x00; |
| |
| /* |
| * bit5, Both write and read data FIFO pointers will be cleared. |
| * |
| * bit4, This bit enables the PECI host to abort the transaction |
| * when FCS error occurs. |
| * |
| * bit2, This bit enables the contention mechanism of the PECI bus. |
| * When this bit is set, the host will abort the transaction |
| * if the PECI bus is contentious. |
| */ |
| IT83XX_PECI_HOCTLR |= 0x34; |
| |
| /* This register is the target address field of the PECI protocol. */ |
| IT83XX_PECI_HOTRADDR = peci->addr; |
| |
| /* This register is the write length field of the PECI protocol. */ |
| ASSERT(peci->w_len <= PECI_WRITE_DATA_FIFO_SIZE); |
| |
| if (peci->cmd_code == PECI_CMD_PING) { |
| /* write length is 0 */ |
| IT83XX_PECI_HOWRLR = 0x00; |
| } else { |
| if ((peci->cmd_code == PECI_CMD_WR_PKG_CFG) || |
| (peci->cmd_code == PECI_CMD_WR_IAMSR) || |
| (peci->cmd_code == PECI_CMD_WR_PCI_CFG) || |
| (peci->cmd_code == PECI_CMD_WR_PCI_CFG_LOCAL)) { |
| |
| /* write length include Cmd Code + AW FCS */ |
| IT83XX_PECI_HOWRLR = peci->w_len + 2; |
| |
| /* bit1, The bit enables the AW_FCS hardwired mechanism |
| * based on the PECI command. This bit is functional |
| * only when the AW_FCS supported command of |
| * PECI 2.0/3.0/3.1 is issued. |
| * When this bit is set, the hardware will handle the |
| * calculation of AW_FCS. |
| */ |
| IT83XX_PECI_HOCTLR |= 0x02; |
| } else { |
| /* write length include Cmd Code */ |
| IT83XX_PECI_HOWRLR = peci->w_len + 1; |
| |
| IT83XX_PECI_HOCTLR &= ~0x02; |
| } |
| } |
| |
| /* This register is the read length field of the PECI protocol. */ |
| ASSERT(peci->r_len <= PECI_READ_DATA_FIFO_SIZE); |
| IT83XX_PECI_HORDLR = peci->r_len; |
| |
| /* This register is the command field of the PECI protocol. */ |
| IT83XX_PECI_HOCMDR = peci->cmd_code; |
| |
| /* The write data field of the PECI protocol. */ |
| for (index = 0x00; index < peci->w_len; index++) |
| IT83XX_PECI_HOWRDR = peci->w_buf[index]; |
| |
| peci_current_task = task_get_current(); |
| task_clear_pending_irq(IT83XX_IRQ_PECI); |
| task_enable_irq(IT83XX_IRQ_PECI); |
| |
| /* start */ |
| IT83XX_PECI_HOCTLR |= 0x01; |
| |
| /* pre-set timeout */ |
| index = peci->timeout_us; |
| if (task_wait_event(peci->timeout_us) != TASK_EVENT_TIMER) |
| index = 0; |
| |
| task_disable_irq(IT83XX_IRQ_PECI); |
| |
| peci_current_task = TASK_ID_INVALID; |
| |
| if (index < peci->timeout_us) { |
| |
| status = IT83XX_PECI_HOSTAR; |
| |
| /* any error */ |
| if (IT83XX_PECI_HOSTAR & PECI_STATUS_ANY_ERR) { |
| |
| if (IT83XX_PECI_HOSTAR & PECI_STATUS_ERR_NEED_RST) |
| peci_reset(); |
| |
| } else if (IT83XX_PECI_HOSTAR & PECI_STATUS_FINISH) { |
| |
| /* The read data field of the PECI protocol. */ |
| for (index = 0x00; index < peci->r_len; index++) |
| peci->r_buf[index] = IT83XX_PECI_HORDDR; |
| |
| /* W/C */ |
| IT83XX_PECI_HOSTAR = PECI_STATUS_FINISH; |
| status = IT83XX_PECI_HOSTAR; |
| } |
| } else { |
| /* transaction timeout */ |
| status = PECI_STATUS_TIMEOUT; |
| } |
| |
| /* Don't disable PECI host controller if controller already enable. */ |
| IT83XX_PECI_HOCTLR = 0x08; |
| |
| /* W/C */ |
| IT83XX_PECI_HOSTAR = PECI_STATUS_ANY_BIT; |
| |
| /* Disable PECI function pin */ |
| IT83XX_GPIO_GPCRF6 = 0x80; |
| |
| return status; |
| } |
| |
| void peci_interrupt(void) |
| { |
| task_clear_pending_irq(IT83XX_IRQ_PECI); |
| task_disable_irq(IT83XX_IRQ_PECI); |
| |
| if (peci_current_task != TASK_ID_INVALID) |
| task_wake(peci_current_task); |
| } |
| |
| static void peci_init(void) |
| { |
| clock_enable_peripheral(CGC_OFFSET_PECI, 0, 0); |
| peci_init_vtt_freq(); |
| |
| /* bit3,this bit enables the PECI host controller. */ |
| IT83XX_PECI_HOCTLR |= 0x08; |
| |
| /* bit4, PECI enable */ |
| IT83XX_GPIO_GRC2 |= 0x10; |
| } |
| DECLARE_HOOK(HOOK_INIT, peci_init, HOOK_PRIO_DEFAULT); |