162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright 2015 Toradex AG 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Stefan Agner <stefan@agner.ch> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Freescale TCON device driver 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/clk.h> 1162306a36Sopenharmony_ci#include <linux/io.h> 1262306a36Sopenharmony_ci#include <linux/mm.h> 1362306a36Sopenharmony_ci#include <linux/of_address.h> 1462306a36Sopenharmony_ci#include <linux/platform_device.h> 1562306a36Sopenharmony_ci#include <linux/regmap.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "fsl_tcon.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_civoid fsl_tcon_bypass_disable(struct fsl_tcon *tcon) 2062306a36Sopenharmony_ci{ 2162306a36Sopenharmony_ci regmap_update_bits(tcon->regs, FSL_TCON_CTRL1, 2262306a36Sopenharmony_ci FSL_TCON_CTRL1_TCON_BYPASS, 0); 2362306a36Sopenharmony_ci} 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_civoid fsl_tcon_bypass_enable(struct fsl_tcon *tcon) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci regmap_update_bits(tcon->regs, FSL_TCON_CTRL1, 2862306a36Sopenharmony_ci FSL_TCON_CTRL1_TCON_BYPASS, 2962306a36Sopenharmony_ci FSL_TCON_CTRL1_TCON_BYPASS); 3062306a36Sopenharmony_ci} 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic struct regmap_config fsl_tcon_regmap_config = { 3362306a36Sopenharmony_ci .reg_bits = 32, 3462306a36Sopenharmony_ci .reg_stride = 4, 3562306a36Sopenharmony_ci .val_bits = 32, 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci .name = "tcon", 3862306a36Sopenharmony_ci}; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic int fsl_tcon_init_regmap(struct device *dev, 4162306a36Sopenharmony_ci struct fsl_tcon *tcon, 4262306a36Sopenharmony_ci struct device_node *np) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci struct resource res; 4562306a36Sopenharmony_ci void __iomem *regs; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci if (of_address_to_resource(np, 0, &res)) 4862306a36Sopenharmony_ci return -EINVAL; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci regs = devm_ioremap_resource(dev, &res); 5162306a36Sopenharmony_ci if (IS_ERR(regs)) 5262306a36Sopenharmony_ci return PTR_ERR(regs); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci tcon->regs = devm_regmap_init_mmio(dev, regs, 5562306a36Sopenharmony_ci &fsl_tcon_regmap_config); 5662306a36Sopenharmony_ci return PTR_ERR_OR_ZERO(tcon->regs); 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistruct fsl_tcon *fsl_tcon_init(struct device *dev) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci struct fsl_tcon *tcon; 6262306a36Sopenharmony_ci struct device_node *np; 6362306a36Sopenharmony_ci int ret; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci /* TCON node is not mandatory, some devices do not provide TCON */ 6662306a36Sopenharmony_ci np = of_parse_phandle(dev->of_node, "fsl,tcon", 0); 6762306a36Sopenharmony_ci if (!np) 6862306a36Sopenharmony_ci return NULL; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci tcon = devm_kzalloc(dev, sizeof(*tcon), GFP_KERNEL); 7162306a36Sopenharmony_ci if (!tcon) 7262306a36Sopenharmony_ci goto err_node_put; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci ret = fsl_tcon_init_regmap(dev, tcon, np); 7562306a36Sopenharmony_ci if (ret) { 7662306a36Sopenharmony_ci dev_err(dev, "Couldn't create the TCON regmap\n"); 7762306a36Sopenharmony_ci goto err_node_put; 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci tcon->ipg_clk = of_clk_get_by_name(np, "ipg"); 8162306a36Sopenharmony_ci if (IS_ERR(tcon->ipg_clk)) { 8262306a36Sopenharmony_ci dev_err(dev, "Couldn't get the TCON bus clock\n"); 8362306a36Sopenharmony_ci goto err_node_put; 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci ret = clk_prepare_enable(tcon->ipg_clk); 8762306a36Sopenharmony_ci if (ret) { 8862306a36Sopenharmony_ci dev_err(dev, "Couldn't enable the TCON clock\n"); 8962306a36Sopenharmony_ci goto err_node_put; 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci of_node_put(np); 9362306a36Sopenharmony_ci dev_info(dev, "Using TCON in bypass mode\n"); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci return tcon; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cierr_node_put: 9862306a36Sopenharmony_ci of_node_put(np); 9962306a36Sopenharmony_ci return NULL; 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_civoid fsl_tcon_free(struct fsl_tcon *tcon) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci clk_disable_unprepare(tcon->ipg_clk); 10562306a36Sopenharmony_ci clk_put(tcon->ipg_clk); 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 108