18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Driver for Amlogic Meson AO CEC Controller 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2015 Amlogic, Inc. All rights reserved 58c2ecf20Sopenharmony_ci * Copyright (C) 2017 BayLibre, SAS 68c2ecf20Sopenharmony_ci * Author: Neil Armstrong <narmstrong@baylibre.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * SPDX-License-Identifier: GPL-2.0+ 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/bitfield.h> 128c2ecf20Sopenharmony_ci#include <linux/clk.h> 138c2ecf20Sopenharmony_ci#include <linux/device.h> 148c2ecf20Sopenharmony_ci#include <linux/io.h> 158c2ecf20Sopenharmony_ci#include <linux/delay.h> 168c2ecf20Sopenharmony_ci#include <linux/kernel.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/of.h> 198c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 208c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 218c2ecf20Sopenharmony_ci#include <linux/types.h> 228c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 238c2ecf20Sopenharmony_ci#include <linux/reset.h> 248c2ecf20Sopenharmony_ci#include <media/cec.h> 258c2ecf20Sopenharmony_ci#include <media/cec-notifier.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* CEC Registers */ 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* 308c2ecf20Sopenharmony_ci * [2:1] cntl_clk 318c2ecf20Sopenharmony_ci * - 0 = Disable clk (Power-off mode) 328c2ecf20Sopenharmony_ci * - 1 = Enable gated clock (Normal mode) 338c2ecf20Sopenharmony_ci * - 2 = Enable free-run clk (Debug mode) 348c2ecf20Sopenharmony_ci */ 358c2ecf20Sopenharmony_ci#define CEC_GEN_CNTL_REG 0x00 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define CEC_GEN_CNTL_RESET BIT(0) 388c2ecf20Sopenharmony_ci#define CEC_GEN_CNTL_CLK_DISABLE 0 398c2ecf20Sopenharmony_ci#define CEC_GEN_CNTL_CLK_ENABLE 1 408c2ecf20Sopenharmony_ci#define CEC_GEN_CNTL_CLK_ENABLE_DBG 2 418c2ecf20Sopenharmony_ci#define CEC_GEN_CNTL_CLK_CTRL_MASK GENMASK(2, 1) 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* 448c2ecf20Sopenharmony_ci * [7:0] cec_reg_addr 458c2ecf20Sopenharmony_ci * [15:8] cec_reg_wrdata 468c2ecf20Sopenharmony_ci * [16] cec_reg_wr 478c2ecf20Sopenharmony_ci * - 0 = Read 488c2ecf20Sopenharmony_ci * - 1 = Write 498c2ecf20Sopenharmony_ci * [23] bus free 508c2ecf20Sopenharmony_ci * [31:24] cec_reg_rddata 518c2ecf20Sopenharmony_ci */ 528c2ecf20Sopenharmony_ci#define CEC_RW_REG 0x04 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#define CEC_RW_ADDR GENMASK(7, 0) 558c2ecf20Sopenharmony_ci#define CEC_RW_WR_DATA GENMASK(15, 8) 568c2ecf20Sopenharmony_ci#define CEC_RW_WRITE_EN BIT(16) 578c2ecf20Sopenharmony_ci#define CEC_RW_BUS_BUSY BIT(23) 588c2ecf20Sopenharmony_ci#define CEC_RW_RD_DATA GENMASK(31, 24) 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* 618c2ecf20Sopenharmony_ci * [1] tx intr 628c2ecf20Sopenharmony_ci * [2] rx intr 638c2ecf20Sopenharmony_ci */ 648c2ecf20Sopenharmony_ci#define CEC_INTR_MASKN_REG 0x08 658c2ecf20Sopenharmony_ci#define CEC_INTR_CLR_REG 0x0c 668c2ecf20Sopenharmony_ci#define CEC_INTR_STAT_REG 0x10 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci#define CEC_INTR_TX BIT(1) 698c2ecf20Sopenharmony_ci#define CEC_INTR_RX BIT(2) 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/* CEC Commands */ 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci#define CEC_TX_MSG_0_HEADER 0x00 748c2ecf20Sopenharmony_ci#define CEC_TX_MSG_1_OPCODE 0x01 758c2ecf20Sopenharmony_ci#define CEC_TX_MSG_2_OP1 0x02 768c2ecf20Sopenharmony_ci#define CEC_TX_MSG_3_OP2 0x03 778c2ecf20Sopenharmony_ci#define CEC_TX_MSG_4_OP3 0x04 788c2ecf20Sopenharmony_ci#define CEC_TX_MSG_5_OP4 0x05 798c2ecf20Sopenharmony_ci#define CEC_TX_MSG_6_OP5 0x06 808c2ecf20Sopenharmony_ci#define CEC_TX_MSG_7_OP6 0x07 818c2ecf20Sopenharmony_ci#define CEC_TX_MSG_8_OP7 0x08 828c2ecf20Sopenharmony_ci#define CEC_TX_MSG_9_OP8 0x09 838c2ecf20Sopenharmony_ci#define CEC_TX_MSG_A_OP9 0x0A 848c2ecf20Sopenharmony_ci#define CEC_TX_MSG_B_OP10 0x0B 858c2ecf20Sopenharmony_ci#define CEC_TX_MSG_C_OP11 0x0C 868c2ecf20Sopenharmony_ci#define CEC_TX_MSG_D_OP12 0x0D 878c2ecf20Sopenharmony_ci#define CEC_TX_MSG_E_OP13 0x0E 888c2ecf20Sopenharmony_ci#define CEC_TX_MSG_F_OP14 0x0F 898c2ecf20Sopenharmony_ci#define CEC_TX_MSG_LENGTH 0x10 908c2ecf20Sopenharmony_ci#define CEC_TX_MSG_CMD 0x11 918c2ecf20Sopenharmony_ci#define CEC_TX_WRITE_BUF 0x12 928c2ecf20Sopenharmony_ci#define CEC_TX_CLEAR_BUF 0x13 938c2ecf20Sopenharmony_ci#define CEC_RX_MSG_CMD 0x14 948c2ecf20Sopenharmony_ci#define CEC_RX_CLEAR_BUF 0x15 958c2ecf20Sopenharmony_ci#define CEC_LOGICAL_ADDR0 0x16 968c2ecf20Sopenharmony_ci#define CEC_LOGICAL_ADDR1 0x17 978c2ecf20Sopenharmony_ci#define CEC_LOGICAL_ADDR2 0x18 988c2ecf20Sopenharmony_ci#define CEC_LOGICAL_ADDR3 0x19 998c2ecf20Sopenharmony_ci#define CEC_LOGICAL_ADDR4 0x1A 1008c2ecf20Sopenharmony_ci#define CEC_CLOCK_DIV_H 0x1B 1018c2ecf20Sopenharmony_ci#define CEC_CLOCK_DIV_L 0x1C 1028c2ecf20Sopenharmony_ci#define CEC_QUIESCENT_25MS_BIT7_0 0x20 1038c2ecf20Sopenharmony_ci#define CEC_QUIESCENT_25MS_BIT11_8 0x21 1048c2ecf20Sopenharmony_ci#define CEC_STARTBITMINL2H_3MS5_BIT7_0 0x22 1058c2ecf20Sopenharmony_ci#define CEC_STARTBITMINL2H_3MS5_BIT8 0x23 1068c2ecf20Sopenharmony_ci#define CEC_STARTBITMAXL2H_3MS9_BIT7_0 0x24 1078c2ecf20Sopenharmony_ci#define CEC_STARTBITMAXL2H_3MS9_BIT8 0x25 1088c2ecf20Sopenharmony_ci#define CEC_STARTBITMINH_0MS6_BIT7_0 0x26 1098c2ecf20Sopenharmony_ci#define CEC_STARTBITMINH_0MS6_BIT8 0x27 1108c2ecf20Sopenharmony_ci#define CEC_STARTBITMAXH_1MS0_BIT7_0 0x28 1118c2ecf20Sopenharmony_ci#define CEC_STARTBITMAXH_1MS0_BIT8 0x29 1128c2ecf20Sopenharmony_ci#define CEC_STARTBITMINTOT_4MS3_BIT7_0 0x2A 1138c2ecf20Sopenharmony_ci#define CEC_STARTBITMINTOT_4MS3_BIT9_8 0x2B 1148c2ecf20Sopenharmony_ci#define CEC_STARTBITMAXTOT_4MS7_BIT7_0 0x2C 1158c2ecf20Sopenharmony_ci#define CEC_STARTBITMAXTOT_4MS7_BIT9_8 0x2D 1168c2ecf20Sopenharmony_ci#define CEC_LOGIC1MINL2H_0MS4_BIT7_0 0x2E 1178c2ecf20Sopenharmony_ci#define CEC_LOGIC1MINL2H_0MS4_BIT8 0x2F 1188c2ecf20Sopenharmony_ci#define CEC_LOGIC1MAXL2H_0MS8_BIT7_0 0x30 1198c2ecf20Sopenharmony_ci#define CEC_LOGIC1MAXL2H_0MS8_BIT8 0x31 1208c2ecf20Sopenharmony_ci#define CEC_LOGIC0MINL2H_1MS3_BIT7_0 0x32 1218c2ecf20Sopenharmony_ci#define CEC_LOGIC0MINL2H_1MS3_BIT8 0x33 1228c2ecf20Sopenharmony_ci#define CEC_LOGIC0MAXL2H_1MS7_BIT7_0 0x34 1238c2ecf20Sopenharmony_ci#define CEC_LOGIC0MAXL2H_1MS7_BIT8 0x35 1248c2ecf20Sopenharmony_ci#define CEC_LOGICMINTOTAL_2MS05_BIT7_0 0x36 1258c2ecf20Sopenharmony_ci#define CEC_LOGICMINTOTAL_2MS05_BIT9_8 0x37 1268c2ecf20Sopenharmony_ci#define CEC_LOGICMAXHIGH_2MS8_BIT7_0 0x38 1278c2ecf20Sopenharmony_ci#define CEC_LOGICMAXHIGH_2MS8_BIT8 0x39 1288c2ecf20Sopenharmony_ci#define CEC_LOGICERRLOW_3MS4_BIT7_0 0x3A 1298c2ecf20Sopenharmony_ci#define CEC_LOGICERRLOW_3MS4_BIT8 0x3B 1308c2ecf20Sopenharmony_ci#define CEC_NOMSMPPOINT_1MS05 0x3C 1318c2ecf20Sopenharmony_ci#define CEC_DELCNTR_LOGICERR 0x3E 1328c2ecf20Sopenharmony_ci#define CEC_TXTIME_17MS_BIT7_0 0x40 1338c2ecf20Sopenharmony_ci#define CEC_TXTIME_17MS_BIT10_8 0x41 1348c2ecf20Sopenharmony_ci#define CEC_TXTIME_2BIT_BIT7_0 0x42 1358c2ecf20Sopenharmony_ci#define CEC_TXTIME_2BIT_BIT10_8 0x43 1368c2ecf20Sopenharmony_ci#define CEC_TXTIME_4BIT_BIT7_0 0x44 1378c2ecf20Sopenharmony_ci#define CEC_TXTIME_4BIT_BIT10_8 0x45 1388c2ecf20Sopenharmony_ci#define CEC_STARTBITNOML2H_3MS7_BIT7_0 0x46 1398c2ecf20Sopenharmony_ci#define CEC_STARTBITNOML2H_3MS7_BIT8 0x47 1408c2ecf20Sopenharmony_ci#define CEC_STARTBITNOMH_0MS8_BIT7_0 0x48 1418c2ecf20Sopenharmony_ci#define CEC_STARTBITNOMH_0MS8_BIT8 0x49 1428c2ecf20Sopenharmony_ci#define CEC_LOGIC1NOML2H_0MS6_BIT7_0 0x4A 1438c2ecf20Sopenharmony_ci#define CEC_LOGIC1NOML2H_0MS6_BIT8 0x4B 1448c2ecf20Sopenharmony_ci#define CEC_LOGIC0NOML2H_1MS5_BIT7_0 0x4C 1458c2ecf20Sopenharmony_ci#define CEC_LOGIC0NOML2H_1MS5_BIT8 0x4D 1468c2ecf20Sopenharmony_ci#define CEC_LOGIC1NOMH_1MS8_BIT7_0 0x4E 1478c2ecf20Sopenharmony_ci#define CEC_LOGIC1NOMH_1MS8_BIT8 0x4F 1488c2ecf20Sopenharmony_ci#define CEC_LOGIC0NOMH_0MS9_BIT7_0 0x50 1498c2ecf20Sopenharmony_ci#define CEC_LOGIC0NOMH_0MS9_BIT8 0x51 1508c2ecf20Sopenharmony_ci#define CEC_LOGICERRLOW_3MS6_BIT7_0 0x52 1518c2ecf20Sopenharmony_ci#define CEC_LOGICERRLOW_3MS6_BIT8 0x53 1528c2ecf20Sopenharmony_ci#define CEC_CHKCONTENTION_0MS1 0x54 1538c2ecf20Sopenharmony_ci#define CEC_PREPARENXTBIT_0MS05_BIT7_0 0x56 1548c2ecf20Sopenharmony_ci#define CEC_PREPARENXTBIT_0MS05_BIT8 0x57 1558c2ecf20Sopenharmony_ci#define CEC_NOMSMPACKPOINT_0MS45 0x58 1568c2ecf20Sopenharmony_ci#define CEC_ACK0NOML2H_1MS5_BIT7_0 0x5A 1578c2ecf20Sopenharmony_ci#define CEC_ACK0NOML2H_1MS5_BIT8 0x5B 1588c2ecf20Sopenharmony_ci#define CEC_BUGFIX_DISABLE_0 0x60 1598c2ecf20Sopenharmony_ci#define CEC_BUGFIX_DISABLE_1 0x61 1608c2ecf20Sopenharmony_ci#define CEC_RX_MSG_0_HEADER 0x80 1618c2ecf20Sopenharmony_ci#define CEC_RX_MSG_1_OPCODE 0x81 1628c2ecf20Sopenharmony_ci#define CEC_RX_MSG_2_OP1 0x82 1638c2ecf20Sopenharmony_ci#define CEC_RX_MSG_3_OP2 0x83 1648c2ecf20Sopenharmony_ci#define CEC_RX_MSG_4_OP3 0x84 1658c2ecf20Sopenharmony_ci#define CEC_RX_MSG_5_OP4 0x85 1668c2ecf20Sopenharmony_ci#define CEC_RX_MSG_6_OP5 0x86 1678c2ecf20Sopenharmony_ci#define CEC_RX_MSG_7_OP6 0x87 1688c2ecf20Sopenharmony_ci#define CEC_RX_MSG_8_OP7 0x88 1698c2ecf20Sopenharmony_ci#define CEC_RX_MSG_9_OP8 0x89 1708c2ecf20Sopenharmony_ci#define CEC_RX_MSG_A_OP9 0x8A 1718c2ecf20Sopenharmony_ci#define CEC_RX_MSG_B_OP10 0x8B 1728c2ecf20Sopenharmony_ci#define CEC_RX_MSG_C_OP11 0x8C 1738c2ecf20Sopenharmony_ci#define CEC_RX_MSG_D_OP12 0x8D 1748c2ecf20Sopenharmony_ci#define CEC_RX_MSG_E_OP13 0x8E 1758c2ecf20Sopenharmony_ci#define CEC_RX_MSG_F_OP14 0x8F 1768c2ecf20Sopenharmony_ci#define CEC_RX_MSG_LENGTH 0x90 1778c2ecf20Sopenharmony_ci#define CEC_RX_MSG_STATUS 0x91 1788c2ecf20Sopenharmony_ci#define CEC_RX_NUM_MSG 0x92 1798c2ecf20Sopenharmony_ci#define CEC_TX_MSG_STATUS 0x93 1808c2ecf20Sopenharmony_ci#define CEC_TX_NUM_MSG 0x94 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci/* CEC_TX_MSG_CMD definition */ 1848c2ecf20Sopenharmony_ci#define TX_NO_OP 0 /* No transaction */ 1858c2ecf20Sopenharmony_ci#define TX_REQ_CURRENT 1 /* Transmit earliest message in buffer */ 1868c2ecf20Sopenharmony_ci#define TX_ABORT 2 /* Abort transmitting earliest message */ 1878c2ecf20Sopenharmony_ci#define TX_REQ_NEXT 3 /* Overwrite earliest msg, transmit next */ 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci/* tx_msg_status definition */ 1908c2ecf20Sopenharmony_ci#define TX_IDLE 0 /* No transaction */ 1918c2ecf20Sopenharmony_ci#define TX_BUSY 1 /* Transmitter is busy */ 1928c2ecf20Sopenharmony_ci#define TX_DONE 2 /* Message successfully transmitted */ 1938c2ecf20Sopenharmony_ci#define TX_ERROR 3 /* Message transmitted with error */ 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci/* rx_msg_cmd */ 1968c2ecf20Sopenharmony_ci#define RX_NO_OP 0 /* No transaction */ 1978c2ecf20Sopenharmony_ci#define RX_ACK_CURRENT 1 /* Read earliest message in buffer */ 1988c2ecf20Sopenharmony_ci#define RX_DISABLE 2 /* Disable receiving latest message */ 1998c2ecf20Sopenharmony_ci#define RX_ACK_NEXT 3 /* Clear earliest msg, read next */ 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci/* rx_msg_status */ 2028c2ecf20Sopenharmony_ci#define RX_IDLE 0 /* No transaction */ 2038c2ecf20Sopenharmony_ci#define RX_BUSY 1 /* Receiver is busy */ 2048c2ecf20Sopenharmony_ci#define RX_DONE 2 /* Message has been received successfully */ 2058c2ecf20Sopenharmony_ci#define RX_ERROR 3 /* Message has been received with error */ 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci/* RX_CLEAR_BUF options */ 2088c2ecf20Sopenharmony_ci#define CLEAR_START 1 2098c2ecf20Sopenharmony_ci#define CLEAR_STOP 0 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci/* CEC_LOGICAL_ADDRx options */ 2128c2ecf20Sopenharmony_ci#define LOGICAL_ADDR_MASK 0xf 2138c2ecf20Sopenharmony_ci#define LOGICAL_ADDR_VALID BIT(4) 2148c2ecf20Sopenharmony_ci#define LOGICAL_ADDR_DISABLE 0 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci#define CEC_CLK_RATE 32768 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistruct meson_ao_cec_device { 2198c2ecf20Sopenharmony_ci struct platform_device *pdev; 2208c2ecf20Sopenharmony_ci void __iomem *base; 2218c2ecf20Sopenharmony_ci struct clk *core; 2228c2ecf20Sopenharmony_ci spinlock_t cec_reg_lock; 2238c2ecf20Sopenharmony_ci struct cec_notifier *notify; 2248c2ecf20Sopenharmony_ci struct cec_adapter *adap; 2258c2ecf20Sopenharmony_ci struct cec_msg rx_msg; 2268c2ecf20Sopenharmony_ci}; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci#define writel_bits_relaxed(mask, val, addr) \ 2298c2ecf20Sopenharmony_ci writel_relaxed((readl_relaxed(addr) & ~(mask)) | (val), addr) 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistatic inline int meson_ao_cec_wait_busy(struct meson_ao_cec_device *ao_cec) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci ktime_t timeout = ktime_add_us(ktime_get(), 5000); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci while (readl_relaxed(ao_cec->base + CEC_RW_REG) & CEC_RW_BUS_BUSY) { 2368c2ecf20Sopenharmony_ci if (ktime_compare(ktime_get(), timeout) > 0) 2378c2ecf20Sopenharmony_ci return -ETIMEDOUT; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci return 0; 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic void meson_ao_cec_read(struct meson_ao_cec_device *ao_cec, 2448c2ecf20Sopenharmony_ci unsigned long address, u8 *data, 2458c2ecf20Sopenharmony_ci int *res) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci unsigned long flags; 2488c2ecf20Sopenharmony_ci u32 reg = FIELD_PREP(CEC_RW_ADDR, address); 2498c2ecf20Sopenharmony_ci int ret = 0; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci if (res && *res) 2528c2ecf20Sopenharmony_ci return; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci spin_lock_irqsave(&ao_cec->cec_reg_lock, flags); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci ret = meson_ao_cec_wait_busy(ao_cec); 2578c2ecf20Sopenharmony_ci if (ret) 2588c2ecf20Sopenharmony_ci goto read_out; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci writel_relaxed(reg, ao_cec->base + CEC_RW_REG); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci ret = meson_ao_cec_wait_busy(ao_cec); 2638c2ecf20Sopenharmony_ci if (ret) 2648c2ecf20Sopenharmony_ci goto read_out; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci *data = FIELD_GET(CEC_RW_RD_DATA, 2678c2ecf20Sopenharmony_ci readl_relaxed(ao_cec->base + CEC_RW_REG)); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ciread_out: 2708c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ao_cec->cec_reg_lock, flags); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if (res) 2738c2ecf20Sopenharmony_ci *res = ret; 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic void meson_ao_cec_write(struct meson_ao_cec_device *ao_cec, 2778c2ecf20Sopenharmony_ci unsigned long address, u8 data, 2788c2ecf20Sopenharmony_ci int *res) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci unsigned long flags; 2818c2ecf20Sopenharmony_ci u32 reg = FIELD_PREP(CEC_RW_ADDR, address) | 2828c2ecf20Sopenharmony_ci FIELD_PREP(CEC_RW_WR_DATA, data) | 2838c2ecf20Sopenharmony_ci CEC_RW_WRITE_EN; 2848c2ecf20Sopenharmony_ci int ret = 0; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci if (res && *res) 2878c2ecf20Sopenharmony_ci return; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci spin_lock_irqsave(&ao_cec->cec_reg_lock, flags); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci ret = meson_ao_cec_wait_busy(ao_cec); 2928c2ecf20Sopenharmony_ci if (ret) 2938c2ecf20Sopenharmony_ci goto write_out; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci writel_relaxed(reg, ao_cec->base + CEC_RW_REG); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ciwrite_out: 2988c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ao_cec->cec_reg_lock, flags); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci if (res) 3018c2ecf20Sopenharmony_ci *res = ret; 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cistatic inline void meson_ao_cec_irq_setup(struct meson_ao_cec_device *ao_cec, 3058c2ecf20Sopenharmony_ci bool enable) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci u32 cfg = CEC_INTR_TX | CEC_INTR_RX; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci writel_bits_relaxed(cfg, enable ? cfg : 0, 3108c2ecf20Sopenharmony_ci ao_cec->base + CEC_INTR_MASKN_REG); 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic inline int meson_ao_cec_clear(struct meson_ao_cec_device *ao_cec) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci int ret = 0; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci meson_ao_cec_write(ao_cec, CEC_RX_MSG_CMD, RX_DISABLE, &ret); 3188c2ecf20Sopenharmony_ci meson_ao_cec_write(ao_cec, CEC_TX_MSG_CMD, TX_ABORT, &ret); 3198c2ecf20Sopenharmony_ci meson_ao_cec_write(ao_cec, CEC_RX_CLEAR_BUF, 1, &ret); 3208c2ecf20Sopenharmony_ci meson_ao_cec_write(ao_cec, CEC_TX_CLEAR_BUF, 1, &ret); 3218c2ecf20Sopenharmony_ci if (ret) 3228c2ecf20Sopenharmony_ci return ret; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci udelay(100); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci meson_ao_cec_write(ao_cec, CEC_RX_CLEAR_BUF, 0, &ret); 3278c2ecf20Sopenharmony_ci meson_ao_cec_write(ao_cec, CEC_TX_CLEAR_BUF, 0, &ret); 3288c2ecf20Sopenharmony_ci if (ret) 3298c2ecf20Sopenharmony_ci return ret; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci udelay(100); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci meson_ao_cec_write(ao_cec, CEC_RX_MSG_CMD, RX_NO_OP, &ret); 3348c2ecf20Sopenharmony_ci meson_ao_cec_write(ao_cec, CEC_TX_MSG_CMD, TX_NO_OP, &ret); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci return ret; 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_cistatic int meson_ao_cec_arbit_bit_time_set(struct meson_ao_cec_device *ao_cec, 3408c2ecf20Sopenharmony_ci unsigned int bit_set, 3418c2ecf20Sopenharmony_ci unsigned int time_set) 3428c2ecf20Sopenharmony_ci{ 3438c2ecf20Sopenharmony_ci int ret = 0; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci switch (bit_set) { 3468c2ecf20Sopenharmony_ci case CEC_SIGNAL_FREE_TIME_RETRY: 3478c2ecf20Sopenharmony_ci meson_ao_cec_write(ao_cec, CEC_TXTIME_4BIT_BIT7_0, 3488c2ecf20Sopenharmony_ci time_set & 0xff, &ret); 3498c2ecf20Sopenharmony_ci meson_ao_cec_write(ao_cec, CEC_TXTIME_4BIT_BIT10_8, 3508c2ecf20Sopenharmony_ci (time_set >> 8) & 0x7, &ret); 3518c2ecf20Sopenharmony_ci break; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci case CEC_SIGNAL_FREE_TIME_NEW_INITIATOR: 3548c2ecf20Sopenharmony_ci meson_ao_cec_write(ao_cec, CEC_TXTIME_2BIT_BIT7_0, 3558c2ecf20Sopenharmony_ci time_set & 0xff, &ret); 3568c2ecf20Sopenharmony_ci meson_ao_cec_write(ao_cec, CEC_TXTIME_2BIT_BIT10_8, 3578c2ecf20Sopenharmony_ci (time_set >> 8) & 0x7, &ret); 3588c2ecf20Sopenharmony_ci break; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci case CEC_SIGNAL_FREE_TIME_NEXT_XFER: 3618c2ecf20Sopenharmony_ci meson_ao_cec_write(ao_cec, CEC_TXTIME_17MS_BIT7_0, 3628c2ecf20Sopenharmony_ci time_set & 0xff, &ret); 3638c2ecf20Sopenharmony_ci meson_ao_cec_write(ao_cec, CEC_TXTIME_17MS_BIT10_8, 3648c2ecf20Sopenharmony_ci (time_set >> 8) & 0x7, &ret); 3658c2ecf20Sopenharmony_ci break; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci return ret; 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cistatic irqreturn_t meson_ao_cec_irq(int irq, void *data) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci struct meson_ao_cec_device *ao_cec = data; 3748c2ecf20Sopenharmony_ci u32 stat = readl_relaxed(ao_cec->base + CEC_INTR_STAT_REG); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci if (stat) 3778c2ecf20Sopenharmony_ci return IRQ_WAKE_THREAD; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci return IRQ_NONE; 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_cistatic void meson_ao_cec_irq_tx(struct meson_ao_cec_device *ao_cec) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci unsigned long tx_status = 0; 3858c2ecf20Sopenharmony_ci u8 stat; 3868c2ecf20Sopenharmony_ci int ret = 0; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci meson_ao_cec_read(ao_cec, CEC_TX_MSG_STATUS, &stat, &ret); 3898c2ecf20Sopenharmony_ci if (ret) 3908c2ecf20Sopenharmony_ci goto tx_reg_err; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci switch (stat) { 3938c2ecf20Sopenharmony_ci case TX_DONE: 3948c2ecf20Sopenharmony_ci tx_status = CEC_TX_STATUS_OK; 3958c2ecf20Sopenharmony_ci break; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci case TX_BUSY: 3988c2ecf20Sopenharmony_ci tx_status = CEC_TX_STATUS_ARB_LOST; 3998c2ecf20Sopenharmony_ci break; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci case TX_IDLE: 4028c2ecf20Sopenharmony_ci tx_status = CEC_TX_STATUS_LOW_DRIVE; 4038c2ecf20Sopenharmony_ci break; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci case TX_ERROR: 4068c2ecf20Sopenharmony_ci default: 4078c2ecf20Sopenharmony_ci tx_status = CEC_TX_STATUS_NACK; 4088c2ecf20Sopenharmony_ci break; 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci /* Clear Interruption */ 4128c2ecf20Sopenharmony_ci writel_relaxed(CEC_INTR_TX, ao_cec->base + CEC_INTR_CLR_REG); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci /* Stop TX */ 4158c2ecf20Sopenharmony_ci meson_ao_cec_write(ao_cec, CEC_TX_MSG_CMD, TX_NO_OP, &ret); 4168c2ecf20Sopenharmony_ci if (ret) 4178c2ecf20Sopenharmony_ci goto tx_reg_err; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci cec_transmit_attempt_done(ao_cec->adap, tx_status); 4208c2ecf20Sopenharmony_ci return; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_citx_reg_err: 4238c2ecf20Sopenharmony_ci cec_transmit_attempt_done(ao_cec->adap, CEC_TX_STATUS_ERROR); 4248c2ecf20Sopenharmony_ci} 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_cistatic void meson_ao_cec_irq_rx(struct meson_ao_cec_device *ao_cec) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci int i, ret = 0; 4298c2ecf20Sopenharmony_ci u8 reg; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci meson_ao_cec_read(ao_cec, CEC_RX_MSG_STATUS, ®, &ret); 4328c2ecf20Sopenharmony_ci if (reg != RX_DONE) 4338c2ecf20Sopenharmony_ci goto rx_out; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci meson_ao_cec_read(ao_cec, CEC_RX_NUM_MSG, ®, &ret); 4368c2ecf20Sopenharmony_ci if (reg != 1) 4378c2ecf20Sopenharmony_ci goto rx_out; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci meson_ao_cec_read(ao_cec, CEC_RX_MSG_LENGTH, ®, &ret); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci ao_cec->rx_msg.len = reg + 1; 4428c2ecf20Sopenharmony_ci if (ao_cec->rx_msg.len > CEC_MAX_MSG_SIZE) 4438c2ecf20Sopenharmony_ci ao_cec->rx_msg.len = CEC_MAX_MSG_SIZE; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci for (i = 0; i < ao_cec->rx_msg.len; i++) { 4468c2ecf20Sopenharmony_ci u8 byte; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci meson_ao_cec_read(ao_cec, CEC_RX_MSG_0_HEADER + i, &byte, &ret); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci ao_cec->rx_msg.msg[i] = byte; 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci if (ret) 4548c2ecf20Sopenharmony_ci goto rx_out; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci cec_received_msg(ao_cec->adap, &ao_cec->rx_msg); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_cirx_out: 4598c2ecf20Sopenharmony_ci /* Clear Interruption */ 4608c2ecf20Sopenharmony_ci writel_relaxed(CEC_INTR_RX, ao_cec->base + CEC_INTR_CLR_REG); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci /* Ack RX message */ 4638c2ecf20Sopenharmony_ci meson_ao_cec_write(ao_cec, CEC_RX_MSG_CMD, RX_ACK_CURRENT, &ret); 4648c2ecf20Sopenharmony_ci meson_ao_cec_write(ao_cec, CEC_RX_MSG_CMD, RX_NO_OP, &ret); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci /* Clear RX buffer */ 4678c2ecf20Sopenharmony_ci meson_ao_cec_write(ao_cec, CEC_RX_CLEAR_BUF, CLEAR_START, &ret); 4688c2ecf20Sopenharmony_ci meson_ao_cec_write(ao_cec, CEC_RX_CLEAR_BUF, CLEAR_STOP, &ret); 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_cistatic irqreturn_t meson_ao_cec_irq_thread(int irq, void *data) 4728c2ecf20Sopenharmony_ci{ 4738c2ecf20Sopenharmony_ci struct meson_ao_cec_device *ao_cec = data; 4748c2ecf20Sopenharmony_ci u32 stat = readl_relaxed(ao_cec->base + CEC_INTR_STAT_REG); 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci if (stat & CEC_INTR_TX) 4778c2ecf20Sopenharmony_ci meson_ao_cec_irq_tx(ao_cec); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci meson_ao_cec_irq_rx(ao_cec); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4828c2ecf20Sopenharmony_ci} 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_cistatic int meson_ao_cec_set_log_addr(struct cec_adapter *adap, u8 logical_addr) 4858c2ecf20Sopenharmony_ci{ 4868c2ecf20Sopenharmony_ci struct meson_ao_cec_device *ao_cec = adap->priv; 4878c2ecf20Sopenharmony_ci int ret = 0; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci meson_ao_cec_write(ao_cec, CEC_LOGICAL_ADDR0, 4908c2ecf20Sopenharmony_ci LOGICAL_ADDR_DISABLE, &ret); 4918c2ecf20Sopenharmony_ci if (ret) 4928c2ecf20Sopenharmony_ci return ret; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci ret = meson_ao_cec_clear(ao_cec); 4958c2ecf20Sopenharmony_ci if (ret) 4968c2ecf20Sopenharmony_ci return ret; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci if (logical_addr == CEC_LOG_ADDR_INVALID) 4998c2ecf20Sopenharmony_ci return 0; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci meson_ao_cec_write(ao_cec, CEC_LOGICAL_ADDR0, 5028c2ecf20Sopenharmony_ci logical_addr & LOGICAL_ADDR_MASK, &ret); 5038c2ecf20Sopenharmony_ci if (ret) 5048c2ecf20Sopenharmony_ci return ret; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci udelay(100); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci meson_ao_cec_write(ao_cec, CEC_LOGICAL_ADDR0, 5098c2ecf20Sopenharmony_ci (logical_addr & LOGICAL_ADDR_MASK) | 5108c2ecf20Sopenharmony_ci LOGICAL_ADDR_VALID, &ret); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci return ret; 5138c2ecf20Sopenharmony_ci} 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_cistatic int meson_ao_cec_transmit(struct cec_adapter *adap, u8 attempts, 5168c2ecf20Sopenharmony_ci u32 signal_free_time, struct cec_msg *msg) 5178c2ecf20Sopenharmony_ci{ 5188c2ecf20Sopenharmony_ci struct meson_ao_cec_device *ao_cec = adap->priv; 5198c2ecf20Sopenharmony_ci int i, ret = 0; 5208c2ecf20Sopenharmony_ci u8 reg; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci meson_ao_cec_read(ao_cec, CEC_TX_MSG_STATUS, ®, &ret); 5238c2ecf20Sopenharmony_ci if (ret) 5248c2ecf20Sopenharmony_ci return ret; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci if (reg == TX_BUSY) { 5278c2ecf20Sopenharmony_ci dev_dbg(&ao_cec->pdev->dev, "%s: busy TX: aborting\n", 5288c2ecf20Sopenharmony_ci __func__); 5298c2ecf20Sopenharmony_ci meson_ao_cec_write(ao_cec, CEC_TX_MSG_CMD, TX_ABORT, &ret); 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci for (i = 0; i < msg->len; i++) { 5338c2ecf20Sopenharmony_ci meson_ao_cec_write(ao_cec, CEC_TX_MSG_0_HEADER + i, 5348c2ecf20Sopenharmony_ci msg->msg[i], &ret); 5358c2ecf20Sopenharmony_ci } 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci meson_ao_cec_write(ao_cec, CEC_TX_MSG_LENGTH, msg->len - 1, &ret); 5388c2ecf20Sopenharmony_ci meson_ao_cec_write(ao_cec, CEC_TX_MSG_CMD, TX_REQ_CURRENT, &ret); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci return ret; 5418c2ecf20Sopenharmony_ci} 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_cistatic int meson_ao_cec_adap_enable(struct cec_adapter *adap, bool enable) 5448c2ecf20Sopenharmony_ci{ 5458c2ecf20Sopenharmony_ci struct meson_ao_cec_device *ao_cec = adap->priv; 5468c2ecf20Sopenharmony_ci int ret; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci meson_ao_cec_irq_setup(ao_cec, false); 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci writel_bits_relaxed(CEC_GEN_CNTL_RESET, CEC_GEN_CNTL_RESET, 5518c2ecf20Sopenharmony_ci ao_cec->base + CEC_GEN_CNTL_REG); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci if (!enable) 5548c2ecf20Sopenharmony_ci return 0; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci /* Enable gated clock (Normal mode). */ 5578c2ecf20Sopenharmony_ci writel_bits_relaxed(CEC_GEN_CNTL_CLK_CTRL_MASK, 5588c2ecf20Sopenharmony_ci FIELD_PREP(CEC_GEN_CNTL_CLK_CTRL_MASK, 5598c2ecf20Sopenharmony_ci CEC_GEN_CNTL_CLK_ENABLE), 5608c2ecf20Sopenharmony_ci ao_cec->base + CEC_GEN_CNTL_REG); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci udelay(100); 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci /* Release Reset */ 5658c2ecf20Sopenharmony_ci writel_bits_relaxed(CEC_GEN_CNTL_RESET, 0, 5668c2ecf20Sopenharmony_ci ao_cec->base + CEC_GEN_CNTL_REG); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci /* Clear buffers */ 5698c2ecf20Sopenharmony_ci ret = meson_ao_cec_clear(ao_cec); 5708c2ecf20Sopenharmony_ci if (ret) 5718c2ecf20Sopenharmony_ci return ret; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci /* CEC arbitration 3/5/7 bit time set. */ 5748c2ecf20Sopenharmony_ci ret = meson_ao_cec_arbit_bit_time_set(ao_cec, 5758c2ecf20Sopenharmony_ci CEC_SIGNAL_FREE_TIME_RETRY, 5768c2ecf20Sopenharmony_ci 0x118); 5778c2ecf20Sopenharmony_ci if (ret) 5788c2ecf20Sopenharmony_ci return ret; 5798c2ecf20Sopenharmony_ci ret = meson_ao_cec_arbit_bit_time_set(ao_cec, 5808c2ecf20Sopenharmony_ci CEC_SIGNAL_FREE_TIME_NEW_INITIATOR, 5818c2ecf20Sopenharmony_ci 0x000); 5828c2ecf20Sopenharmony_ci if (ret) 5838c2ecf20Sopenharmony_ci return ret; 5848c2ecf20Sopenharmony_ci ret = meson_ao_cec_arbit_bit_time_set(ao_cec, 5858c2ecf20Sopenharmony_ci CEC_SIGNAL_FREE_TIME_NEXT_XFER, 5868c2ecf20Sopenharmony_ci 0x2aa); 5878c2ecf20Sopenharmony_ci if (ret) 5888c2ecf20Sopenharmony_ci return ret; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci meson_ao_cec_irq_setup(ao_cec, true); 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci return 0; 5938c2ecf20Sopenharmony_ci} 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_cistatic const struct cec_adap_ops meson_ao_cec_ops = { 5968c2ecf20Sopenharmony_ci .adap_enable = meson_ao_cec_adap_enable, 5978c2ecf20Sopenharmony_ci .adap_log_addr = meson_ao_cec_set_log_addr, 5988c2ecf20Sopenharmony_ci .adap_transmit = meson_ao_cec_transmit, 5998c2ecf20Sopenharmony_ci}; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_cistatic int meson_ao_cec_probe(struct platform_device *pdev) 6028c2ecf20Sopenharmony_ci{ 6038c2ecf20Sopenharmony_ci struct meson_ao_cec_device *ao_cec; 6048c2ecf20Sopenharmony_ci struct device *hdmi_dev; 6058c2ecf20Sopenharmony_ci struct resource *res; 6068c2ecf20Sopenharmony_ci int ret, irq; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci hdmi_dev = cec_notifier_parse_hdmi_phandle(&pdev->dev); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci if (IS_ERR(hdmi_dev)) 6118c2ecf20Sopenharmony_ci return PTR_ERR(hdmi_dev); 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci ao_cec = devm_kzalloc(&pdev->dev, sizeof(*ao_cec), GFP_KERNEL); 6148c2ecf20Sopenharmony_ci if (!ao_cec) 6158c2ecf20Sopenharmony_ci return -ENOMEM; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci spin_lock_init(&ao_cec->cec_reg_lock); 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci ao_cec->adap = cec_allocate_adapter(&meson_ao_cec_ops, ao_cec, 6208c2ecf20Sopenharmony_ci "meson_ao_cec", 6218c2ecf20Sopenharmony_ci CEC_CAP_DEFAULTS | 6228c2ecf20Sopenharmony_ci CEC_CAP_CONNECTOR_INFO, 6238c2ecf20Sopenharmony_ci 1); /* Use 1 for now */ 6248c2ecf20Sopenharmony_ci if (IS_ERR(ao_cec->adap)) 6258c2ecf20Sopenharmony_ci return PTR_ERR(ao_cec->adap); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci ao_cec->adap->owner = THIS_MODULE; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 6308c2ecf20Sopenharmony_ci ao_cec->base = devm_ioremap_resource(&pdev->dev, res); 6318c2ecf20Sopenharmony_ci if (IS_ERR(ao_cec->base)) { 6328c2ecf20Sopenharmony_ci ret = PTR_ERR(ao_cec->base); 6338c2ecf20Sopenharmony_ci goto out_probe_adapter; 6348c2ecf20Sopenharmony_ci } 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 6378c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(&pdev->dev, irq, 6388c2ecf20Sopenharmony_ci meson_ao_cec_irq, 6398c2ecf20Sopenharmony_ci meson_ao_cec_irq_thread, 6408c2ecf20Sopenharmony_ci 0, NULL, ao_cec); 6418c2ecf20Sopenharmony_ci if (ret) { 6428c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "irq request failed\n"); 6438c2ecf20Sopenharmony_ci goto out_probe_adapter; 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci ao_cec->core = devm_clk_get(&pdev->dev, "core"); 6478c2ecf20Sopenharmony_ci if (IS_ERR(ao_cec->core)) { 6488c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "core clock request failed\n"); 6498c2ecf20Sopenharmony_ci ret = PTR_ERR(ao_cec->core); 6508c2ecf20Sopenharmony_ci goto out_probe_adapter; 6518c2ecf20Sopenharmony_ci } 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci ret = clk_prepare_enable(ao_cec->core); 6548c2ecf20Sopenharmony_ci if (ret) { 6558c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "core clock enable failed\n"); 6568c2ecf20Sopenharmony_ci goto out_probe_adapter; 6578c2ecf20Sopenharmony_ci } 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci ret = clk_set_rate(ao_cec->core, CEC_CLK_RATE); 6608c2ecf20Sopenharmony_ci if (ret) { 6618c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "core clock set rate failed\n"); 6628c2ecf20Sopenharmony_ci goto out_probe_clk; 6638c2ecf20Sopenharmony_ci } 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci device_reset_optional(&pdev->dev); 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci ao_cec->pdev = pdev; 6688c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, ao_cec); 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci ao_cec->notify = cec_notifier_cec_adap_register(hdmi_dev, NULL, 6718c2ecf20Sopenharmony_ci ao_cec->adap); 6728c2ecf20Sopenharmony_ci if (!ao_cec->notify) { 6738c2ecf20Sopenharmony_ci ret = -ENOMEM; 6748c2ecf20Sopenharmony_ci goto out_probe_clk; 6758c2ecf20Sopenharmony_ci } 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci ret = cec_register_adapter(ao_cec->adap, &pdev->dev); 6788c2ecf20Sopenharmony_ci if (ret < 0) 6798c2ecf20Sopenharmony_ci goto out_probe_notify; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci /* Setup Hardware */ 6828c2ecf20Sopenharmony_ci writel_relaxed(CEC_GEN_CNTL_RESET, 6838c2ecf20Sopenharmony_ci ao_cec->base + CEC_GEN_CNTL_REG); 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci return 0; 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ciout_probe_notify: 6888c2ecf20Sopenharmony_ci cec_notifier_cec_adap_unregister(ao_cec->notify, ao_cec->adap); 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ciout_probe_clk: 6918c2ecf20Sopenharmony_ci clk_disable_unprepare(ao_cec->core); 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ciout_probe_adapter: 6948c2ecf20Sopenharmony_ci cec_delete_adapter(ao_cec->adap); 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "CEC controller registration failed\n"); 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci return ret; 6998c2ecf20Sopenharmony_ci} 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_cistatic int meson_ao_cec_remove(struct platform_device *pdev) 7028c2ecf20Sopenharmony_ci{ 7038c2ecf20Sopenharmony_ci struct meson_ao_cec_device *ao_cec = platform_get_drvdata(pdev); 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci clk_disable_unprepare(ao_cec->core); 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci cec_notifier_cec_adap_unregister(ao_cec->notify, ao_cec->adap); 7088c2ecf20Sopenharmony_ci cec_unregister_adapter(ao_cec->adap); 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci return 0; 7118c2ecf20Sopenharmony_ci} 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_cistatic const struct of_device_id meson_ao_cec_of_match[] = { 7148c2ecf20Sopenharmony_ci { .compatible = "amlogic,meson-gx-ao-cec", }, 7158c2ecf20Sopenharmony_ci { /* sentinel */ } 7168c2ecf20Sopenharmony_ci}; 7178c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, meson_ao_cec_of_match); 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_cistatic struct platform_driver meson_ao_cec_driver = { 7208c2ecf20Sopenharmony_ci .probe = meson_ao_cec_probe, 7218c2ecf20Sopenharmony_ci .remove = meson_ao_cec_remove, 7228c2ecf20Sopenharmony_ci .driver = { 7238c2ecf20Sopenharmony_ci .name = "meson-ao-cec", 7248c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(meson_ao_cec_of_match), 7258c2ecf20Sopenharmony_ci }, 7268c2ecf20Sopenharmony_ci}; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_cimodule_platform_driver(meson_ao_cec_driver); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Meson AO CEC Controller driver"); 7318c2ecf20Sopenharmony_ciMODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); 7328c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 733