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 mfd_cell sun8i_a23_prcm_subdevs[] = { 80 { 81 .name = "sun8i-a23-apb0-clk", 82 .of_compatible = "allwinner,sun8i-a23-apb0-clk", 83 .num_resources = ARRAY_SIZE(sun6i_a31_apb0_clk_res), 84 .resources = sun6i_a31_apb0_clk_res, 85 }, 86 { 87 .name = "sun6i-a31-apb0-gates-clk", 88 .of_compatible = "allwinner,sun8i-a23-apb0-gates-clk", 89 .num_resources = ARRAY_SIZE(sun6i_a31_apb0_gates_clk_res), 90 .resources = sun6i_a31_apb0_gates_clk_res, 91 }, 92 { 93 .name = "sun6i-a31-apb0-clock-reset", 94 .of_compatible = "allwinner,sun6i-a31-clock-reset", 95 .num_resources = ARRAY_SIZE(sun6i_a31_apb0_rstc_res), 96 .resources = sun6i_a31_apb0_rstc_res, 97 }, 98 }; 99 100 static const struct prcm_data sun6i_a31_prcm_data = { 101 .nsubdevs = ARRAY_SIZE(sun6i_a31_prcm_subdevs), 102 .subdevs = sun6i_a31_prcm_subdevs, 103 }; 104 105 static const struct prcm_data sun8i_a23_prcm_data = { 106 .nsubdevs = ARRAY_SIZE(sun8i_a23_prcm_subdevs), 107 .subdevs = sun8i_a23_prcm_subdevs, 108 }; 109 110 static const struct of_device_id sun6i_prcm_dt_ids[] = { 111 { 112 .compatible = "allwinner,sun6i-a31-prcm", 113 .data = &sun6i_a31_prcm_data, 114 }, 115 { 116 .compatible = "allwinner,sun8i-a23-prcm", 117 .data = &sun8i_a23_prcm_data, 118 }, 119 { /* sentinel */ }, 120 }; 121 122 static int sun6i_prcm_probe(struct platform_device *pdev) 123 { 124 struct device_node *np = pdev->dev.of_node; 125 const struct of_device_id *match; 126 const struct prcm_data *data; 127 struct resource *res; 128 int ret; 129 130 match = of_match_node(sun6i_prcm_dt_ids, np); 131 if (!match) 132 return -EINVAL; 133 134 data = match->data; 135 136 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 137 if (!res) { 138 dev_err(&pdev->dev, "no prcm memory region provided\n"); 139 return -ENOENT; 140 } 141 142 ret = mfd_add_devices(&pdev->dev, 0, data->subdevs, data->nsubdevs, 143 res, -1, NULL); 144 if (ret) { 145 dev_err(&pdev->dev, "failed to add subdevices\n"); 146 return ret; 147 } 148 149 return 0; 150 } 151 152 static struct platform_driver sun6i_prcm_driver = { 153 .driver = { 154 .name = "sun6i-prcm", 155 .owner = THIS_MODULE, 156 .of_match_table = sun6i_prcm_dt_ids, 157 }, 158 .probe = sun6i_prcm_probe, 159 }; 160 module_platform_driver(sun6i_prcm_driver); 161 162 MODULE_AUTHOR("Boris BREZILLON <boris.brezillon@free-electrons.com>"); 163 MODULE_DESCRIPTION("Allwinner sun6i PRCM driver"); 164 MODULE_LICENSE("GPL v2"); 165