1 /* 2 * Copyright (C) 2014 Free Electrons 3 * Copyright (C) 2014 Atmel 4 * 5 * Author: Boris BREZILLON <boris.brezillon@free-electrons.com> 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License version 2 as published by 9 * the Free Software Foundation. 10 * 11 * This program is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 14 * more details. 15 * 16 * You should have received a copy of the GNU General Public License along with 17 * this program. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 #include <linux/clk.h> 21 #include <linux/iopoll.h> 22 #include <linux/mfd/atmel-hlcdc.h> 23 #include <linux/mfd/core.h> 24 #include <linux/module.h> 25 #include <linux/platform_device.h> 26 #include <linux/regmap.h> 27 28 #define ATMEL_HLCDC_REG_MAX (0x4000 - 0x4) 29 30 struct atmel_hlcdc_regmap { 31 void __iomem *regs; 32 }; 33 34 static const struct mfd_cell atmel_hlcdc_cells[] = { 35 { 36 .name = "atmel-hlcdc-pwm", 37 .of_compatible = "atmel,hlcdc-pwm", 38 }, 39 { 40 .name = "atmel-hlcdc-dc", 41 .of_compatible = "atmel,hlcdc-display-controller", 42 }, 43 }; 44 45 static int regmap_atmel_hlcdc_reg_write(void *context, unsigned int reg, 46 unsigned int val) 47 { 48 struct atmel_hlcdc_regmap *hregmap = context; 49 50 if (reg <= ATMEL_HLCDC_DIS) { 51 u32 status; 52 53 readl_poll_timeout(hregmap->regs + ATMEL_HLCDC_SR, status, 54 !(status & ATMEL_HLCDC_SIP), 1, 100); 55 } 56 57 writel(val, hregmap->regs + reg); 58 59 return 0; 60 } 61 62 static int regmap_atmel_hlcdc_reg_read(void *context, unsigned int reg, 63 unsigned int *val) 64 { 65 struct atmel_hlcdc_regmap *hregmap = context; 66 67 *val = readl(hregmap->regs + reg); 68 69 return 0; 70 } 71 72 static const struct regmap_config atmel_hlcdc_regmap_config = { 73 .reg_bits = 32, 74 .val_bits = 32, 75 .reg_stride = 4, 76 .max_register = ATMEL_HLCDC_REG_MAX, 77 .reg_write = regmap_atmel_hlcdc_reg_write, 78 .reg_read = regmap_atmel_hlcdc_reg_read, 79 .fast_io = true, 80 }; 81 82 static int atmel_hlcdc_probe(struct platform_device *pdev) 83 { 84 struct atmel_hlcdc_regmap *hregmap; 85 struct device *dev = &pdev->dev; 86 struct atmel_hlcdc *hlcdc; 87 struct resource *res; 88 89 hregmap = devm_kzalloc(dev, sizeof(*hregmap), GFP_KERNEL); 90 if (!hregmap) 91 return -ENOMEM; 92 93 hlcdc = devm_kzalloc(dev, sizeof(*hlcdc), GFP_KERNEL); 94 if (!hlcdc) 95 return -ENOMEM; 96 97 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 98 hregmap->regs = devm_ioremap_resource(dev, res); 99 if (IS_ERR(hregmap->regs)) 100 return PTR_ERR(hregmap->regs); 101 102 hlcdc->irq = platform_get_irq(pdev, 0); 103 if (hlcdc->irq < 0) 104 return hlcdc->irq; 105 106 hlcdc->periph_clk = devm_clk_get(dev, "periph_clk"); 107 if (IS_ERR(hlcdc->periph_clk)) { 108 dev_err(dev, "failed to get peripheral clock\n"); 109 return PTR_ERR(hlcdc->periph_clk); 110 } 111 112 hlcdc->sys_clk = devm_clk_get(dev, "sys_clk"); 113 if (IS_ERR(hlcdc->sys_clk)) { 114 dev_err(dev, "failed to get system clock\n"); 115 return PTR_ERR(hlcdc->sys_clk); 116 } 117 118 hlcdc->slow_clk = devm_clk_get(dev, "slow_clk"); 119 if (IS_ERR(hlcdc->slow_clk)) { 120 dev_err(dev, "failed to get slow clock\n"); 121 return PTR_ERR(hlcdc->slow_clk); 122 } 123 124 hlcdc->regmap = devm_regmap_init(dev, NULL, hregmap, 125 &atmel_hlcdc_regmap_config); 126 if (IS_ERR(hlcdc->regmap)) 127 return PTR_ERR(hlcdc->regmap); 128 129 dev_set_drvdata(dev, hlcdc); 130 131 return mfd_add_devices(dev, -1, atmel_hlcdc_cells, 132 ARRAY_SIZE(atmel_hlcdc_cells), 133 NULL, 0, NULL); 134 } 135 136 static int atmel_hlcdc_remove(struct platform_device *pdev) 137 { 138 mfd_remove_devices(&pdev->dev); 139 140 return 0; 141 } 142 143 static const struct of_device_id atmel_hlcdc_match[] = { 144 { .compatible = "atmel,at91sam9n12-hlcdc" }, 145 { .compatible = "atmel,at91sam9x5-hlcdc" }, 146 { .compatible = "atmel,sama5d2-hlcdc" }, 147 { .compatible = "atmel,sama5d3-hlcdc" }, 148 { .compatible = "atmel,sama5d4-hlcdc" }, 149 { /* sentinel */ }, 150 }; 151 152 static struct platform_driver atmel_hlcdc_driver = { 153 .probe = atmel_hlcdc_probe, 154 .remove = atmel_hlcdc_remove, 155 .driver = { 156 .name = "atmel-hlcdc", 157 .of_match_table = atmel_hlcdc_match, 158 }, 159 }; 160 module_platform_driver(atmel_hlcdc_driver); 161 162 MODULE_ALIAS("platform:atmel-hlcdc"); 163 MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>"); 164 MODULE_DESCRIPTION("Atmel HLCDC driver"); 165 MODULE_LICENSE("GPL v2"); 166