Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

clk: at91: add a driver for the h32mx clock

Newer SoCs have two different AHB interconnect. The AHB 32 bits Matrix
interconnect (h32mx) has a clock that can be setup at the half of the h64mx
clock (which is mck). The h32mx clock can not exceed 90 MHz.

Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
Acked-by: Boris Brezillon <boris.brezillon@free-electrons.com>
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>

authored by

Alexandre Belloni and committed by
Nicolas Ferre
bcc5fd49 5db722ee

+153
+14
Documentation/devicetree/bindings/clock/at91-clock.txt
··· 74 74 "atmel,at91sam9x5-clk-utmi": 75 75 at91 utmi clock 76 76 77 + "atmel,sama5d4-clk-h32mx": 78 + at91 h32mx clock 79 + 77 80 Required properties for SCKC node: 78 81 - reg : defines the IO memory reserved for the SCKC. 79 82 - #size-cells : shall be 0 (reg is used to encode clk id). ··· 449 446 interrupts = <AT91_PMC_LOCKU IRQ_TYPE_LEVEL_HIGH>; 450 447 #clock-cells = <0>; 451 448 clocks = <&main>; 449 + }; 450 + 451 + Required properties for 32 bits bus Matrix clock (h32mx clock): 452 + - #clock-cells : from common clock binding; shall be set to 0. 453 + - clocks : shall be the master clock source phandle. 454 + 455 + For example: 456 + h32ck: h32mxck { 457 + #clock-cells = <0>; 458 + compatible = "atmel,sama5d4-clk-h32mx"; 459 + clocks = <&mck>; 452 460 };
+3
arch/arm/mach-at91/Kconfig
··· 42 42 config HAVE_AT91_SMD 43 43 bool 44 44 45 + config HAVE_AT91_H32MX 46 + bool 47 + 45 48 config SOC_AT91SAM9 46 49 bool 47 50 select AT91_SAM9_TIME
+1
drivers/clk/at91/Makefile
··· 9 9 obj-$(CONFIG_HAVE_AT91_UTMI) += clk-utmi.o 10 10 obj-$(CONFIG_HAVE_AT91_USB_CLK) += clk-usb.o 11 11 obj-$(CONFIG_HAVE_AT91_SMD) += clk-smd.o 12 + obj-$(CONFIG_HAVE_AT91_H32MX) += clk-h32mx.o
+123
drivers/clk/at91/clk-h32mx.c
··· 1 + /* 2 + * clk-h32mx.c 3 + * 4 + * Copyright (C) 2014 Atmel 5 + * 6 + * Alexandre Belloni <alexandre.belloni@free-electrons.com> 7 + * 8 + * This program is free software; you can redistribute it and/or modify 9 + * it under the terms of the GNU General Public License as published by 10 + * the Free Software Foundation; either version 2 of the License, or 11 + * (at your option) any later version. 12 + * 13 + */ 14 + 15 + #include <linux/clk-provider.h> 16 + #include <linux/clkdev.h> 17 + #include <linux/clk/at91_pmc.h> 18 + #include <linux/delay.h> 19 + #include <linux/of.h> 20 + #include <linux/of_address.h> 21 + #include <linux/of_irq.h> 22 + #include <linux/io.h> 23 + #include <linux/interrupt.h> 24 + #include <linux/irq.h> 25 + #include <linux/sched.h> 26 + #include <linux/wait.h> 27 + 28 + #include "pmc.h" 29 + 30 + #define H32MX_MAX_FREQ 90000000 31 + 32 + struct clk_sama5d4_h32mx { 33 + struct clk_hw hw; 34 + struct at91_pmc *pmc; 35 + }; 36 + 37 + #define to_clk_sama5d4_h32mx(hw) container_of(hw, struct clk_sama5d4_h32mx, hw) 38 + 39 + static unsigned long clk_sama5d4_h32mx_recalc_rate(struct clk_hw *hw, 40 + unsigned long parent_rate) 41 + { 42 + struct clk_sama5d4_h32mx *h32mxclk = to_clk_sama5d4_h32mx(hw); 43 + 44 + if (pmc_read(h32mxclk->pmc, AT91_PMC_MCKR) & AT91_PMC_H32MXDIV) 45 + return parent_rate / 2; 46 + 47 + if (parent_rate > H32MX_MAX_FREQ) 48 + pr_warn("H32MX clock is too fast\n"); 49 + return parent_rate; 50 + } 51 + 52 + static long clk_sama5d4_h32mx_round_rate(struct clk_hw *hw, unsigned long rate, 53 + unsigned long *parent_rate) 54 + { 55 + unsigned long div; 56 + 57 + if (rate > *parent_rate) 58 + return *parent_rate; 59 + div = *parent_rate / 2; 60 + if (rate < div) 61 + return div; 62 + 63 + if (rate - div < *parent_rate - rate) 64 + return div; 65 + 66 + return *parent_rate; 67 + } 68 + 69 + static int clk_sama5d4_h32mx_set_rate(struct clk_hw *hw, unsigned long rate, 70 + unsigned long parent_rate) 71 + { 72 + struct clk_sama5d4_h32mx *h32mxclk = to_clk_sama5d4_h32mx(hw); 73 + struct at91_pmc *pmc = h32mxclk->pmc; 74 + u32 tmp; 75 + 76 + if (parent_rate != rate && (parent_rate / 2) != rate) 77 + return -EINVAL; 78 + 79 + pmc_lock(pmc); 80 + tmp = pmc_read(pmc, AT91_PMC_MCKR) & ~AT91_PMC_H32MXDIV; 81 + if ((parent_rate / 2) == rate) 82 + tmp |= AT91_PMC_H32MXDIV; 83 + pmc_write(pmc, AT91_PMC_MCKR, tmp); 84 + pmc_unlock(pmc); 85 + 86 + return 0; 87 + } 88 + 89 + static const struct clk_ops h32mx_ops = { 90 + .recalc_rate = clk_sama5d4_h32mx_recalc_rate, 91 + .round_rate = clk_sama5d4_h32mx_round_rate, 92 + .set_rate = clk_sama5d4_h32mx_set_rate, 93 + }; 94 + 95 + void __init of_sama5d4_clk_h32mx_setup(struct device_node *np, 96 + struct at91_pmc *pmc) 97 + { 98 + struct clk_sama5d4_h32mx *h32mxclk; 99 + struct clk_init_data init; 100 + const char *parent_name; 101 + struct clk *clk; 102 + 103 + h32mxclk = kzalloc(sizeof(*h32mxclk), GFP_KERNEL); 104 + if (!h32mxclk) 105 + return; 106 + 107 + parent_name = of_clk_get_parent_name(np, 0); 108 + 109 + init.name = np->name; 110 + init.ops = &h32mx_ops; 111 + init.parent_names = parent_name ? &parent_name : NULL; 112 + init.num_parents = parent_name ? 1 : 0; 113 + init.flags = CLK_SET_RATE_GATE; 114 + 115 + h32mxclk->hw.init = &init; 116 + h32mxclk->pmc = pmc; 117 + 118 + clk = clk_register(NULL, &h32mxclk->hw); 119 + if (!clk) 120 + return; 121 + 122 + of_clk_add_provider(np, of_clk_src_simple_get, clk); 123 + }
+6
drivers/clk/at91/pmc.c
··· 337 337 .data = of_at91sam9x5_clk_smd_setup, 338 338 }, 339 339 #endif 340 + #if defined(CONFIG_HAVE_AT91_H32MX) 341 + { 342 + .compatible = "atmel,sama5d4-clk-h32mx", 343 + .data = of_sama5d4_clk_h32mx_setup, 344 + }, 345 + #endif 340 346 { /*sentinel*/ } 341 347 }; 342 348
+5
drivers/clk/at91/pmc.h
··· 120 120 struct at91_pmc *pmc); 121 121 #endif 122 122 123 + #if defined(CONFIG_HAVE_AT91_SMD) 124 + extern void __init of_sama5d4_clk_h32mx_setup(struct device_node *np, 125 + struct at91_pmc *pmc); 126 + #endif 127 + 123 128 #endif /* __PMC_H_ */
+1
include/linux/clk/at91_pmc.h
··· 125 125 #define AT91_PMC_PLLADIV2 (1 << 12) /* PLLA divisor by 2 [some SAM9 only] */ 126 126 #define AT91_PMC_PLLADIV2_OFF (0 << 12) 127 127 #define AT91_PMC_PLLADIV2_ON (1 << 12) 128 + #define AT91_PMC_H32MXDIV BIT(24) 128 129 129 130 #define AT91_PMC_USB 0x38 /* USB Clock Register [some SAM9 only] */ 130 131 #define AT91_PMC_USBS (0x1 << 0) /* USB OHCI Input clock selection */