1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2014 Free Electrons 4 * Copyright (C) 2014 Atmel 5 * 6 * Author: Boris BREZILLON <boris.brezillon@free-electrons.com> 7 */ 8 9 #include <linux/clk.h> 10 #include <linux/iopoll.h> 11 #include <linux/mfd/atmel-hlcdc.h> 12 #include <linux/mfd/core.h> 13 #include <linux/module.h> 14 #include <linux/mod_devicetable.h> 15 #include <linux/platform_device.h> 16 #include <linux/regmap.h> 17 18 #define ATMEL_HLCDC_REG_MAX (0x4000 - 0x4) 19 20 struct atmel_hlcdc_regmap { 21 void __iomem *regs; 22 struct device *dev; 23 }; 24 25 static const struct mfd_cell atmel_hlcdc_cells[] = { 26 { 27 .name = "atmel-hlcdc-pwm", 28 .of_compatible = "atmel,hlcdc-pwm", 29 }, 30 { 31 .name = "atmel-hlcdc-dc", 32 .of_compatible = "atmel,hlcdc-display-controller", 33 }, 34 }; 35 36 static int regmap_atmel_hlcdc_reg_write(void *context, unsigned int reg, 37 unsigned int val) 38 { 39 struct atmel_hlcdc_regmap *hregmap = context; 40 41 if (reg <= ATMEL_HLCDC_DIS) { 42 u32 status; 43 int ret; 44 45 ret = readl_poll_timeout_atomic(hregmap->regs + ATMEL_HLCDC_SR, 46 status, 47 !(status & ATMEL_HLCDC_SIP), 48 1, 100); 49 if (ret) { 50 dev_err(hregmap->dev, 51 "Timeout! Clock domain synchronization is in progress!\n"); 52 return ret; 53 } 54 } 55 56 writel(val, hregmap->regs + reg); 57 58 return 0; 59 } 60 61 static int regmap_atmel_hlcdc_reg_read(void *context, unsigned int reg, 62 unsigned int *val) 63 { 64 struct atmel_hlcdc_regmap *hregmap = context; 65 66 *val = readl(hregmap->regs + reg); 67 68 return 0; 69 } 70 71 static const struct regmap_config atmel_hlcdc_regmap_config = { 72 .reg_bits = 32, 73 .val_bits = 32, 74 .reg_stride = 4, 75 .max_register = ATMEL_HLCDC_REG_MAX, 76 .reg_write = regmap_atmel_hlcdc_reg_write, 77 .reg_read = regmap_atmel_hlcdc_reg_read, 78 .fast_io = true, 79 }; 80 81 static int atmel_hlcdc_probe(struct platform_device *pdev) 82 { 83 struct atmel_hlcdc_regmap *hregmap; 84 struct device *dev = &pdev->dev; 85 struct atmel_hlcdc *hlcdc; 86 87 hregmap = devm_kzalloc(dev, sizeof(*hregmap), GFP_KERNEL); 88 if (!hregmap) 89 return -ENOMEM; 90 91 hlcdc = devm_kzalloc(dev, sizeof(*hlcdc), GFP_KERNEL); 92 if (!hlcdc) 93 return -ENOMEM; 94 95 hregmap->regs = devm_platform_ioremap_resource(pdev, 0); 96 if (IS_ERR(hregmap->regs)) 97 return PTR_ERR(hregmap->regs); 98 99 hregmap->dev = &pdev->dev; 100 101 hlcdc->irq = platform_get_irq(pdev, 0); 102 if (hlcdc->irq < 0) 103 return hlcdc->irq; 104 105 hlcdc->periph_clk = devm_clk_get(dev, "periph_clk"); 106 if (IS_ERR(hlcdc->periph_clk)) { 107 dev_err(dev, "failed to get peripheral clock\n"); 108 return PTR_ERR(hlcdc->periph_clk); 109 } 110 111 hlcdc->sys_clk = devm_clk_get(dev, "sys_clk"); 112 if (IS_ERR(hlcdc->sys_clk)) { 113 dev_err(dev, "failed to get system clock\n"); 114 return PTR_ERR(hlcdc->sys_clk); 115 } 116 117 hlcdc->slow_clk = devm_clk_get(dev, "slow_clk"); 118 if (IS_ERR(hlcdc->slow_clk)) { 119 dev_err(dev, "failed to get slow clock\n"); 120 return PTR_ERR(hlcdc->slow_clk); 121 } 122 123 hlcdc->regmap = devm_regmap_init(dev, NULL, hregmap, 124 &atmel_hlcdc_regmap_config); 125 if (IS_ERR(hlcdc->regmap)) 126 return PTR_ERR(hlcdc->regmap); 127 128 dev_set_drvdata(dev, hlcdc); 129 130 return devm_mfd_add_devices(dev, -1, atmel_hlcdc_cells, 131 ARRAY_SIZE(atmel_hlcdc_cells), 132 NULL, 0, NULL); 133 } 134 135 static const struct of_device_id atmel_hlcdc_match[] = { 136 { .compatible = "atmel,at91sam9n12-hlcdc" }, 137 { .compatible = "atmel,at91sam9x5-hlcdc" }, 138 { .compatible = "atmel,sama5d2-hlcdc" }, 139 { .compatible = "atmel,sama5d3-hlcdc" }, 140 { .compatible = "atmel,sama5d4-hlcdc" }, 141 { .compatible = "microchip,sam9x60-hlcdc" }, 142 { /* sentinel */ }, 143 }; 144 MODULE_DEVICE_TABLE(of, atmel_hlcdc_match); 145 146 static struct platform_driver atmel_hlcdc_driver = { 147 .probe = atmel_hlcdc_probe, 148 .driver = { 149 .name = "atmel-hlcdc", 150 .of_match_table = atmel_hlcdc_match, 151 }, 152 }; 153 module_platform_driver(atmel_hlcdc_driver); 154 155 MODULE_ALIAS("platform:atmel-hlcdc"); 156 MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>"); 157 MODULE_DESCRIPTION("Atmel HLCDC driver"); 158 MODULE_LICENSE("GPL v2"); 159