diff options
| -rw-r--r-- | drivers/clk/at91/Makefile | 1 | ||||
| -rw-r--r-- | drivers/clk/at91/sam9x60.c | 307 |
2 files changed, 308 insertions, 0 deletions
diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile index 0a30fc8dfcb0..3732241352ce 100644 --- a/drivers/clk/at91/Makefile +++ b/drivers/clk/at91/Makefile | |||
| @@ -16,5 +16,6 @@ obj-$(CONFIG_HAVE_AT91_GENERATED_CLK) += clk-generated.o | |||
| 16 | obj-$(CONFIG_HAVE_AT91_I2S_MUX_CLK) += clk-i2s-mux.o | 16 | obj-$(CONFIG_HAVE_AT91_I2S_MUX_CLK) += clk-i2s-mux.o |
| 17 | obj-$(CONFIG_HAVE_AT91_SAM9X60_PLL) += clk-sam9x60-pll.o | 17 | obj-$(CONFIG_HAVE_AT91_SAM9X60_PLL) += clk-sam9x60-pll.o |
| 18 | obj-$(CONFIG_SOC_AT91SAM9) += at91sam9260.o at91sam9rl.o at91sam9x5.o | 18 | obj-$(CONFIG_SOC_AT91SAM9) += at91sam9260.o at91sam9rl.o at91sam9x5.o |
| 19 | obj-$(CONFIG_SOC_SAM9X60) += sam9x60.o | ||
| 19 | obj-$(CONFIG_SOC_SAMA5D4) += sama5d4.o | 20 | obj-$(CONFIG_SOC_SAMA5D4) += sama5d4.o |
| 20 | obj-$(CONFIG_SOC_SAMA5D2) += sama5d2.o | 21 | obj-$(CONFIG_SOC_SAMA5D2) += sama5d2.o |
diff --git a/drivers/clk/at91/sam9x60.c b/drivers/clk/at91/sam9x60.c new file mode 100644 index 000000000000..0979c757cc58 --- /dev/null +++ b/drivers/clk/at91/sam9x60.c | |||
| @@ -0,0 +1,307 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | #include <linux/clk-provider.h> | ||
| 3 | #include <linux/mfd/syscon.h> | ||
| 4 | #include <linux/slab.h> | ||
| 5 | |||
| 6 | #include <dt-bindings/clock/at91.h> | ||
| 7 | |||
| 8 | #include "pmc.h" | ||
| 9 | |||
| 10 | static DEFINE_SPINLOCK(pmc_pll_lock); | ||
| 11 | |||
| 12 | static const struct clk_master_characteristics mck_characteristics = { | ||
| 13 | .output = { .min = 140000000, .max = 200000000 }, | ||
| 14 | .divisors = { 1, 2, 4, 3 }, | ||
| 15 | .have_div3_pres = 1, | ||
| 16 | }; | ||
| 17 | |||
| 18 | static const struct clk_master_layout sam9x60_master_layout = { | ||
| 19 | .mask = 0x373, | ||
| 20 | .pres_shift = 4, | ||
| 21 | .offset = 0x28, | ||
| 22 | }; | ||
| 23 | |||
| 24 | static struct clk_range plla_outputs[] = { | ||
| 25 | { .min = 300000000, .max = 600000000 }, | ||
| 26 | }; | ||
| 27 | |||
| 28 | static const struct clk_pll_characteristics plla_characteristics = { | ||
| 29 | .input = { .min = 12000000, .max = 48000000 }, | ||
| 30 | .num_output = ARRAY_SIZE(plla_outputs), | ||
| 31 | .output = plla_outputs, | ||
| 32 | }; | ||
| 33 | |||
| 34 | static struct clk_range upll_outputs[] = { | ||
| 35 | { .min = 300000000, .max = 500000000 }, | ||
| 36 | }; | ||
| 37 | |||
| 38 | static const struct clk_pll_characteristics upll_characteristics = { | ||
| 39 | .input = { .min = 12000000, .max = 48000000 }, | ||
| 40 | .num_output = ARRAY_SIZE(upll_outputs), | ||
| 41 | .output = upll_outputs, | ||
| 42 | .upll = true, | ||
| 43 | }; | ||
| 44 | |||
| 45 | static const struct clk_programmable_layout sam9x60_programmable_layout = { | ||
| 46 | .pres_shift = 8, | ||
| 47 | .css_mask = 0x1f, | ||
| 48 | .have_slck_mck = 0, | ||
| 49 | }; | ||
| 50 | |||
| 51 | static const struct clk_pcr_layout sam9x60_pcr_layout = { | ||
| 52 | .offset = 0x88, | ||
| 53 | .cmd = BIT(31), | ||
| 54 | .gckcss_mask = GENMASK(12, 8), | ||
| 55 | .pid_mask = GENMASK(6, 0), | ||
| 56 | }; | ||
| 57 | |||
| 58 | static const struct { | ||
| 59 | char *n; | ||
| 60 | char *p; | ||
| 61 | u8 id; | ||
| 62 | } sam9x60_systemck[] = { | ||
| 63 | { .n = "ddrck", .p = "masterck", .id = 2 }, | ||
| 64 | { .n = "uhpck", .p = "usbck", .id = 6 }, | ||
| 65 | { .n = "pck0", .p = "prog0", .id = 8 }, | ||
| 66 | { .n = "pck1", .p = "prog1", .id = 9 }, | ||
| 67 | { .n = "qspick", .p = "masterck", .id = 19 }, | ||
| 68 | }; | ||
| 69 | |||
| 70 | static const struct { | ||
| 71 | char *n; | ||
| 72 | u8 id; | ||
| 73 | } sam9x60_periphck[] = { | ||
| 74 | { .n = "pioA_clk", .id = 2, }, | ||
| 75 | { .n = "pioB_clk", .id = 3, }, | ||
| 76 | { .n = "pioC_clk", .id = 4, }, | ||
| 77 | { .n = "flex0_clk", .id = 5, }, | ||
| 78 | { .n = "flex1_clk", .id = 6, }, | ||
| 79 | { .n = "flex2_clk", .id = 7, }, | ||
| 80 | { .n = "flex3_clk", .id = 8, }, | ||
| 81 | { .n = "flex6_clk", .id = 9, }, | ||
| 82 | { .n = "flex7_clk", .id = 10, }, | ||
| 83 | { .n = "flex8_clk", .id = 11, }, | ||
| 84 | { .n = "sdmmc0_clk", .id = 12, }, | ||
| 85 | { .n = "flex4_clk", .id = 13, }, | ||
| 86 | { .n = "flex5_clk", .id = 14, }, | ||
| 87 | { .n = "flex9_clk", .id = 15, }, | ||
| 88 | { .n = "flex10_clk", .id = 16, }, | ||
| 89 | { .n = "tcb0_clk", .id = 17, }, | ||
| 90 | { .n = "pwm_clk", .id = 18, }, | ||
| 91 | { .n = "adc_clk", .id = 19, }, | ||
| 92 | { .n = "dma0_clk", .id = 20, }, | ||
| 93 | { .n = "matrix_clk", .id = 21, }, | ||
| 94 | { .n = "uhphs_clk", .id = 22, }, | ||
| 95 | { .n = "udphs_clk", .id = 23, }, | ||
| 96 | { .n = "macb0_clk", .id = 24, }, | ||
| 97 | { .n = "lcd_clk", .id = 25, }, | ||
| 98 | { .n = "sdmmc1_clk", .id = 26, }, | ||
| 99 | { .n = "macb1_clk", .id = 27, }, | ||
| 100 | { .n = "ssc_clk", .id = 28, }, | ||
| 101 | { .n = "can0_clk", .id = 29, }, | ||
| 102 | { .n = "can1_clk", .id = 30, }, | ||
| 103 | { .n = "flex11_clk", .id = 32, }, | ||
| 104 | { .n = "flex12_clk", .id = 33, }, | ||
| 105 | { .n = "i2s_clk", .id = 34, }, | ||
| 106 | { .n = "qspi_clk", .id = 35, }, | ||
| 107 | { .n = "gfx2d_clk", .id = 36, }, | ||
| 108 | { .n = "pit64b_clk", .id = 37, }, | ||
| 109 | { .n = "trng_clk", .id = 38, }, | ||
| 110 | { .n = "aes_clk", .id = 39, }, | ||
| 111 | { .n = "tdes_clk", .id = 40, }, | ||
| 112 | { .n = "sha_clk", .id = 41, }, | ||
| 113 | { .n = "classd_clk", .id = 42, }, | ||
| 114 | { .n = "isi_clk", .id = 43, }, | ||
| 115 | { .n = "pioD_clk", .id = 44, }, | ||
| 116 | { .n = "tcb1_clk", .id = 45, }, | ||
| 117 | { .n = "dbgu_clk", .id = 47, }, | ||
| 118 | { .n = "mpddr_clk", .id = 49, }, | ||
| 119 | }; | ||
| 120 | |||
| 121 | static const struct { | ||
| 122 | char *n; | ||
| 123 | u8 id; | ||
| 124 | struct clk_range r; | ||
| 125 | bool pll; | ||
| 126 | } sam9x60_gck[] = { | ||
| 127 | { .n = "flex0_gclk", .id = 5, }, | ||
| 128 | { .n = "flex1_gclk", .id = 6, }, | ||
| 129 | { .n = "flex2_gclk", .id = 7, }, | ||
| 130 | { .n = "flex3_gclk", .id = 8, }, | ||
| 131 | { .n = "flex6_gclk", .id = 9, }, | ||
| 132 | { .n = "flex7_gclk", .id = 10, }, | ||
| 133 | { .n = "flex8_gclk", .id = 11, }, | ||
| 134 | { .n = "sdmmc0_gclk", .id = 12, .r = { .min = 0, .max = 105000000 }, }, | ||
| 135 | { .n = "flex4_gclk", .id = 13, }, | ||
| 136 | { .n = "flex5_gclk", .id = 14, }, | ||
| 137 | { .n = "flex9_gclk", .id = 15, }, | ||
| 138 | { .n = "flex10_gclk", .id = 16, }, | ||
| 139 | { .n = "tcb0_gclk", .id = 17, }, | ||
| 140 | { .n = "adc_gclk", .id = 19, }, | ||
| 141 | { .n = "lcd_gclk", .id = 25, .r = { .min = 0, .max = 140000000 }, }, | ||
| 142 | { .n = "sdmmc1_gclk", .id = 26, .r = { .min = 0, .max = 105000000 }, }, | ||
| 143 | { .n = "flex11_gclk", .id = 32, }, | ||
| 144 | { .n = "flex12_gclk", .id = 33, }, | ||
| 145 | { .n = "i2s_gclk", .id = 34, .r = { .min = 0, .max = 105000000 }, | ||
| 146 | .pll = true, }, | ||
| 147 | { .n = "pit64b_gclk", .id = 37, }, | ||
| 148 | { .n = "classd_gclk", .id = 42, .r = { .min = 0, .max = 100000000 }, | ||
| 149 | .pll = true, }, | ||
| 150 | { .n = "tcb1_gclk", .id = 45, }, | ||
| 151 | { .n = "dbgu_gclk", .id = 47, }, | ||
| 152 | }; | ||
| 153 | |||
| 154 | static void __init sam9x60_pmc_setup(struct device_node *np) | ||
| 155 | { | ||
| 156 | struct clk_range range = CLK_RANGE(0, 0); | ||
| 157 | const char *td_slck_name, *md_slck_name, *mainxtal_name; | ||
| 158 | struct pmc_data *sam9x60_pmc; | ||
| 159 | const char *parent_names[6]; | ||
| 160 | struct regmap *regmap; | ||
| 161 | struct clk_hw *hw; | ||
| 162 | int i; | ||
| 163 | bool bypass; | ||
| 164 | |||
| 165 | i = of_property_match_string(np, "clock-names", "td_slck"); | ||
| 166 | if (i < 0) | ||
| 167 | return; | ||
| 168 | |||
| 169 | td_slck_name = of_clk_get_parent_name(np, i); | ||
| 170 | |||
| 171 | i = of_property_match_string(np, "clock-names", "md_slck"); | ||
| 172 | if (i < 0) | ||
| 173 | return; | ||
| 174 | |||
| 175 | md_slck_name = of_clk_get_parent_name(np, i); | ||
| 176 | |||
| 177 | i = of_property_match_string(np, "clock-names", "main_xtal"); | ||
| 178 | if (i < 0) | ||
| 179 | return; | ||
| 180 | mainxtal_name = of_clk_get_parent_name(np, i); | ||
| 181 | |||
| 182 | regmap = syscon_node_to_regmap(np); | ||
| 183 | if (IS_ERR(regmap)) | ||
| 184 | return; | ||
| 185 | |||
| 186 | sam9x60_pmc = pmc_data_allocate(PMC_MAIN + 1, | ||
| 187 | nck(sam9x60_systemck), | ||
| 188 | nck(sam9x60_periphck), | ||
| 189 | nck(sam9x60_gck)); | ||
| 190 | if (!sam9x60_pmc) | ||
| 191 | return; | ||
| 192 | |||
| 193 | hw = at91_clk_register_main_rc_osc(regmap, "main_rc_osc", 24000000, | ||
| 194 | 50000000); | ||
| 195 | if (IS_ERR(hw)) | ||
| 196 | goto err_free; | ||
| 197 | |||
| 198 | bypass = of_property_read_bool(np, "atmel,osc-bypass"); | ||
| 199 | |||
| 200 | hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name, | ||
| 201 | bypass); | ||
| 202 | if (IS_ERR(hw)) | ||
| 203 | goto err_free; | ||
| 204 | |||
| 205 | parent_names[0] = "main_rc_osc"; | ||
| 206 | parent_names[1] = "main_osc"; | ||
| 207 | hw = at91_clk_register_sam9x5_main(regmap, "mainck", parent_names, 2); | ||
| 208 | if (IS_ERR(hw)) | ||
| 209 | goto err_free; | ||
| 210 | |||
| 211 | sam9x60_pmc->chws[PMC_MAIN] = hw; | ||
| 212 | |||
| 213 | hw = sam9x60_clk_register_pll(regmap, &pmc_pll_lock, "pllack", | ||
| 214 | "mainck", 0, &plla_characteristics); | ||
| 215 | if (IS_ERR(hw)) | ||
| 216 | goto err_free; | ||
| 217 | |||
| 218 | hw = sam9x60_clk_register_pll(regmap, &pmc_pll_lock, "upllck", | ||
| 219 | "main_osc", 1, &upll_characteristics); | ||
| 220 | if (IS_ERR(hw)) | ||
| 221 | goto err_free; | ||
| 222 | |||
| 223 | sam9x60_pmc->chws[PMC_UTMI] = hw; | ||
| 224 | |||
| 225 | parent_names[0] = md_slck_name; | ||
| 226 | parent_names[1] = "mainck"; | ||
| 227 | parent_names[2] = "pllack"; | ||
| 228 | hw = at91_clk_register_master(regmap, "masterck", 3, parent_names, | ||
| 229 | &sam9x60_master_layout, | ||
| 230 | &mck_characteristics); | ||
| 231 | if (IS_ERR(hw)) | ||
| 232 | goto err_free; | ||
| 233 | |||
| 234 | sam9x60_pmc->chws[PMC_MCK] = hw; | ||
| 235 | |||
| 236 | parent_names[0] = "pllack"; | ||
| 237 | parent_names[1] = "upllck"; | ||
| 238 | parent_names[2] = "mainck"; | ||
| 239 | parent_names[3] = "mainck"; | ||
| 240 | hw = sam9x60_clk_register_usb(regmap, "usbck", parent_names, 4); | ||
| 241 | if (IS_ERR(hw)) | ||
| 242 | goto err_free; | ||
| 243 | |||
| 244 | parent_names[0] = md_slck_name; | ||
| 245 | parent_names[1] = td_slck_name; | ||
| 246 | parent_names[2] = "mainck"; | ||
| 247 | parent_names[3] = "masterck"; | ||
| 248 | parent_names[4] = "pllack"; | ||
| 249 | parent_names[5] = "upllck"; | ||
| 250 | for (i = 0; i < 8; i++) { | ||
| 251 | char name[6]; | ||
| 252 | |||
| 253 | snprintf(name, sizeof(name), "prog%d", i); | ||
| 254 | |||
| 255 | hw = at91_clk_register_programmable(regmap, name, | ||
| 256 | parent_names, 6, i, | ||
| 257 | &sam9x60_programmable_layout); | ||
| 258 | if (IS_ERR(hw)) | ||
| 259 | goto err_free; | ||
| 260 | } | ||
| 261 | |||
| 262 | for (i = 0; i < ARRAY_SIZE(sam9x60_systemck); i++) { | ||
| 263 | hw = at91_clk_register_system(regmap, sam9x60_systemck[i].n, | ||
| 264 | sam9x60_systemck[i].p, | ||
| 265 | sam9x60_systemck[i].id); | ||
| 266 | if (IS_ERR(hw)) | ||
| 267 | goto err_free; | ||
| 268 | |||
| 269 | sam9x60_pmc->shws[sam9x60_systemck[i].id] = hw; | ||
| 270 | } | ||
| 271 | |||
| 272 | for (i = 0; i < ARRAY_SIZE(sam9x60_periphck); i++) { | ||
| 273 | hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock, | ||
| 274 | &sam9x60_pcr_layout, | ||
| 275 | sam9x60_periphck[i].n, | ||
| 276 | "masterck", | ||
| 277 | sam9x60_periphck[i].id, | ||
| 278 | &range); | ||
| 279 | if (IS_ERR(hw)) | ||
| 280 | goto err_free; | ||
| 281 | |||
| 282 | sam9x60_pmc->phws[sam9x60_periphck[i].id] = hw; | ||
| 283 | } | ||
| 284 | |||
| 285 | for (i = 0; i < ARRAY_SIZE(sam9x60_gck); i++) { | ||
| 286 | hw = at91_clk_register_generated(regmap, &pmc_pcr_lock, | ||
| 287 | &sam9x60_pcr_layout, | ||
| 288 | sam9x60_gck[i].n, | ||
| 289 | parent_names, 6, | ||
| 290 | sam9x60_gck[i].id, | ||
| 291 | sam9x60_gck[i].pll, | ||
| 292 | &sam9x60_gck[i].r); | ||
| 293 | if (IS_ERR(hw)) | ||
| 294 | goto err_free; | ||
| 295 | |||
| 296 | sam9x60_pmc->ghws[sam9x60_gck[i].id] = hw; | ||
| 297 | } | ||
| 298 | |||
| 299 | of_clk_add_hw_provider(np, of_clk_hw_pmc_get, sam9x60_pmc); | ||
| 300 | |||
| 301 | return; | ||
| 302 | |||
| 303 | err_free: | ||
| 304 | pmc_data_free(sam9x60_pmc); | ||
| 305 | } | ||
| 306 | /* Some clks are used for a clocksource */ | ||
| 307 | CLK_OF_DECLARE(sam9x60_pmc, "microchip,sam9x60-pmc", sam9x60_pmc_setup); | ||
