brintos

brintos / linux-shallow public Read only

0
0
Text · 20.8 KiB · 51294b9 Raw
793 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Driver for Amlogic Meson AO CEC G12A Controller4 *5 * Copyright (C) 2017 Amlogic, Inc. All rights reserved6 * Copyright (C) 2019 BayLibre, SAS7 * Author: Neil Armstrong <narmstrong@baylibre.com>8 */9 10#include <linux/bitfield.h>11#include <linux/clk.h>12#include <linux/device.h>13#include <linux/io.h>14#include <linux/delay.h>15#include <linux/kernel.h>16#include <linux/module.h>17#include <linux/of.h>18#include <linux/of_platform.h>19#include <linux/platform_device.h>20#include <linux/types.h>21#include <linux/interrupt.h>22#include <linux/reset.h>23#include <linux/slab.h>24#include <linux/regmap.h>25#include <media/cec.h>26#include <media/cec-notifier.h>27#include <linux/clk-provider.h>28 29/* CEC Registers */30 31#define CECB_CLK_CNTL_REG0		0x0032 33#define CECB_CLK_CNTL_N1		GENMASK(11, 0)34#define CECB_CLK_CNTL_N2		GENMASK(23, 12)35#define CECB_CLK_CNTL_DUAL_EN		BIT(28)36#define CECB_CLK_CNTL_OUTPUT_EN		BIT(30)37#define CECB_CLK_CNTL_INPUT_EN		BIT(31)38 39#define CECB_CLK_CNTL_REG1		0x0440 41#define CECB_CLK_CNTL_M1		GENMASK(11, 0)42#define CECB_CLK_CNTL_M2		GENMASK(23, 12)43#define CECB_CLK_CNTL_BYPASS_EN		BIT(24)44 45/*46 * [14:12] Filter_del. For glitch-filtering CEC line, ignore signal47 *       change pulse width < filter_del * T(filter_tick) * 3.48 * [9:8] Filter_tick_sel: Select which periodical pulse for49 *       glitch-filtering CEC line signal.50 *  - 0=Use T(xtal)*3 = 125ns;51 *  - 1=Use once-per-1us pulse;52 *  - 2=Use once-per-10us pulse;53 *  - 3=Use once-per-100us pulse.54 * [3]   Sysclk_en. 0=Disable system clock; 1=Enable system clock.55 * [2:1] cntl_clk56 *  - 0 = Disable clk (Power-off mode)57 *  - 1 = Enable gated clock (Normal mode)58 *  - 2 = Enable free-run clk (Debug mode)59 * [0] SW_RESET 1=Apply reset; 0=No reset.60 */61#define CECB_GEN_CNTL_REG		0x0862 63#define CECB_GEN_CNTL_RESET		BIT(0)64#define CECB_GEN_CNTL_CLK_DISABLE	065#define CECB_GEN_CNTL_CLK_ENABLE	166#define CECB_GEN_CNTL_CLK_ENABLE_DBG	267#define CECB_GEN_CNTL_CLK_CTRL_MASK	GENMASK(2, 1)68#define CECB_GEN_CNTL_SYS_CLK_EN	BIT(3)69#define CECB_GEN_CNTL_FILTER_TICK_125NS	070#define CECB_GEN_CNTL_FILTER_TICK_1US	171#define CECB_GEN_CNTL_FILTER_TICK_10US	272#define CECB_GEN_CNTL_FILTER_TICK_100US	373#define CECB_GEN_CNTL_FILTER_TICK_SEL	GENMASK(9, 8)74#define CECB_GEN_CNTL_FILTER_DEL	GENMASK(14, 12)75 76/*77 * [7:0] cec_reg_addr78 * [15:8] cec_reg_wrdata79 * [16] cec_reg_wr80 *  - 0 = Read81 *  - 1 = Write82 * [31:24] cec_reg_rddata83 */84#define CECB_RW_REG			0x0c85 86#define CECB_RW_ADDR			GENMASK(7, 0)87#define CECB_RW_WR_DATA			GENMASK(15, 8)88#define CECB_RW_WRITE_EN		BIT(16)89#define CECB_RW_BUS_BUSY		BIT(23)90#define CECB_RW_RD_DATA			GENMASK(31, 24)91 92/*93 * [0] DONE Interrupt94 * [1] End Of Message Interrupt95 * [2] Not Acknowlegde Interrupt96 * [3] Arbitration Loss Interrupt97 * [4] Initiator Error Interrupt98 * [5] Follower Error Interrupt99 * [6] Wake-Up Interrupt100 */101#define CECB_INTR_MASKN_REG		0x10102#define CECB_INTR_CLR_REG		0x14103#define CECB_INTR_STAT_REG		0x18104 105#define CECB_INTR_DONE			BIT(0)106#define CECB_INTR_EOM			BIT(1)107#define CECB_INTR_NACK			BIT(2)108#define CECB_INTR_ARB_LOSS		BIT(3)109#define CECB_INTR_INITIATOR_ERR		BIT(4)110#define CECB_INTR_FOLLOWER_ERR		BIT(5)111#define CECB_INTR_WAKE_UP		BIT(6)112 113/* CEC Commands */114 115#define CECB_CTRL		0x00116 117#define CECB_CTRL_SEND		BIT(0)118#define CECB_CTRL_TYPE		GENMASK(2, 1)119#define CECB_CTRL_TYPE_RETRY	0120#define CECB_CTRL_TYPE_NEW	1121#define CECB_CTRL_TYPE_NEXT	2122 123#define CECB_CTRL2		0x01124 125#define CECB_CTRL2_RISE_DEL_MAX	GENMASK(4, 0)126 127#define CECB_INTR_MASK		0x02128#define CECB_LADD_LOW		0x05129#define CECB_LADD_HIGH		0x06130#define CECB_TX_CNT		0x07131#define CECB_RX_CNT		0x08132#define CECB_STAT0		0x09133#define CECB_TX_DATA00		0x10134#define CECB_TX_DATA01		0x11135#define CECB_TX_DATA02		0x12136#define CECB_TX_DATA03		0x13137#define CECB_TX_DATA04		0x14138#define CECB_TX_DATA05		0x15139#define CECB_TX_DATA06		0x16140#define CECB_TX_DATA07		0x17141#define CECB_TX_DATA08		0x18142#define CECB_TX_DATA09		0x19143#define CECB_TX_DATA10		0x1A144#define CECB_TX_DATA11		0x1B145#define CECB_TX_DATA12		0x1C146#define CECB_TX_DATA13		0x1D147#define CECB_TX_DATA14		0x1E148#define CECB_TX_DATA15		0x1F149#define CECB_RX_DATA00		0x20150#define CECB_RX_DATA01		0x21151#define CECB_RX_DATA02		0x22152#define CECB_RX_DATA03		0x23153#define CECB_RX_DATA04		0x24154#define CECB_RX_DATA05		0x25155#define CECB_RX_DATA06		0x26156#define CECB_RX_DATA07		0x27157#define CECB_RX_DATA08		0x28158#define CECB_RX_DATA09		0x29159#define CECB_RX_DATA10		0x2A160#define CECB_RX_DATA11		0x2B161#define CECB_RX_DATA12		0x2C162#define CECB_RX_DATA13		0x2D163#define CECB_RX_DATA14		0x2E164#define CECB_RX_DATA15		0x2F165#define CECB_LOCK_BUF		0x30166 167#define CECB_LOCK_BUF_EN	BIT(0)168 169#define CECB_WAKEUPCTRL		0x31170 171struct meson_ao_cec_g12a_data {172	/* Setup the internal CECB_CTRL2 register */173	bool				ctrl2_setup;174};175 176struct meson_ao_cec_g12a_device {177	struct platform_device		*pdev;178	struct regmap			*regmap;179	struct regmap			*regmap_cec;180	spinlock_t			cec_reg_lock;181	struct cec_notifier		*notify;182	struct cec_adapter		*adap;183	struct cec_msg			rx_msg;184	struct clk			*oscin;185	struct clk			*core;186	const struct meson_ao_cec_g12a_data *data;187};188 189static const struct regmap_config meson_ao_cec_g12a_regmap_conf = {190	.reg_bits = 8,191	.val_bits = 32,192	.reg_stride = 4,193	.max_register = CECB_INTR_STAT_REG,194};195 196/*197 * The AO-CECB embeds a dual/divider to generate a more precise198 * 32,768KHz clock for CEC core clock.199 *                      ______   ______200 *                     |      | |      |201 *         ______      | Div1 |-| Cnt1 |       ______202 *        |      |    /|______| |______|\     |      |203 * Xtal-->| Gate |---|  ______   ______  X-X--| Gate |-->204 *        |______| |  \|      | |      |/  |  |______|205 *                 |   | Div2 |-| Cnt2 |   |206 *                 |   |______| |______|   |207 *                 |_______________________|208 *209 * The dividing can be switched to single or dual, with a counter210 * for each divider to set when the switching is done.211 * The entire dividing mechanism can be also bypassed.212 */213 214struct meson_ao_cec_g12a_dualdiv_clk {215	struct clk_hw hw;216	struct regmap *regmap;217};218 219#define hw_to_meson_ao_cec_g12a_dualdiv_clk(_hw)			\220	container_of(_hw, struct meson_ao_cec_g12a_dualdiv_clk, hw)	\221 222static unsigned long223meson_ao_cec_g12a_dualdiv_clk_recalc_rate(struct clk_hw *hw,224					  unsigned long parent_rate)225{226	struct meson_ao_cec_g12a_dualdiv_clk *dualdiv_clk =227		hw_to_meson_ao_cec_g12a_dualdiv_clk(hw);228	unsigned long n1;229	u32 reg0, reg1;230 231	regmap_read(dualdiv_clk->regmap, CECB_CLK_CNTL_REG0, &reg0);232	regmap_read(dualdiv_clk->regmap, CECB_CLK_CNTL_REG0, &reg1);233 234	if (reg1 & CECB_CLK_CNTL_BYPASS_EN)235		return parent_rate;236 237	if (reg0 & CECB_CLK_CNTL_DUAL_EN) {238		unsigned long n2, m1, m2, f1, f2, p1, p2;239 240		n1 = FIELD_GET(CECB_CLK_CNTL_N1, reg0) + 1;241		n2 = FIELD_GET(CECB_CLK_CNTL_N2, reg0) + 1;242 243		m1 = FIELD_GET(CECB_CLK_CNTL_M1, reg1) + 1;244		m2 = FIELD_GET(CECB_CLK_CNTL_M1, reg1) + 1;245 246		f1 = DIV_ROUND_CLOSEST(parent_rate, n1);247		f2 = DIV_ROUND_CLOSEST(parent_rate, n2);248 249		p1 = DIV_ROUND_CLOSEST(100000000 * m1, f1 * (m1 + m2));250		p2 = DIV_ROUND_CLOSEST(100000000 * m2, f2 * (m1 + m2));251 252		return DIV_ROUND_UP(100000000, p1 + p2);253	}254 255	n1 = FIELD_GET(CECB_CLK_CNTL_N1, reg0) + 1;256 257	return DIV_ROUND_CLOSEST(parent_rate, n1);258}259 260static int meson_ao_cec_g12a_dualdiv_clk_enable(struct clk_hw *hw)261{262	struct meson_ao_cec_g12a_dualdiv_clk *dualdiv_clk =263		hw_to_meson_ao_cec_g12a_dualdiv_clk(hw);264 265 266	/* Disable Input & Output */267	regmap_update_bits(dualdiv_clk->regmap, CECB_CLK_CNTL_REG0,268			   CECB_CLK_CNTL_INPUT_EN | CECB_CLK_CNTL_OUTPUT_EN,269			   0);270 271	/* Set N1 & N2 */272	regmap_update_bits(dualdiv_clk->regmap, CECB_CLK_CNTL_REG0,273			   CECB_CLK_CNTL_N1,274			   FIELD_PREP(CECB_CLK_CNTL_N1, 733 - 1));275 276	regmap_update_bits(dualdiv_clk->regmap, CECB_CLK_CNTL_REG0,277			   CECB_CLK_CNTL_N2,278			   FIELD_PREP(CECB_CLK_CNTL_N2, 732 - 1));279 280	/* Set M1 & M2 */281	regmap_update_bits(dualdiv_clk->regmap, CECB_CLK_CNTL_REG1,282			   CECB_CLK_CNTL_M1,283			   FIELD_PREP(CECB_CLK_CNTL_M1, 8 - 1));284 285	regmap_update_bits(dualdiv_clk->regmap, CECB_CLK_CNTL_REG1,286			   CECB_CLK_CNTL_M2,287			   FIELD_PREP(CECB_CLK_CNTL_M2, 11 - 1));288 289	/* Enable Dual divisor */290	regmap_update_bits(dualdiv_clk->regmap, CECB_CLK_CNTL_REG0,291			   CECB_CLK_CNTL_DUAL_EN, CECB_CLK_CNTL_DUAL_EN);292 293	/* Disable divisor bypass */294	regmap_update_bits(dualdiv_clk->regmap, CECB_CLK_CNTL_REG1,295			   CECB_CLK_CNTL_BYPASS_EN, 0);296 297	/* Enable Input & Output */298	regmap_update_bits(dualdiv_clk->regmap, CECB_CLK_CNTL_REG0,299			   CECB_CLK_CNTL_INPUT_EN | CECB_CLK_CNTL_OUTPUT_EN,300			   CECB_CLK_CNTL_INPUT_EN | CECB_CLK_CNTL_OUTPUT_EN);301 302	return 0;303}304 305static void meson_ao_cec_g12a_dualdiv_clk_disable(struct clk_hw *hw)306{307	struct meson_ao_cec_g12a_dualdiv_clk *dualdiv_clk =308		hw_to_meson_ao_cec_g12a_dualdiv_clk(hw);309 310	regmap_update_bits(dualdiv_clk->regmap, CECB_CLK_CNTL_REG0,311			   CECB_CLK_CNTL_INPUT_EN | CECB_CLK_CNTL_OUTPUT_EN,312			   0);313}314 315static int meson_ao_cec_g12a_dualdiv_clk_is_enabled(struct clk_hw *hw)316{317	struct meson_ao_cec_g12a_dualdiv_clk *dualdiv_clk =318		hw_to_meson_ao_cec_g12a_dualdiv_clk(hw);319	int val;320 321	regmap_read(dualdiv_clk->regmap, CECB_CLK_CNTL_REG0, &val);322 323	return !!(val & (CECB_CLK_CNTL_INPUT_EN | CECB_CLK_CNTL_OUTPUT_EN));324}325 326static const struct clk_ops meson_ao_cec_g12a_dualdiv_clk_ops = {327	.recalc_rate	= meson_ao_cec_g12a_dualdiv_clk_recalc_rate,328	.is_enabled	= meson_ao_cec_g12a_dualdiv_clk_is_enabled,329	.enable		= meson_ao_cec_g12a_dualdiv_clk_enable,330	.disable	= meson_ao_cec_g12a_dualdiv_clk_disable,331};332 333static int meson_ao_cec_g12a_setup_clk(struct meson_ao_cec_g12a_device *ao_cec)334{335	struct meson_ao_cec_g12a_dualdiv_clk *dualdiv_clk;336	struct device *dev = &ao_cec->pdev->dev;337	struct clk_init_data init;338	const char *parent_name;339	struct clk *clk;340	char *name;341 342	dualdiv_clk = devm_kzalloc(dev, sizeof(*dualdiv_clk), GFP_KERNEL);343	if (!dualdiv_clk)344		return -ENOMEM;345 346	name = kasprintf(GFP_KERNEL, "%s#dualdiv_clk", dev_name(dev));347	if (!name)348		return -ENOMEM;349 350	parent_name = __clk_get_name(ao_cec->oscin);351 352	init.name = name;353	init.ops = &meson_ao_cec_g12a_dualdiv_clk_ops;354	init.flags = 0;355	init.parent_names = &parent_name;356	init.num_parents = 1;357	dualdiv_clk->regmap = ao_cec->regmap;358	dualdiv_clk->hw.init = &init;359 360	clk = devm_clk_register(dev, &dualdiv_clk->hw);361	kfree(name);362	if (IS_ERR(clk)) {363		dev_err(dev, "failed to register clock\n");364		return PTR_ERR(clk);365	}366 367	ao_cec->core = clk;368 369	return 0;370}371 372static int meson_ao_cec_g12a_read(void *context, unsigned int addr,373				  unsigned int *data)374{375	struct meson_ao_cec_g12a_device *ao_cec = context;376	u32 reg = FIELD_PREP(CECB_RW_ADDR, addr);377	int ret = 0;378 379	ret = regmap_write(ao_cec->regmap, CECB_RW_REG, reg);380	if (ret)381		return ret;382 383	ret = regmap_read_poll_timeout(ao_cec->regmap, CECB_RW_REG, reg,384				       !(reg & CECB_RW_BUS_BUSY),385				       5, 1000);386	if (ret)387		return ret;388 389	ret = regmap_read(ao_cec->regmap, CECB_RW_REG, &reg);390 391	*data = FIELD_GET(CECB_RW_RD_DATA, reg);392 393	return ret;394}395 396static int meson_ao_cec_g12a_write(void *context, unsigned int addr,397				   unsigned int data)398{399	struct meson_ao_cec_g12a_device *ao_cec = context;400	u32 reg = FIELD_PREP(CECB_RW_ADDR, addr) |401		  FIELD_PREP(CECB_RW_WR_DATA, data) |402		  CECB_RW_WRITE_EN;403 404	return regmap_write(ao_cec->regmap, CECB_RW_REG, reg);405}406 407static const struct regmap_config meson_ao_cec_g12a_cec_regmap_conf = {408	.reg_bits = 8,409	.val_bits = 8,410	.reg_read = meson_ao_cec_g12a_read,411	.reg_write = meson_ao_cec_g12a_write,412	.max_register = 0xffff,413};414 415static inline void416meson_ao_cec_g12a_irq_setup(struct meson_ao_cec_g12a_device *ao_cec,417			    bool enable)418{419	u32 cfg = CECB_INTR_DONE | CECB_INTR_EOM | CECB_INTR_NACK |420		  CECB_INTR_ARB_LOSS | CECB_INTR_INITIATOR_ERR |421		  CECB_INTR_FOLLOWER_ERR;422 423	regmap_write(ao_cec->regmap, CECB_INTR_MASKN_REG,424		     enable ? cfg : 0);425}426 427static void meson_ao_cec_g12a_irq_rx(struct meson_ao_cec_g12a_device *ao_cec)428{429	int i, ret = 0;430	u32 val;431 432	ret = regmap_read(ao_cec->regmap_cec, CECB_RX_CNT, &val);433 434	ao_cec->rx_msg.len = val;435	if (ao_cec->rx_msg.len > CEC_MAX_MSG_SIZE)436		ao_cec->rx_msg.len = CEC_MAX_MSG_SIZE;437 438	for (i = 0; i < ao_cec->rx_msg.len; i++) {439		ret |= regmap_read(ao_cec->regmap_cec,440				   CECB_RX_DATA00 + i, &val);441 442		ao_cec->rx_msg.msg[i] = val & 0xff;443	}444 445	ret |= regmap_write(ao_cec->regmap_cec, CECB_LOCK_BUF, 0);446	if (ret)447		return;448 449	cec_received_msg(ao_cec->adap, &ao_cec->rx_msg);450}451 452static irqreturn_t meson_ao_cec_g12a_irq(int irq, void *data)453{454	struct meson_ao_cec_g12a_device *ao_cec = data;455	u32 stat;456 457	regmap_read(ao_cec->regmap, CECB_INTR_STAT_REG, &stat);458	if (stat)459		return IRQ_WAKE_THREAD;460 461	return IRQ_NONE;462}463 464static irqreturn_t meson_ao_cec_g12a_irq_thread(int irq, void *data)465{466	struct meson_ao_cec_g12a_device *ao_cec = data;467	u32 stat;468 469	regmap_read(ao_cec->regmap, CECB_INTR_STAT_REG, &stat);470	regmap_write(ao_cec->regmap, CECB_INTR_CLR_REG, stat);471 472	if (stat & CECB_INTR_DONE)473		cec_transmit_attempt_done(ao_cec->adap, CEC_TX_STATUS_OK);474 475	if (stat & CECB_INTR_EOM)476		meson_ao_cec_g12a_irq_rx(ao_cec);477 478	if (stat & CECB_INTR_NACK)479		cec_transmit_attempt_done(ao_cec->adap, CEC_TX_STATUS_NACK);480 481	if (stat & CECB_INTR_ARB_LOSS) {482		regmap_write(ao_cec->regmap_cec, CECB_TX_CNT, 0);483		regmap_update_bits(ao_cec->regmap_cec, CECB_CTRL,484				   CECB_CTRL_SEND | CECB_CTRL_TYPE, 0);485		cec_transmit_attempt_done(ao_cec->adap, CEC_TX_STATUS_ARB_LOST);486	}487 488	/* Initiator reports an error on the CEC bus */489	if (stat & CECB_INTR_INITIATOR_ERR)490		cec_transmit_attempt_done(ao_cec->adap, CEC_TX_STATUS_ERROR);491 492	/* Follower reports a receive error, just reset RX buffer */493	if (stat & CECB_INTR_FOLLOWER_ERR)494		regmap_write(ao_cec->regmap_cec, CECB_LOCK_BUF, 0);495 496	return IRQ_HANDLED;497}498 499static int500meson_ao_cec_g12a_set_log_addr(struct cec_adapter *adap, u8 logical_addr)501{502	struct meson_ao_cec_g12a_device *ao_cec = adap->priv;503	int ret = 0;504 505	if (logical_addr == CEC_LOG_ADDR_INVALID) {506		/* Assume this will allways succeed */507		regmap_write(ao_cec->regmap_cec, CECB_LADD_LOW, 0);508		regmap_write(ao_cec->regmap_cec, CECB_LADD_HIGH, 0);509 510		return 0;511	} else if (logical_addr < 8) {512		ret = regmap_update_bits(ao_cec->regmap_cec, CECB_LADD_LOW,513					 BIT(logical_addr),514					 BIT(logical_addr));515	} else {516		ret = regmap_update_bits(ao_cec->regmap_cec, CECB_LADD_HIGH,517					 BIT(logical_addr - 8),518					 BIT(logical_addr - 8));519	}520 521	/* Always set Broadcast/Unregistered 15 address */522	ret |= regmap_update_bits(ao_cec->regmap_cec, CECB_LADD_HIGH,523				  BIT(CEC_LOG_ADDR_UNREGISTERED - 8),524				  BIT(CEC_LOG_ADDR_UNREGISTERED - 8));525 526	return ret ? -EIO : 0;527}528 529static int meson_ao_cec_g12a_transmit(struct cec_adapter *adap, u8 attempts,530				 u32 signal_free_time, struct cec_msg *msg)531{532	struct meson_ao_cec_g12a_device *ao_cec = adap->priv;533	unsigned int type;534	int ret = 0;535	u32 val;536	int i;537 538	/* Check if RX is in progress */539	ret = regmap_read(ao_cec->regmap_cec, CECB_LOCK_BUF, &val);540	if (ret)541		return ret;542	if (val & CECB_LOCK_BUF_EN)543		return -EBUSY;544 545	/* Check if TX Busy */546	ret = regmap_read(ao_cec->regmap_cec, CECB_CTRL, &val);547	if (ret)548		return ret;549	if (val & CECB_CTRL_SEND)550		return -EBUSY;551 552	switch (signal_free_time) {553	case CEC_SIGNAL_FREE_TIME_RETRY:554		type = CECB_CTRL_TYPE_RETRY;555		break;556	case CEC_SIGNAL_FREE_TIME_NEXT_XFER:557		type = CECB_CTRL_TYPE_NEXT;558		break;559	case CEC_SIGNAL_FREE_TIME_NEW_INITIATOR:560	default:561		type = CECB_CTRL_TYPE_NEW;562		break;563	}564 565	for (i = 0; i < msg->len; i++)566		ret |= regmap_write(ao_cec->regmap_cec, CECB_TX_DATA00 + i,567				    msg->msg[i]);568 569	ret |= regmap_write(ao_cec->regmap_cec, CECB_TX_CNT, msg->len);570	if (ret)571		return -EIO;572 573	ret = regmap_update_bits(ao_cec->regmap_cec, CECB_CTRL,574				 CECB_CTRL_SEND |575				 CECB_CTRL_TYPE,576				 CECB_CTRL_SEND |577				 FIELD_PREP(CECB_CTRL_TYPE, type));578 579	return ret;580}581 582static int meson_ao_cec_g12a_adap_enable(struct cec_adapter *adap, bool enable)583{584	struct meson_ao_cec_g12a_device *ao_cec = adap->priv;585 586	meson_ao_cec_g12a_irq_setup(ao_cec, false);587 588	regmap_update_bits(ao_cec->regmap, CECB_GEN_CNTL_REG,589			   CECB_GEN_CNTL_RESET, CECB_GEN_CNTL_RESET);590 591	if (!enable)592		return 0;593 594	/* Setup Filter */595	regmap_update_bits(ao_cec->regmap, CECB_GEN_CNTL_REG,596			   CECB_GEN_CNTL_FILTER_TICK_SEL |597			   CECB_GEN_CNTL_FILTER_DEL,598			   FIELD_PREP(CECB_GEN_CNTL_FILTER_TICK_SEL,599				      CECB_GEN_CNTL_FILTER_TICK_1US) |600			   FIELD_PREP(CECB_GEN_CNTL_FILTER_DEL, 7));601 602	/* Enable System Clock */603	regmap_update_bits(ao_cec->regmap, CECB_GEN_CNTL_REG,604			   CECB_GEN_CNTL_SYS_CLK_EN,605			   CECB_GEN_CNTL_SYS_CLK_EN);606 607	/* Enable gated clock (Normal mode). */608	regmap_update_bits(ao_cec->regmap, CECB_GEN_CNTL_REG,609			   CECB_GEN_CNTL_CLK_CTRL_MASK,610			    FIELD_PREP(CECB_GEN_CNTL_CLK_CTRL_MASK,611				       CECB_GEN_CNTL_CLK_ENABLE));612 613	/* Release Reset */614	regmap_update_bits(ao_cec->regmap, CECB_GEN_CNTL_REG,615			   CECB_GEN_CNTL_RESET, 0);616 617	if (ao_cec->data->ctrl2_setup)618		regmap_write(ao_cec->regmap_cec, CECB_CTRL2,619			     FIELD_PREP(CECB_CTRL2_RISE_DEL_MAX, 2));620 621	meson_ao_cec_g12a_irq_setup(ao_cec, true);622 623	return 0;624}625 626static const struct cec_adap_ops meson_ao_cec_g12a_ops = {627	.adap_enable = meson_ao_cec_g12a_adap_enable,628	.adap_log_addr = meson_ao_cec_g12a_set_log_addr,629	.adap_transmit = meson_ao_cec_g12a_transmit,630};631 632static int meson_ao_cec_g12a_probe(struct platform_device *pdev)633{634	struct meson_ao_cec_g12a_device *ao_cec;635	struct device *hdmi_dev;636	void __iomem *base;637	int ret, irq;638 639	hdmi_dev = cec_notifier_parse_hdmi_phandle(&pdev->dev);640	if (IS_ERR(hdmi_dev))641		return PTR_ERR(hdmi_dev);642 643	ao_cec = devm_kzalloc(&pdev->dev, sizeof(*ao_cec), GFP_KERNEL);644	if (!ao_cec)645		return -ENOMEM;646 647	ao_cec->data = of_device_get_match_data(&pdev->dev);648	if (!ao_cec->data) {649		dev_err(&pdev->dev, "failed to get match data\n");650		return -ENODEV;651	}652 653	spin_lock_init(&ao_cec->cec_reg_lock);654	ao_cec->pdev = pdev;655 656	ao_cec->adap = cec_allocate_adapter(&meson_ao_cec_g12a_ops, ao_cec,657					    "meson_g12a_ao_cec",658					    CEC_CAP_DEFAULTS |659					    CEC_CAP_CONNECTOR_INFO,660					    CEC_MAX_LOG_ADDRS);661	if (IS_ERR(ao_cec->adap))662		return PTR_ERR(ao_cec->adap);663 664	ao_cec->adap->owner = THIS_MODULE;665 666	base = devm_platform_ioremap_resource(pdev, 0);667	if (IS_ERR(base)) {668		ret = PTR_ERR(base);669		goto out_probe_adapter;670	}671 672	ao_cec->regmap = devm_regmap_init_mmio(&pdev->dev, base,673					       &meson_ao_cec_g12a_regmap_conf);674	if (IS_ERR(ao_cec->regmap)) {675		ret = PTR_ERR(ao_cec->regmap);676		goto out_probe_adapter;677	}678 679	ao_cec->regmap_cec = devm_regmap_init(&pdev->dev, NULL, ao_cec,680					   &meson_ao_cec_g12a_cec_regmap_conf);681	if (IS_ERR(ao_cec->regmap_cec)) {682		ret = PTR_ERR(ao_cec->regmap_cec);683		goto out_probe_adapter;684	}685 686	irq = platform_get_irq(pdev, 0);687	ret = devm_request_threaded_irq(&pdev->dev, irq,688					meson_ao_cec_g12a_irq,689					meson_ao_cec_g12a_irq_thread,690					0, NULL, ao_cec);691	if (ret) {692		dev_err(&pdev->dev, "irq request failed\n");693		goto out_probe_adapter;694	}695 696	ao_cec->oscin = devm_clk_get(&pdev->dev, "oscin");697	if (IS_ERR(ao_cec->oscin)) {698		dev_err(&pdev->dev, "oscin clock request failed\n");699		ret = PTR_ERR(ao_cec->oscin);700		goto out_probe_adapter;701	}702 703	ret = meson_ao_cec_g12a_setup_clk(ao_cec);704	if (ret)705		goto out_probe_adapter;706 707	ret = clk_prepare_enable(ao_cec->core);708	if (ret) {709		dev_err(&pdev->dev, "core clock enable failed\n");710		goto out_probe_adapter;711	}712 713	device_reset_optional(&pdev->dev);714 715	platform_set_drvdata(pdev, ao_cec);716 717	ao_cec->notify = cec_notifier_cec_adap_register(hdmi_dev, NULL,718							ao_cec->adap);719	if (!ao_cec->notify) {720		ret = -ENOMEM;721		goto out_probe_core_clk;722	}723 724	ret = cec_register_adapter(ao_cec->adap, &pdev->dev);725	if (ret < 0)726		goto out_probe_notify;727 728	/* Setup Hardware */729	regmap_write(ao_cec->regmap, CECB_GEN_CNTL_REG, CECB_GEN_CNTL_RESET);730 731	return 0;732 733out_probe_notify:734	cec_notifier_cec_adap_unregister(ao_cec->notify, ao_cec->adap);735 736out_probe_core_clk:737	clk_disable_unprepare(ao_cec->core);738 739out_probe_adapter:740	cec_delete_adapter(ao_cec->adap);741 742	dev_err(&pdev->dev, "CEC controller registration failed\n");743 744	return ret;745}746 747static void meson_ao_cec_g12a_remove(struct platform_device *pdev)748{749	struct meson_ao_cec_g12a_device *ao_cec = platform_get_drvdata(pdev);750 751	clk_disable_unprepare(ao_cec->core);752 753	cec_notifier_cec_adap_unregister(ao_cec->notify, ao_cec->adap);754 755	cec_unregister_adapter(ao_cec->adap);756}757 758static const struct meson_ao_cec_g12a_data ao_cec_g12a_data = {759	.ctrl2_setup = false,760};761 762static const struct meson_ao_cec_g12a_data ao_cec_sm1_data = {763	.ctrl2_setup = true,764};765 766static const struct of_device_id meson_ao_cec_g12a_of_match[] = {767	{768		.compatible = "amlogic,meson-g12a-ao-cec",769		.data = &ao_cec_g12a_data,770	},771	{772		.compatible = "amlogic,meson-sm1-ao-cec",773		.data = &ao_cec_sm1_data,774	},775	{ /* sentinel */ }776};777MODULE_DEVICE_TABLE(of, meson_ao_cec_g12a_of_match);778 779static struct platform_driver meson_ao_cec_g12a_driver = {780	.probe   = meson_ao_cec_g12a_probe,781	.remove_new = meson_ao_cec_g12a_remove,782	.driver  = {783		.name = "meson-ao-cec-g12a",784		.of_match_table = of_match_ptr(meson_ao_cec_g12a_of_match),785	},786};787 788module_platform_driver(meson_ao_cec_g12a_driver);789 790MODULE_DESCRIPTION("Meson AO CEC G12A Controller driver");791MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");792MODULE_LICENSE("GPL");793