18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright 2015 Toradex AG
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Stefan Agner <stefan@agner.ch>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Freescale TCON device driver
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/clk.h>
118c2ecf20Sopenharmony_ci#include <linux/io.h>
128c2ecf20Sopenharmony_ci#include <linux/mm.h>
138c2ecf20Sopenharmony_ci#include <linux/of_address.h>
148c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
158c2ecf20Sopenharmony_ci#include <linux/regmap.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include "fsl_tcon.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_civoid fsl_tcon_bypass_disable(struct fsl_tcon *tcon)
208c2ecf20Sopenharmony_ci{
218c2ecf20Sopenharmony_ci	regmap_update_bits(tcon->regs, FSL_TCON_CTRL1,
228c2ecf20Sopenharmony_ci			   FSL_TCON_CTRL1_TCON_BYPASS, 0);
238c2ecf20Sopenharmony_ci}
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_civoid fsl_tcon_bypass_enable(struct fsl_tcon *tcon)
268c2ecf20Sopenharmony_ci{
278c2ecf20Sopenharmony_ci	regmap_update_bits(tcon->regs, FSL_TCON_CTRL1,
288c2ecf20Sopenharmony_ci			   FSL_TCON_CTRL1_TCON_BYPASS,
298c2ecf20Sopenharmony_ci			   FSL_TCON_CTRL1_TCON_BYPASS);
308c2ecf20Sopenharmony_ci}
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic struct regmap_config fsl_tcon_regmap_config = {
338c2ecf20Sopenharmony_ci	.reg_bits = 32,
348c2ecf20Sopenharmony_ci	.reg_stride = 4,
358c2ecf20Sopenharmony_ci	.val_bits = 32,
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	.name = "tcon",
388c2ecf20Sopenharmony_ci};
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic int fsl_tcon_init_regmap(struct device *dev,
418c2ecf20Sopenharmony_ci				struct fsl_tcon *tcon,
428c2ecf20Sopenharmony_ci				struct device_node *np)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	struct resource res;
458c2ecf20Sopenharmony_ci	void __iomem *regs;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	if (of_address_to_resource(np, 0, &res))
488c2ecf20Sopenharmony_ci		return -EINVAL;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	regs = devm_ioremap_resource(dev, &res);
518c2ecf20Sopenharmony_ci	if (IS_ERR(regs))
528c2ecf20Sopenharmony_ci		return PTR_ERR(regs);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	tcon->regs = devm_regmap_init_mmio(dev, regs,
558c2ecf20Sopenharmony_ci					   &fsl_tcon_regmap_config);
568c2ecf20Sopenharmony_ci	return PTR_ERR_OR_ZERO(tcon->regs);
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistruct fsl_tcon *fsl_tcon_init(struct device *dev)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	struct fsl_tcon *tcon;
628c2ecf20Sopenharmony_ci	struct device_node *np;
638c2ecf20Sopenharmony_ci	int ret;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	/* TCON node is not mandatory, some devices do not provide TCON */
668c2ecf20Sopenharmony_ci	np = of_parse_phandle(dev->of_node, "fsl,tcon", 0);
678c2ecf20Sopenharmony_ci	if (!np)
688c2ecf20Sopenharmony_ci		return NULL;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	tcon = devm_kzalloc(dev, sizeof(*tcon), GFP_KERNEL);
718c2ecf20Sopenharmony_ci	if (!tcon)
728c2ecf20Sopenharmony_ci		goto err_node_put;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	ret = fsl_tcon_init_regmap(dev, tcon, np);
758c2ecf20Sopenharmony_ci	if (ret) {
768c2ecf20Sopenharmony_ci		dev_err(dev, "Couldn't create the TCON regmap\n");
778c2ecf20Sopenharmony_ci		goto err_node_put;
788c2ecf20Sopenharmony_ci	}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	tcon->ipg_clk = of_clk_get_by_name(np, "ipg");
818c2ecf20Sopenharmony_ci	if (IS_ERR(tcon->ipg_clk)) {
828c2ecf20Sopenharmony_ci		dev_err(dev, "Couldn't get the TCON bus clock\n");
838c2ecf20Sopenharmony_ci		goto err_node_put;
848c2ecf20Sopenharmony_ci	}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(tcon->ipg_clk);
878c2ecf20Sopenharmony_ci	if (ret) {
888c2ecf20Sopenharmony_ci		dev_err(dev, "Couldn't enable the TCON clock\n");
898c2ecf20Sopenharmony_ci		goto err_node_put;
908c2ecf20Sopenharmony_ci	}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	of_node_put(np);
938c2ecf20Sopenharmony_ci	dev_info(dev, "Using TCON in bypass mode\n");
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	return tcon;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cierr_node_put:
988c2ecf20Sopenharmony_ci	of_node_put(np);
998c2ecf20Sopenharmony_ci	return NULL;
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_civoid fsl_tcon_free(struct fsl_tcon *tcon)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	clk_disable_unprepare(tcon->ipg_clk);
1058c2ecf20Sopenharmony_ci	clk_put(tcon->ipg_clk);
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
108