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); 60fb127b79SStefan Agner if (IS_ERR(tcon->regs)) 61fb127b79SStefan Agner return PTR_ERR(tcon->regs); 62fb127b79SStefan Agner 63fb127b79SStefan Agner return 0; 64fb127b79SStefan Agner } 65fb127b79SStefan Agner 66fb127b79SStefan Agner struct fsl_tcon *fsl_tcon_init(struct device *dev) 67fb127b79SStefan Agner { 68fb127b79SStefan Agner struct fsl_tcon *tcon; 69fb127b79SStefan Agner struct device_node *np; 70fb127b79SStefan Agner int ret; 71fb127b79SStefan Agner 72fb127b79SStefan Agner /* TCON node is not mandatory, some devices do not provide TCON */ 73fb127b79SStefan Agner np = of_parse_phandle(dev->of_node, "fsl,tcon", 0); 74fb127b79SStefan Agner if (!np) 75fb127b79SStefan Agner return NULL; 76fb127b79SStefan Agner 77fb127b79SStefan Agner tcon = devm_kzalloc(dev, sizeof(*tcon), GFP_KERNEL); 78fb127b79SStefan Agner if (!tcon) { 79fb127b79SStefan Agner ret = -ENOMEM; 80fb127b79SStefan Agner goto err_node_put; 81fb127b79SStefan Agner } 82fb127b79SStefan Agner 83fb127b79SStefan Agner ret = fsl_tcon_init_regmap(dev, tcon, np); 84fb127b79SStefan Agner if (ret) { 85fb127b79SStefan Agner dev_err(dev, "Couldn't create the TCON regmap\n"); 86fb127b79SStefan Agner goto err_node_put; 87fb127b79SStefan Agner } 88fb127b79SStefan Agner 89fb127b79SStefan Agner tcon->ipg_clk = of_clk_get_by_name(np, "ipg"); 90fb127b79SStefan Agner if (IS_ERR(tcon->ipg_clk)) { 91fb127b79SStefan Agner dev_err(dev, "Couldn't get the TCON bus clock\n"); 92fb127b79SStefan Agner goto err_node_put; 93fb127b79SStefan Agner } 94fb127b79SStefan Agner 9564bf74eeSPeter Chen of_node_put(np); 96fb127b79SStefan Agner clk_prepare_enable(tcon->ipg_clk); 97fb127b79SStefan Agner 98fb127b79SStefan Agner dev_info(dev, "Using TCON in bypass mode\n"); 99fb127b79SStefan Agner 100fb127b79SStefan Agner return tcon; 101fb127b79SStefan Agner 102fb127b79SStefan Agner err_node_put: 103fb127b79SStefan Agner of_node_put(np); 104fb127b79SStefan Agner return NULL; 105fb127b79SStefan Agner } 106fb127b79SStefan Agner 107fb127b79SStefan Agner void fsl_tcon_free(struct fsl_tcon *tcon) 108fb127b79SStefan Agner { 109fb127b79SStefan Agner clk_disable_unprepare(tcon->ipg_clk); 110fb127b79SStefan Agner clk_put(tcon->ipg_clk); 111fb127b79SStefan Agner } 112fb127b79SStefan Agner 113