1fb127b79SStefan Agner /* 2fb127b79SStefan Agner * Copyright 2015 Toradex AG 3fb127b79SStefan Agner * 4fb127b79SStefan Agner * Stefan Agner <stefan@agner.ch> 5fb127b79SStefan Agner * 6fb127b79SStefan Agner * Freescale TCON device driver 7fb127b79SStefan Agner * 8fb127b79SStefan Agner * This program is free software; you can redistribute it and/or modify 9fb127b79SStefan Agner * it under the terms of the GNU General Public License as published by 10fb127b79SStefan Agner * the Free Software Foundation; either version 2 of the License, or 11fb127b79SStefan Agner * (at your option) any later version. 12fb127b79SStefan Agner */ 13fb127b79SStefan Agner 14fb127b79SStefan Agner #include <linux/clk.h> 15fb127b79SStefan Agner #include <linux/io.h> 16fb127b79SStefan Agner #include <linux/mm.h> 17fb127b79SStefan Agner #include <linux/of_address.h> 18fb127b79SStefan Agner #include <linux/platform_device.h> 19fb127b79SStefan Agner #include <linux/regmap.h> 20fb127b79SStefan Agner 21fb127b79SStefan Agner #include "fsl_tcon.h" 22fb127b79SStefan Agner 23fb127b79SStefan Agner void fsl_tcon_bypass_disable(struct fsl_tcon *tcon) 24fb127b79SStefan Agner { 25fb127b79SStefan Agner regmap_update_bits(tcon->regs, FSL_TCON_CTRL1, 26fb127b79SStefan Agner FSL_TCON_CTRL1_TCON_BYPASS, 0); 27fb127b79SStefan Agner } 28fb127b79SStefan Agner 29fb127b79SStefan Agner void fsl_tcon_bypass_enable(struct fsl_tcon *tcon) 30fb127b79SStefan Agner { 31fb127b79SStefan Agner regmap_update_bits(tcon->regs, FSL_TCON_CTRL1, 32fb127b79SStefan Agner FSL_TCON_CTRL1_TCON_BYPASS, 33fb127b79SStefan Agner FSL_TCON_CTRL1_TCON_BYPASS); 34fb127b79SStefan Agner } 35fb127b79SStefan Agner 36fb127b79SStefan Agner static struct regmap_config fsl_tcon_regmap_config = { 37fb127b79SStefan Agner .reg_bits = 32, 38fb127b79SStefan Agner .reg_stride = 4, 39fb127b79SStefan Agner .val_bits = 32, 40fb127b79SStefan Agner 41fb127b79SStefan Agner .name = "tcon", 42fb127b79SStefan Agner }; 43fb127b79SStefan Agner 44fb127b79SStefan Agner static int fsl_tcon_init_regmap(struct device *dev, 45fb127b79SStefan Agner struct fsl_tcon *tcon, 46fb127b79SStefan Agner struct device_node *np) 47fb127b79SStefan Agner { 48fb127b79SStefan Agner struct resource res; 49fb127b79SStefan Agner void __iomem *regs; 50fb127b79SStefan Agner 51fb127b79SStefan Agner if (of_address_to_resource(np, 0, &res)) 52fb127b79SStefan Agner return -EINVAL; 53fb127b79SStefan Agner 54fb127b79SStefan Agner regs = devm_ioremap_resource(dev, &res); 55fb127b79SStefan Agner if (IS_ERR(regs)) 56fb127b79SStefan Agner return PTR_ERR(regs); 57fb127b79SStefan Agner 58fb127b79SStefan Agner tcon->regs = devm_regmap_init_mmio(dev, regs, 59fb127b79SStefan Agner &fsl_tcon_regmap_config); 60acd4d615SWei Yongjun return PTR_ERR_OR_ZERO(tcon->regs); 61fb127b79SStefan Agner } 62fb127b79SStefan Agner 63fb127b79SStefan Agner struct fsl_tcon *fsl_tcon_init(struct device *dev) 64fb127b79SStefan Agner { 65fb127b79SStefan Agner struct fsl_tcon *tcon; 66fb127b79SStefan Agner struct device_node *np; 67fb127b79SStefan Agner int ret; 68fb127b79SStefan Agner 69fb127b79SStefan Agner /* TCON node is not mandatory, some devices do not provide TCON */ 70fb127b79SStefan Agner np = of_parse_phandle(dev->of_node, "fsl,tcon", 0); 71fb127b79SStefan Agner if (!np) 72fb127b79SStefan Agner return NULL; 73fb127b79SStefan Agner 74fb127b79SStefan Agner tcon = devm_kzalloc(dev, sizeof(*tcon), GFP_KERNEL); 755d2883d5SFabio Estevam if (!tcon) 76fb127b79SStefan Agner goto err_node_put; 77fb127b79SStefan Agner 78fb127b79SStefan Agner ret = fsl_tcon_init_regmap(dev, tcon, np); 79fb127b79SStefan Agner if (ret) { 80fb127b79SStefan Agner dev_err(dev, "Couldn't create the TCON regmap\n"); 81fb127b79SStefan Agner goto err_node_put; 82fb127b79SStefan Agner } 83fb127b79SStefan Agner 84fb127b79SStefan Agner tcon->ipg_clk = of_clk_get_by_name(np, "ipg"); 85fb127b79SStefan Agner if (IS_ERR(tcon->ipg_clk)) { 86fb127b79SStefan Agner dev_err(dev, "Couldn't get the TCON bus clock\n"); 87fb127b79SStefan Agner goto err_node_put; 88fb127b79SStefan Agner } 89fb127b79SStefan Agner 90ef15d361SFabio Estevam ret = clk_prepare_enable(tcon->ipg_clk); 91ef15d361SFabio Estevam if (ret) { 92ef15d361SFabio Estevam dev_err(dev, "Couldn't enable the TCON clock\n"); 93ef15d361SFabio Estevam goto err_node_put; 94ef15d361SFabio Estevam } 95fb127b79SStefan Agner 96ef15d361SFabio Estevam of_node_put(np); 97fb127b79SStefan Agner dev_info(dev, "Using TCON in bypass mode\n"); 98fb127b79SStefan Agner 99fb127b79SStefan Agner return tcon; 100fb127b79SStefan Agner 101fb127b79SStefan Agner err_node_put: 102fb127b79SStefan Agner of_node_put(np); 103fb127b79SStefan Agner return NULL; 104fb127b79SStefan Agner } 105fb127b79SStefan Agner 106fb127b79SStefan Agner void fsl_tcon_free(struct fsl_tcon *tcon) 107fb127b79SStefan Agner { 108fb127b79SStefan Agner clk_disable_unprepare(tcon->ipg_clk); 109fb127b79SStefan Agner clk_put(tcon->ipg_clk); 110fb127b79SStefan Agner } 111fb127b79SStefan Agner 112