1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Slow clock support for AT91 architectures. 4 * 5 * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries 6 * 7 * Author: Claudiu Beznea <claudiu.beznea@microchip.com> 8 */ 9 10#include <common.h> 11#include <clk-uclass.h> 12#include <dm.h> 13#include <dt-bindings/clk/at91.h> 14#include <linux/clk-provider.h> 15 16#include "pmc.h" 17 18#define UBOOT_DM_CLK_AT91_SAM9X60_TD_SLCK "at91-sam9x60-td-slck" 19#define UBOOT_DM_CLK_AT91_SCKC "at91-sckc" 20 21#define AT91_OSC_SEL BIT(24) 22#define AT91_OSC_SEL_SHIFT (24) 23 24struct sam9x60_sckc { 25 void __iomem *reg; 26 const char **parent_names; 27 unsigned int num_parents; 28 struct clk clk; 29}; 30 31#define to_sam9x60_sckc(c) container_of(c, struct sam9x60_sckc, clk) 32 33static int sam9x60_sckc_of_xlate(struct clk *clk, 34 struct ofnode_phandle_args *args) 35{ 36 if (args->args_count != 1) { 37 debug("AT91: SCKC: Invalid args_count: %d\n", args->args_count); 38 return -EINVAL; 39 } 40 41 clk->id = AT91_TO_CLK_ID(PMC_TYPE_SLOW, args->args[0]); 42 43 return 0; 44} 45 46static const struct clk_ops sam9x60_sckc_ops = { 47 .of_xlate = sam9x60_sckc_of_xlate, 48 .get_rate = clk_generic_get_rate, 49}; 50 51static int sam9x60_td_slck_set_parent(struct clk *clk, struct clk *parent) 52{ 53 struct sam9x60_sckc *sckc = to_sam9x60_sckc(clk); 54 u32 i; 55 56 for (i = 0; i < sckc->num_parents; i++) { 57 if (!strcmp(parent->dev->name, sckc->parent_names[i])) 58 break; 59 } 60 if (i == sckc->num_parents) 61 return -EINVAL; 62 63 pmc_update_bits(sckc->reg, 0, AT91_OSC_SEL, (i << AT91_OSC_SEL_SHIFT)); 64 65 return 0; 66} 67 68static const struct clk_ops sam9x60_td_slck_ops = { 69 .get_rate = clk_generic_get_rate, 70 .set_parent = sam9x60_td_slck_set_parent, 71}; 72 73static struct clk *at91_sam9x60_clk_register_td_slck(struct sam9x60_sckc *sckc, 74 const char *name, const char * const *parent_names, 75 int num_parents) 76{ 77 struct clk *clk; 78 int ret = -ENOMEM; 79 u32 val, i; 80 81 if (!sckc || !name || !parent_names || num_parents != 2) 82 return ERR_PTR(-EINVAL); 83 84 sckc->parent_names = kzalloc(sizeof(*sckc->parent_names) * num_parents, 85 GFP_KERNEL); 86 if (!sckc->parent_names) 87 return ERR_PTR(ret); 88 89 for (i = 0; i < num_parents; i++) { 90 sckc->parent_names[i] = kmemdup(parent_names[i], 91 strlen(parent_names[i]) + 1, GFP_KERNEL); 92 if (!sckc->parent_names[i]) 93 goto free; 94 } 95 sckc->num_parents = num_parents; 96 97 pmc_read(sckc->reg, 0, &val); 98 val = (val & AT91_OSC_SEL) >> AT91_OSC_SEL_SHIFT; 99 100 clk = &sckc->clk; 101 ret = clk_register(clk, UBOOT_DM_CLK_AT91_SAM9X60_TD_SLCK, name, 102 parent_names[val]); 103 if (ret) 104 goto free; 105 106 return clk; 107 108free: 109 for (; i >= 0; i--) 110 kfree(sckc->parent_names[i]); 111 kfree(sckc->parent_names); 112 113 return ERR_PTR(ret); 114} 115 116U_BOOT_DRIVER(at91_sam9x60_td_slck) = { 117 .name = UBOOT_DM_CLK_AT91_SAM9X60_TD_SLCK, 118 .id = UCLASS_CLK, 119 .ops = &sam9x60_td_slck_ops, 120 .flags = DM_FLAG_PRE_RELOC, 121}; 122 123static int at91_sam9x60_sckc_probe(struct udevice *dev) 124{ 125 struct sam9x60_sckc *sckc = dev_get_priv(dev); 126 void __iomem *base = devfdt_get_addr_ptr(dev); 127 const char *slow_rc_osc, *slow_osc; 128 const char *parents[2]; 129 struct clk *clk, c; 130 int ret; 131 132 ret = clk_get_by_index(dev, 0, &c); 133 if (ret) 134 return ret; 135 slow_rc_osc = clk_hw_get_name(&c); 136 137 ret = clk_get_by_index(dev, 1, &c); 138 if (ret) 139 return ret; 140 slow_osc = clk_hw_get_name(&c); 141 142 clk = clk_register_fixed_factor(NULL, "md_slck", slow_rc_osc, 0, 1, 1); 143 if (IS_ERR(clk)) 144 return PTR_ERR(clk); 145 clk_dm(AT91_TO_CLK_ID(PMC_TYPE_SLOW, 0), clk); 146 147 parents[0] = slow_rc_osc; 148 parents[1] = slow_osc; 149 sckc[1].reg = base; 150 clk = at91_sam9x60_clk_register_td_slck(&sckc[1], "td_slck", 151 parents, 2); 152 if (IS_ERR(clk)) 153 return PTR_ERR(clk); 154 clk_dm(AT91_TO_CLK_ID(PMC_TYPE_SLOW, 1), clk); 155 156 return 0; 157} 158 159static const struct udevice_id sam9x60_sckc_ids[] = { 160 { .compatible = "microchip,sam9x60-sckc" }, 161 { /* Sentinel. */ }, 162}; 163 164U_BOOT_DRIVER(at91_sckc) = { 165 .name = UBOOT_DM_CLK_AT91_SCKC, 166 .id = UCLASS_CLK, 167 .of_match = sam9x60_sckc_ids, 168 .priv_auto = sizeof(struct sam9x60_sckc) * 2, 169 .ops = &sam9x60_sckc_ops, 170 .probe = at91_sam9x60_sckc_probe, 171 .flags = DM_FLAG_PRE_RELOC, 172}; 173