1 /* 2 * Copyright (C) 2014 Free Electrons 3 * 4 * License Terms: GNU General Public License v2 5 * Author: Boris BREZILLON <boris.brezillon@free-electrons.com> 6 * 7 * Allwinner PRCM (Power/Reset/Clock Management) driver 8 * 9 */ 10 11 #include <linux/mfd/core.h> 12 #include <linux/module.h> 13 #include <linux/of.h> 14 15 struct prcm_data { 16 int nsubdevs; 17 const struct mfd_cell *subdevs; 18 }; 19 20 static const struct resource sun6i_a31_ar100_clk_res[] = { 21 { 22 .start = 0x0, 23 .end = 0x3, 24 .flags = IORESOURCE_MEM, 25 }, 26 }; 27 28 static const struct resource sun6i_a31_apb0_clk_res[] = { 29 { 30 .start = 0xc, 31 .end = 0xf, 32 .flags = IORESOURCE_MEM, 33 }, 34 }; 35 36 static const struct resource sun6i_a31_apb0_gates_clk_res[] = { 37 { 38 .start = 0x28, 39 .end = 0x2b, 40 .flags = IORESOURCE_MEM, 41 }, 42 }; 43 44 static const struct resource sun6i_a31_apb0_rstc_res[] = { 45 { 46 .start = 0xb0, 47 .end = 0xb3, 48 .flags = IORESOURCE_MEM, 49 }, 50 }; 51 52 static const struct mfd_cell sun6i_a31_prcm_subdevs[] = { 53 { 54 .name = "sun6i-a31-ar100-clk", 55 .of_compatible = "allwinner,sun6i-a31-ar100-clk", 56 .num_resources = ARRAY_SIZE(sun6i_a31_ar100_clk_res), 57 .resources = sun6i_a31_ar100_clk_res, 58 }, 59 { 60 .name = "sun6i-a31-apb0-clk", 61 .of_compatible = "allwinner,sun6i-a31-apb0-clk", 62 .num_resources = ARRAY_SIZE(sun6i_a31_apb0_clk_res), 63 .resources = sun6i_a31_apb0_clk_res, 64 }, 65 { 66 .name = "sun6i-a31-apb0-gates-clk", 67 .of_compatible = "allwinner,sun6i-a31-apb0-gates-clk", 68 .num_resources = ARRAY_SIZE(sun6i_a31_apb0_gates_clk_res), 69 .resources = sun6i_a31_apb0_gates_clk_res, 70 }, 71 { 72 .name = "sun6i-a31-apb0-clock-reset", 73 .of_compatible = "allwinner,sun6i-a31-clock-reset", 74 .num_resources = ARRAY_SIZE(sun6i_a31_apb0_rstc_res), 75 .resources = sun6i_a31_apb0_rstc_res, 76 }, 77 }; 78 79 static const struct prcm_data sun6i_a31_prcm_data = { 80 .nsubdevs = ARRAY_SIZE(sun6i_a31_prcm_subdevs), 81 .subdevs = sun6i_a31_prcm_subdevs, 82 }; 83 84 static const struct of_device_id sun6i_prcm_dt_ids[] = { 85 { 86 .compatible = "allwinner,sun6i-a31-prcm", 87 .data = &sun6i_a31_prcm_data, 88 }, 89 { /* sentinel */ }, 90 }; 91 92 static int sun6i_prcm_probe(struct platform_device *pdev) 93 { 94 struct device_node *np = pdev->dev.of_node; 95 const struct of_device_id *match; 96 const struct prcm_data *data; 97 struct resource *res; 98 int ret; 99 100 match = of_match_node(sun6i_prcm_dt_ids, np); 101 if (!match) 102 return -EINVAL; 103 104 data = match->data; 105 106 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 107 if (!res) { 108 dev_err(&pdev->dev, "no prcm memory region provided\n"); 109 return -ENOENT; 110 } 111 112 ret = mfd_add_devices(&pdev->dev, 0, data->subdevs, data->nsubdevs, 113 res, -1, NULL); 114 if (ret) { 115 dev_err(&pdev->dev, "failed to add subdevices\n"); 116 return ret; 117 } 118 119 return 0; 120 } 121 122 static struct platform_driver sun6i_prcm_driver = { 123 .driver = { 124 .name = "sun6i-prcm", 125 .owner = THIS_MODULE, 126 .of_match_table = sun6i_prcm_dt_ids, 127 }, 128 .probe = sun6i_prcm_probe, 129 }; 130 module_platform_driver(sun6i_prcm_driver); 131 132 MODULE_AUTHOR("Boris BREZILLON <boris.brezillon@free-electrons.com>"); 133 MODULE_DESCRIPTION("Allwinner sun6i PRCM driver"); 134 MODULE_LICENSE("GPL v2"); 135