1caab277bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 22c86e9fbSBoris Brezillon /* 32c86e9fbSBoris Brezillon * Copyright (C) 2014 Free Electrons 42c86e9fbSBoris Brezillon * Copyright (C) 2014 Atmel 52c86e9fbSBoris Brezillon * 62c86e9fbSBoris Brezillon * Author: Boris BREZILLON <boris.brezillon@free-electrons.com> 72c86e9fbSBoris Brezillon */ 82c86e9fbSBoris Brezillon 92c86e9fbSBoris Brezillon #include <linux/clk.h> 10ea31c0cfSBoris Brezillon #include <linux/iopoll.h> 112c86e9fbSBoris Brezillon #include <linux/mfd/atmel-hlcdc.h> 122c86e9fbSBoris Brezillon #include <linux/mfd/core.h> 132c86e9fbSBoris Brezillon #include <linux/module.h> 14ac316725SRandy Dunlap #include <linux/mod_devicetable.h> 152c86e9fbSBoris Brezillon #include <linux/platform_device.h> 162c86e9fbSBoris Brezillon #include <linux/regmap.h> 172c86e9fbSBoris Brezillon 182c86e9fbSBoris Brezillon #define ATMEL_HLCDC_REG_MAX (0x4000 - 0x4) 192c86e9fbSBoris Brezillon 20ea31c0cfSBoris Brezillon struct atmel_hlcdc_regmap { 21ea31c0cfSBoris Brezillon void __iomem *regs; 224b1ca3a4SClaudiu Beznea struct device *dev; 23ea31c0cfSBoris Brezillon }; 24ea31c0cfSBoris Brezillon 252c86e9fbSBoris Brezillon static const struct mfd_cell atmel_hlcdc_cells[] = { 262c86e9fbSBoris Brezillon { 272c86e9fbSBoris Brezillon .name = "atmel-hlcdc-pwm", 282c86e9fbSBoris Brezillon .of_compatible = "atmel,hlcdc-pwm", 292c86e9fbSBoris Brezillon }, 302c86e9fbSBoris Brezillon { 312c86e9fbSBoris Brezillon .name = "atmel-hlcdc-dc", 322c86e9fbSBoris Brezillon .of_compatible = "atmel,hlcdc-display-controller", 332c86e9fbSBoris Brezillon }, 342c86e9fbSBoris Brezillon }; 352c86e9fbSBoris Brezillon 36ea31c0cfSBoris Brezillon static int regmap_atmel_hlcdc_reg_write(void *context, unsigned int reg, 37ea31c0cfSBoris Brezillon unsigned int val) 38ea31c0cfSBoris Brezillon { 39ea31c0cfSBoris Brezillon struct atmel_hlcdc_regmap *hregmap = context; 40ea31c0cfSBoris Brezillon 41ea31c0cfSBoris Brezillon if (reg <= ATMEL_HLCDC_DIS) { 42ea31c0cfSBoris Brezillon u32 status; 4310f91676SClaudiu Beznea int ret; 44ea31c0cfSBoris Brezillon 4510f91676SClaudiu Beznea ret = readl_poll_timeout_atomic(hregmap->regs + ATMEL_HLCDC_SR, 4610f91676SClaudiu Beznea status, 4710f91676SClaudiu Beznea !(status & ATMEL_HLCDC_SIP), 482c2469bcSBoris Brezillon 1, 100); 4910f91676SClaudiu Beznea if (ret) { 5010f91676SClaudiu Beznea dev_err(hregmap->dev, 5110f91676SClaudiu Beznea "Timeout! Clock domain synchronization is in progress!\n"); 5210f91676SClaudiu Beznea return ret; 5310f91676SClaudiu Beznea } 54ea31c0cfSBoris Brezillon } 55ea31c0cfSBoris Brezillon 56ea31c0cfSBoris Brezillon writel(val, hregmap->regs + reg); 57ea31c0cfSBoris Brezillon 58ea31c0cfSBoris Brezillon return 0; 59ea31c0cfSBoris Brezillon } 60ea31c0cfSBoris Brezillon 61ea31c0cfSBoris Brezillon static int regmap_atmel_hlcdc_reg_read(void *context, unsigned int reg, 62ea31c0cfSBoris Brezillon unsigned int *val) 63ea31c0cfSBoris Brezillon { 64ea31c0cfSBoris Brezillon struct atmel_hlcdc_regmap *hregmap = context; 65ea31c0cfSBoris Brezillon 66ea31c0cfSBoris Brezillon *val = readl(hregmap->regs + reg); 67ea31c0cfSBoris Brezillon 68ea31c0cfSBoris Brezillon return 0; 69ea31c0cfSBoris Brezillon } 70ea31c0cfSBoris Brezillon 712c86e9fbSBoris Brezillon static const struct regmap_config atmel_hlcdc_regmap_config = { 722c86e9fbSBoris Brezillon .reg_bits = 32, 732c86e9fbSBoris Brezillon .val_bits = 32, 742c86e9fbSBoris Brezillon .reg_stride = 4, 752c86e9fbSBoris Brezillon .max_register = ATMEL_HLCDC_REG_MAX, 76ea31c0cfSBoris Brezillon .reg_write = regmap_atmel_hlcdc_reg_write, 77ea31c0cfSBoris Brezillon .reg_read = regmap_atmel_hlcdc_reg_read, 78ea31c0cfSBoris Brezillon .fast_io = true, 792c86e9fbSBoris Brezillon }; 802c86e9fbSBoris Brezillon 812c86e9fbSBoris Brezillon static int atmel_hlcdc_probe(struct platform_device *pdev) 822c86e9fbSBoris Brezillon { 83ea31c0cfSBoris Brezillon struct atmel_hlcdc_regmap *hregmap; 842c86e9fbSBoris Brezillon struct device *dev = &pdev->dev; 852c86e9fbSBoris Brezillon struct atmel_hlcdc *hlcdc; 862c86e9fbSBoris Brezillon struct resource *res; 87ea31c0cfSBoris Brezillon 88ea31c0cfSBoris Brezillon hregmap = devm_kzalloc(dev, sizeof(*hregmap), GFP_KERNEL); 89ea31c0cfSBoris Brezillon if (!hregmap) 90ea31c0cfSBoris Brezillon return -ENOMEM; 912c86e9fbSBoris Brezillon 922c86e9fbSBoris Brezillon hlcdc = devm_kzalloc(dev, sizeof(*hlcdc), GFP_KERNEL); 932c86e9fbSBoris Brezillon if (!hlcdc) 942c86e9fbSBoris Brezillon return -ENOMEM; 952c86e9fbSBoris Brezillon 962c86e9fbSBoris Brezillon res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 97ea31c0cfSBoris Brezillon hregmap->regs = devm_ioremap_resource(dev, res); 98ea31c0cfSBoris Brezillon if (IS_ERR(hregmap->regs)) 99ea31c0cfSBoris Brezillon return PTR_ERR(hregmap->regs); 1002c86e9fbSBoris Brezillon 1014b1ca3a4SClaudiu Beznea hregmap->dev = &pdev->dev; 1024b1ca3a4SClaudiu Beznea 1032c86e9fbSBoris Brezillon hlcdc->irq = platform_get_irq(pdev, 0); 1042c86e9fbSBoris Brezillon if (hlcdc->irq < 0) 1052c86e9fbSBoris Brezillon return hlcdc->irq; 1062c86e9fbSBoris Brezillon 1072c86e9fbSBoris Brezillon hlcdc->periph_clk = devm_clk_get(dev, "periph_clk"); 1082c86e9fbSBoris Brezillon if (IS_ERR(hlcdc->periph_clk)) { 1092c86e9fbSBoris Brezillon dev_err(dev, "failed to get peripheral clock\n"); 1102c86e9fbSBoris Brezillon return PTR_ERR(hlcdc->periph_clk); 1112c86e9fbSBoris Brezillon } 1122c86e9fbSBoris Brezillon 1132c86e9fbSBoris Brezillon hlcdc->sys_clk = devm_clk_get(dev, "sys_clk"); 1142c86e9fbSBoris Brezillon if (IS_ERR(hlcdc->sys_clk)) { 1152c86e9fbSBoris Brezillon dev_err(dev, "failed to get system clock\n"); 1162c86e9fbSBoris Brezillon return PTR_ERR(hlcdc->sys_clk); 1172c86e9fbSBoris Brezillon } 1182c86e9fbSBoris Brezillon 1192c86e9fbSBoris Brezillon hlcdc->slow_clk = devm_clk_get(dev, "slow_clk"); 1202c86e9fbSBoris Brezillon if (IS_ERR(hlcdc->slow_clk)) { 1212c86e9fbSBoris Brezillon dev_err(dev, "failed to get slow clock\n"); 1222c86e9fbSBoris Brezillon return PTR_ERR(hlcdc->slow_clk); 1232c86e9fbSBoris Brezillon } 1242c86e9fbSBoris Brezillon 125ea31c0cfSBoris Brezillon hlcdc->regmap = devm_regmap_init(dev, NULL, hregmap, 1262c86e9fbSBoris Brezillon &atmel_hlcdc_regmap_config); 1272c86e9fbSBoris Brezillon if (IS_ERR(hlcdc->regmap)) 1282c86e9fbSBoris Brezillon return PTR_ERR(hlcdc->regmap); 1292c86e9fbSBoris Brezillon 1302c86e9fbSBoris Brezillon dev_set_drvdata(dev, hlcdc); 1312c86e9fbSBoris Brezillon 132f32fb9a5SLaxman Dewangan return devm_mfd_add_devices(dev, -1, atmel_hlcdc_cells, 1332c86e9fbSBoris Brezillon ARRAY_SIZE(atmel_hlcdc_cells), 1342c86e9fbSBoris Brezillon NULL, 0, NULL); 1352c86e9fbSBoris Brezillon } 1362c86e9fbSBoris Brezillon 1372c86e9fbSBoris Brezillon static const struct of_device_id atmel_hlcdc_match[] = { 138d9c93f5dSBoris Brezillon { .compatible = "atmel,at91sam9n12-hlcdc" }, 139d9c93f5dSBoris Brezillon { .compatible = "atmel,at91sam9x5-hlcdc" }, 140d9c93f5dSBoris Brezillon { .compatible = "atmel,sama5d2-hlcdc" }, 1412c86e9fbSBoris Brezillon { .compatible = "atmel,sama5d3-hlcdc" }, 142d9c93f5dSBoris Brezillon { .compatible = "atmel,sama5d4-hlcdc" }, 1431e8c1585SClaudiu Beznea { .compatible = "microchip,sam9x60-hlcdc" }, 1442c86e9fbSBoris Brezillon { /* sentinel */ }, 1452c86e9fbSBoris Brezillon }; 146a0aef1f5SLuis de Bethencourt MODULE_DEVICE_TABLE(of, atmel_hlcdc_match); 1472c86e9fbSBoris Brezillon 1482c86e9fbSBoris Brezillon static struct platform_driver atmel_hlcdc_driver = { 1492c86e9fbSBoris Brezillon .probe = atmel_hlcdc_probe, 1502c86e9fbSBoris Brezillon .driver = { 1512c86e9fbSBoris Brezillon .name = "atmel-hlcdc", 1522c86e9fbSBoris Brezillon .of_match_table = atmel_hlcdc_match, 1532c86e9fbSBoris Brezillon }, 1542c86e9fbSBoris Brezillon }; 1552c86e9fbSBoris Brezillon module_platform_driver(atmel_hlcdc_driver); 1562c86e9fbSBoris Brezillon 1572c86e9fbSBoris Brezillon MODULE_ALIAS("platform:atmel-hlcdc"); 1582c86e9fbSBoris Brezillon MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>"); 1592c86e9fbSBoris Brezillon MODULE_DESCRIPTION("Atmel HLCDC driver"); 1602c86e9fbSBoris Brezillon MODULE_LICENSE("GPL v2"); 161