Merge branches 'clk-ti', 'clk-ingenic', 'clk-typo', 'clk-at91', 'clk-mmp2' and 'clk-arm-icst' into clk-next
- EHRPWM's TimeBase clock(TBCLK) for TI AM654 SoCs - Support PMC clks on at91sam9n12, at91rm9200, sama5d3, and at91sam9g45 SoCs - Fixes and improvements for the Marvell MMP2/MMP3 SoC clk drivers * clk-ti: clk: keystone: Add new driver to handle syscon based clocks dt-bindings: clock: Add binding documentation for TI EHRPWM TBCLK * clk-ingenic: clk: ingenic/TCU: Fix round_rate returning error clk: ingenic/jz4770: Exit with error if CGU init failed clk: JZ4780: Add function for enable the second core. clk: Ingenic: Add support for TCU of X1000. * clk-typo: clk: Fix trivia typo in comment exlusive => exclusive * clk-at91: clk: at91: add at91rm9200 pmc driver clk: at91: add at91sam9n12 pmc driver clk: at91: add sama5d3 pmc driver clk: at91: add at91sam9g45 pmc driver clk: at91: usb: introduce num_parents in driver's structure clk: at91: usb: use proper usbs_mask clk: at91: sam9x60: fix usb clock parents clk: at91: usb: continue if clk_hw_round_rate() return zero clk: at91: sam9x60: Don't use audio PLL * clk-mmp2: clk: mmp2: Fix bit masks for LCDC I/O and pixel clocks clk: mmp2: Add clock for fifth SD HCI on MMP3 dt-bindings: marvell,mmp2: Add clock id for the fifth SD HCI on MMP3 clk: mmp2: Add clocks for the thermal sensors dt-bindings: marvell,mmp2: Add clock ids for the thermal sensors clk: mmp2: add the GPU clocks dt-bindings: marvell,mmp2: Add clock ids for the GPU clocks clk: mmp2: Add PLLs that are available on MMP3 dt-bindings: marvell,mmp2: Add clock ids for MMP3 PLLs clk: mmp2: Check for MMP3 dt-bindings: clock: Add MMP3 compatible string clk: mmp2: Stop pretending PLL outputs are constant clk: mmp2: Add support for PLL clock sources dt-bindings: clock: Convert marvell,mmp2-clock to json-schema clk: mmp2: Constify some strings clk: mmp2: Remove a unused prototype * clk-arm-icst: MAINTAINERS: dt: update reference for arm-integrator.txt clk: versatile: Add device tree probing for IM-PD1 clocks clk: versatile: Export icst_clk_setup() dt-bindings: clock: Create YAML schema for ICST clocks
This commit is contained in:
commit
53a2cc5cc3
103
Documentation/devicetree/bindings/clock/arm,syscon-icst.yaml
Normal file
103
Documentation/devicetree/bindings/clock/arm,syscon-icst.yaml
Normal file
|
@ -0,0 +1,103 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/clock/arm,syscon-icst.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: ARM System Controller ICST Clocks
|
||||
|
||||
maintainers:
|
||||
- Linus Walleij <linusw@kernel.org>
|
||||
|
||||
description: |
|
||||
The ICS525 and ICS307 oscillators are produced by Integrated
|
||||
Devices Technology (IDT). ARM integrated these oscillators deeply into their
|
||||
reference designs by adding special control registers that manage such
|
||||
oscillators to their system controllers.
|
||||
|
||||
The various ARM system controllers contain logic to serialize and initialize
|
||||
an ICST clock request after a write to the 32 bit register at an offset
|
||||
into the system controller. Furthermore, to even be able to alter one of
|
||||
these frequencies, the system controller must first be unlocked by
|
||||
writing a special token to another offset in the system controller.
|
||||
|
||||
Some ARM hardware contain special versions of the serial interface that only
|
||||
connects the low 8 bits of the VDW (missing one bit), hard-wires RDW to
|
||||
different values and sometimes also hard-wires the output divider. They
|
||||
therefore have special compatible strings as per this table (the OD value is
|
||||
the value on the pins, not the resulting output divider).
|
||||
|
||||
In the core modules and logic tiles, the ICST is a configurable clock fed
|
||||
from a 24 MHz clock on the motherboard (usually the main crystal) used for
|
||||
generating e.g. video clocks. It is located on the core module and there is
|
||||
only one of these. This clock node must be a subnode of the core module.
|
||||
|
||||
Hardware variant RDW OD VDW
|
||||
|
||||
Integrator/AP 22 1 Bit 8 0, rest variable
|
||||
integratorap-cm
|
||||
|
||||
Integrator/AP 46 3 Bit 8 0, rest variable
|
||||
integratorap-sys
|
||||
|
||||
Integrator/AP 22 or 1 17 or (33 or 25 MHz)
|
||||
integratorap-pci 14 1 14
|
||||
|
||||
Integrator/CP 22 variable Bit 8 0, rest variable
|
||||
integratorcp-cm-core
|
||||
|
||||
Integrator/CP 22 variable Bit 8 0, rest variable
|
||||
integratorcp-cm-mem
|
||||
|
||||
The ICST oscillator must be provided inside a system controller node.
|
||||
|
||||
properties:
|
||||
"#clock-cells":
|
||||
const: 0
|
||||
|
||||
compatible:
|
||||
enum:
|
||||
- arm,syscon-icst525
|
||||
- arm,syscon-icst307
|
||||
- arm,syscon-icst525-integratorap-cm
|
||||
- arm,syscon-icst525-integratorap-sys
|
||||
- arm,syscon-icst525-integratorap-pci
|
||||
- arm,syscon-icst525-integratorcp-cm-core
|
||||
- arm,syscon-icst525-integratorcp-cm-mem
|
||||
- arm,integrator-cm-auxosc
|
||||
- arm,versatile-cm-auxosc
|
||||
- arm,impd-vco1
|
||||
- arm,impd-vco2
|
||||
|
||||
clocks:
|
||||
description: Parent clock for the ICST VCO
|
||||
maxItems: 1
|
||||
|
||||
clock-output-names:
|
||||
maxItems: 1
|
||||
|
||||
lock-offset:
|
||||
$ref: '/schemas/types.yaml#/definitions/uint32'
|
||||
description: Offset to the unlocking register for the oscillator
|
||||
|
||||
vco-offset:
|
||||
$ref: '/schemas/types.yaml#/definitions/uint32'
|
||||
description: Offset to the VCO register for the oscillator
|
||||
|
||||
required:
|
||||
- "#clock-cells"
|
||||
- compatible
|
||||
- clocks
|
||||
|
||||
examples:
|
||||
- |
|
||||
vco1: clock@00 {
|
||||
compatible = "arm,impd1-vco1";
|
||||
#clock-cells = <0>;
|
||||
lock-offset = <0x08>;
|
||||
vco-offset = <0x00>;
|
||||
clocks = <&sysclk>;
|
||||
clock-output-names = "IM-PD1-VCO1";
|
||||
};
|
||||
|
||||
...
|
|
@ -1,34 +0,0 @@
|
|||
Clock bindings for ARM Integrator and Versatile Core Module clocks
|
||||
|
||||
Auxiliary Oscillator Clock
|
||||
|
||||
This is a configurable clock fed from a 24 MHz chrystal,
|
||||
used for generating e.g. video clocks. It is located on the
|
||||
core module and there is only one of these.
|
||||
|
||||
This clock node *must* be a subnode of the core module, since
|
||||
it obtains the base address for it's address range from its
|
||||
parent node.
|
||||
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "arm,integrator-cm-auxosc" or "arm,versatile-cm-auxosc"
|
||||
- #clock-cells: must be <0>
|
||||
|
||||
Optional properties:
|
||||
- clocks: parent clock(s)
|
||||
|
||||
Example:
|
||||
|
||||
core-module@10000000 {
|
||||
xtal24mhz: xtal24mhz@24M {
|
||||
#clock-cells = <0>;
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <24000000>;
|
||||
};
|
||||
auxosc: cm_aux_osc@25M {
|
||||
#clock-cells = <0>;
|
||||
compatible = "arm,integrator-cm-auxosc";
|
||||
clocks = <&xtal24mhz>;
|
||||
};
|
||||
};
|
|
@ -1,70 +0,0 @@
|
|||
ARM System Controller ICST clocks
|
||||
|
||||
The ICS525 and ICS307 oscillators are produced by Integrated Devices
|
||||
Technology (IDT). ARM integrated these oscillators deeply into their
|
||||
reference designs by adding special control registers that manage such
|
||||
oscillators to their system controllers.
|
||||
|
||||
The various ARM system controllers contain logic to serialize and initialize
|
||||
an ICST clock request after a write to the 32 bit register at an offset
|
||||
into the system controller. Furthermore, to even be able to alter one of
|
||||
these frequencies, the system controller must first be unlocked by
|
||||
writing a special token to another offset in the system controller.
|
||||
|
||||
Some ARM hardware contain special versions of the serial interface that only
|
||||
connects the low 8 bits of the VDW (missing one bit), hardwires RDW to
|
||||
different values and sometimes also hardwire the output divider. They
|
||||
therefore have special compatible strings as per this table (the OD value is
|
||||
the value on the pins, not the resulting output divider):
|
||||
|
||||
Hardware variant: RDW OD VDW
|
||||
|
||||
Integrator/AP 22 1 Bit 8 0, rest variable
|
||||
integratorap-cm
|
||||
|
||||
Integrator/AP 46 3 Bit 8 0, rest variable
|
||||
integratorap-sys
|
||||
|
||||
Integrator/AP 22 or 1 17 or (33 or 25 MHz)
|
||||
integratorap-pci 14 1 14
|
||||
|
||||
Integrator/CP 22 variable Bit 8 0, rest variable
|
||||
integratorcp-cm-core
|
||||
|
||||
Integrator/CP 22 variable Bit 8 0, rest variable
|
||||
integratorcp-cm-mem
|
||||
|
||||
The ICST oscillator must be provided inside a system controller node.
|
||||
|
||||
Required properties:
|
||||
- compatible: must be one of
|
||||
"arm,syscon-icst525"
|
||||
"arm,syscon-icst307"
|
||||
"arm,syscon-icst525-integratorap-cm"
|
||||
"arm,syscon-icst525-integratorap-sys"
|
||||
"arm,syscon-icst525-integratorap-pci"
|
||||
"arm,syscon-icst525-integratorcp-cm-core"
|
||||
"arm,syscon-icst525-integratorcp-cm-mem"
|
||||
- lock-offset: the offset address into the system controller where the
|
||||
unlocking register is located
|
||||
- vco-offset: the offset address into the system controller where the
|
||||
ICST control register is located (even 32 bit address)
|
||||
- #clock-cells: must be <0>
|
||||
- clocks: parent clock, since the ICST needs a parent clock to derive its
|
||||
frequency from, this attribute is compulsory.
|
||||
|
||||
Example:
|
||||
|
||||
syscon: syscon@10000000 {
|
||||
compatible = "syscon";
|
||||
reg = <0x10000000 0x1000>;
|
||||
|
||||
oscclk0: osc0@c {
|
||||
compatible = "arm,syscon-icst307";
|
||||
#clock-cells = <0>;
|
||||
lock-offset = <0x20>;
|
||||
vco-offset = <0x0c>;
|
||||
clocks = <&xtal24mhz>;
|
||||
};
|
||||
(...)
|
||||
};
|
|
@ -0,0 +1,64 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/clock/marvell,mmp2-clock.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Marvell MMP2 and MMP3 Clock Controller
|
||||
|
||||
maintainers:
|
||||
- Lubomir Rintel <lkundrak@v3.sk>
|
||||
|
||||
description: |
|
||||
The clock subsystem on MMP2 or MMP3 generates and supplies clock to various
|
||||
controllers within the SoC.
|
||||
|
||||
Each clock is assigned an identifier and client nodes use this identifier
|
||||
to specify the clock which they consume.
|
||||
|
||||
All these identifiers could be found in <dt-bindings/clock/marvell,mmp2.h>.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- marvell,mmp2-clock # controller compatible with MMP2 SoC
|
||||
- marvell,mmp3-clock # controller compatible with MMP3 SoC
|
||||
|
||||
reg:
|
||||
items:
|
||||
- description: MPMU register region
|
||||
- description: APMU register region
|
||||
- description: APBC register region
|
||||
|
||||
reg-names:
|
||||
items:
|
||||
- const: mpmu
|
||||
- const: apmu
|
||||
- const: apbc
|
||||
|
||||
'#clock-cells':
|
||||
const: 1
|
||||
|
||||
'#reset-cells':
|
||||
const: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- reg-names
|
||||
- '#clock-cells'
|
||||
- '#reset-cells'
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
clock-controller@d4050000 {
|
||||
compatible = "marvell,mmp2-clock";
|
||||
reg = <0xd4050000 0x1000>,
|
||||
<0xd4282800 0x400>,
|
||||
<0xd4015000 0x1000>;
|
||||
reg-names = "mpmu", "apmu", "apbc";
|
||||
#clock-cells = <1>;
|
||||
#reset-cells = <1>;
|
||||
};
|
|
@ -1,21 +0,0 @@
|
|||
* Marvell MMP2 Clock Controller
|
||||
|
||||
The MMP2 clock subsystem generates and supplies clock to various
|
||||
controllers within the MMP2 SoC.
|
||||
|
||||
Required Properties:
|
||||
|
||||
- compatible: should be one of the following.
|
||||
- "marvell,mmp2-clock" - controller compatible with MMP2 SoC.
|
||||
|
||||
- reg: physical base address of the clock subsystem and length of memory mapped
|
||||
region. There are 3 places in SOC has clock control logic:
|
||||
"mpmu", "apmu", "apbc". So three reg spaces need to be defined.
|
||||
|
||||
- #clock-cells: should be 1.
|
||||
- #reset-cells: should be 1.
|
||||
|
||||
Each clock is assigned an identifier and client nodes use this identifier
|
||||
to specify the clock which they consume.
|
||||
|
||||
All these identifiers could be found in <dt-bindings/clock/marvell,mmp2.h>.
|
|
@ -0,0 +1,35 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/clock/ti,am654-ehrpwm-tbclk.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: TI EHRPWM Time Base Clock
|
||||
|
||||
maintainers:
|
||||
- Vignesh Raghavendra <vigneshr@ti.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- const: ti,am654-ehrpwm-tbclk
|
||||
- const: syscon
|
||||
|
||||
"#clock-cells":
|
||||
const: 1
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- "#clock-cells"
|
||||
- reg
|
||||
|
||||
examples:
|
||||
- |
|
||||
ehrpwm_tbclk: syscon@4140 {
|
||||
compatible = "ti,am654-ehrpwm-tbclk", "syscon";
|
||||
reg = <0x4140 0x18>;
|
||||
#clock-cells = <1>;
|
||||
};
|
|
@ -1280,7 +1280,7 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
|||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/arm/arm-boards
|
||||
F: Documentation/devicetree/bindings/auxdisplay/arm-charlcd.txt
|
||||
F: Documentation/devicetree/bindings/clock/arm-integrator.txt
|
||||
F: Documentation/devicetree/bindings/clock/arm,syscon-icst.yaml
|
||||
F: Documentation/devicetree/bindings/i2c/i2c-versatile.txt
|
||||
F: Documentation/devicetree/bindings/interrupt-controller/arm,versatile-fpga-irq.txt
|
||||
F: Documentation/devicetree/bindings/mtd/arm-versatile.txt
|
||||
|
|
|
@ -15,7 +15,11 @@ obj-$(CONFIG_HAVE_AT91_H32MX) += clk-h32mx.o
|
|||
obj-$(CONFIG_HAVE_AT91_GENERATED_CLK) += clk-generated.o
|
||||
obj-$(CONFIG_HAVE_AT91_I2S_MUX_CLK) += clk-i2s-mux.o
|
||||
obj-$(CONFIG_HAVE_AT91_SAM9X60_PLL) += clk-sam9x60-pll.o
|
||||
obj-$(CONFIG_SOC_AT91RM9200) += at91rm9200.o
|
||||
obj-$(CONFIG_SOC_AT91SAM9) += at91sam9260.o at91sam9rl.o at91sam9x5.o
|
||||
obj-$(CONFIG_SOC_AT91SAM9) += at91sam9g45.o
|
||||
obj-$(CONFIG_SOC_AT91SAM9) += at91sam9n12.o at91sam9x5.o
|
||||
obj-$(CONFIG_SOC_SAM9X60) += sam9x60.o
|
||||
obj-$(CONFIG_SOC_SAMA5D3) += sama5d3.o
|
||||
obj-$(CONFIG_SOC_SAMA5D4) += sama5d4.o
|
||||
obj-$(CONFIG_SOC_SAMA5D2) += sama5d2.o
|
||||
|
|
199
drivers/clk/at91/at91rm9200.c
Normal file
199
drivers/clk/at91/at91rm9200.c
Normal file
|
@ -0,0 +1,199 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <dt-bindings/clock/at91.h>
|
||||
|
||||
#include "pmc.h"
|
||||
|
||||
struct sck {
|
||||
char *n;
|
||||
char *p;
|
||||
u8 id;
|
||||
};
|
||||
|
||||
struct pck {
|
||||
char *n;
|
||||
u8 id;
|
||||
};
|
||||
|
||||
static const struct clk_master_characteristics rm9200_mck_characteristics = {
|
||||
.output = { .min = 0, .max = 80000000 },
|
||||
.divisors = { 1, 2, 3, 4 },
|
||||
};
|
||||
|
||||
static u8 rm9200_pll_out[] = { 0, 2 };
|
||||
|
||||
static const struct clk_range rm9200_pll_outputs[] = {
|
||||
{ .min = 80000000, .max = 160000000 },
|
||||
{ .min = 150000000, .max = 180000000 },
|
||||
};
|
||||
|
||||
static const struct clk_pll_characteristics rm9200_pll_characteristics = {
|
||||
.input = { .min = 1000000, .max = 32000000 },
|
||||
.num_output = ARRAY_SIZE(rm9200_pll_outputs),
|
||||
.output = rm9200_pll_outputs,
|
||||
.out = rm9200_pll_out,
|
||||
};
|
||||
|
||||
static const struct sck at91rm9200_systemck[] = {
|
||||
{ .n = "udpck", .p = "usbck", .id = 2 },
|
||||
{ .n = "uhpck", .p = "usbck", .id = 4 },
|
||||
{ .n = "pck0", .p = "prog0", .id = 8 },
|
||||
{ .n = "pck1", .p = "prog1", .id = 9 },
|
||||
{ .n = "pck2", .p = "prog2", .id = 10 },
|
||||
{ .n = "pck3", .p = "prog3", .id = 11 },
|
||||
};
|
||||
|
||||
static const struct pck at91rm9200_periphck[] = {
|
||||
{ .n = "pioA_clk", .id = 2 },
|
||||
{ .n = "pioB_clk", .id = 3 },
|
||||
{ .n = "pioC_clk", .id = 4 },
|
||||
{ .n = "pioD_clk", .id = 5 },
|
||||
{ .n = "usart0_clk", .id = 6 },
|
||||
{ .n = "usart1_clk", .id = 7 },
|
||||
{ .n = "usart2_clk", .id = 8 },
|
||||
{ .n = "usart3_clk", .id = 9 },
|
||||
{ .n = "mci0_clk", .id = 10 },
|
||||
{ .n = "udc_clk", .id = 11 },
|
||||
{ .n = "twi0_clk", .id = 12 },
|
||||
{ .n = "spi0_clk", .id = 13 },
|
||||
{ .n = "ssc0_clk", .id = 14 },
|
||||
{ .n = "ssc1_clk", .id = 15 },
|
||||
{ .n = "ssc2_clk", .id = 16 },
|
||||
{ .n = "tc0_clk", .id = 17 },
|
||||
{ .n = "tc1_clk", .id = 18 },
|
||||
{ .n = "tc2_clk", .id = 19 },
|
||||
{ .n = "tc3_clk", .id = 20 },
|
||||
{ .n = "tc4_clk", .id = 21 },
|
||||
{ .n = "tc5_clk", .id = 22 },
|
||||
{ .n = "ohci_clk", .id = 23 },
|
||||
{ .n = "macb0_clk", .id = 24 },
|
||||
};
|
||||
|
||||
static void __init at91rm9200_pmc_setup(struct device_node *np)
|
||||
{
|
||||
const char *slowxtal_name, *mainxtal_name;
|
||||
struct pmc_data *at91rm9200_pmc;
|
||||
u32 usb_div[] = { 1, 2, 0, 0 };
|
||||
const char *parent_names[6];
|
||||
struct regmap *regmap;
|
||||
struct clk_hw *hw;
|
||||
int i;
|
||||
bool bypass;
|
||||
|
||||
i = of_property_match_string(np, "clock-names", "slow_xtal");
|
||||
if (i < 0)
|
||||
return;
|
||||
|
||||
slowxtal_name = of_clk_get_parent_name(np, i);
|
||||
|
||||
i = of_property_match_string(np, "clock-names", "main_xtal");
|
||||
if (i < 0)
|
||||
return;
|
||||
mainxtal_name = of_clk_get_parent_name(np, i);
|
||||
|
||||
regmap = device_node_to_regmap(np);
|
||||
if (IS_ERR(regmap))
|
||||
return;
|
||||
|
||||
at91rm9200_pmc = pmc_data_allocate(PMC_MAIN + 1,
|
||||
nck(at91rm9200_systemck),
|
||||
nck(at91rm9200_periphck), 0);
|
||||
if (!at91rm9200_pmc)
|
||||
return;
|
||||
|
||||
bypass = of_property_read_bool(np, "atmel,osc-bypass");
|
||||
|
||||
hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name,
|
||||
bypass);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
hw = at91_clk_register_rm9200_main(regmap, "mainck", "main_osc");
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
at91rm9200_pmc->chws[PMC_MAIN] = hw;
|
||||
|
||||
hw = at91_clk_register_pll(regmap, "pllack", "mainck", 0,
|
||||
&at91rm9200_pll_layout,
|
||||
&rm9200_pll_characteristics);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
hw = at91_clk_register_pll(regmap, "pllbck", "mainck", 1,
|
||||
&at91rm9200_pll_layout,
|
||||
&rm9200_pll_characteristics);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
parent_names[0] = slowxtal_name;
|
||||
parent_names[1] = "mainck";
|
||||
parent_names[2] = "pllack";
|
||||
parent_names[3] = "pllbck";
|
||||
hw = at91_clk_register_master(regmap, "masterck", 4, parent_names,
|
||||
&at91rm9200_master_layout,
|
||||
&rm9200_mck_characteristics);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
at91rm9200_pmc->chws[PMC_MCK] = hw;
|
||||
|
||||
hw = at91rm9200_clk_register_usb(regmap, "usbck", "pllbck", usb_div);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
parent_names[0] = slowxtal_name;
|
||||
parent_names[1] = "mainck";
|
||||
parent_names[2] = "pllack";
|
||||
parent_names[3] = "pllbck";
|
||||
for (i = 0; i < 4; i++) {
|
||||
char name[6];
|
||||
|
||||
snprintf(name, sizeof(name), "prog%d", i);
|
||||
|
||||
hw = at91_clk_register_programmable(regmap, name,
|
||||
parent_names, 4, i,
|
||||
&at91rm9200_programmable_layout);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(at91rm9200_systemck); i++) {
|
||||
hw = at91_clk_register_system(regmap, at91rm9200_systemck[i].n,
|
||||
at91rm9200_systemck[i].p,
|
||||
at91rm9200_systemck[i].id);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
at91rm9200_pmc->shws[at91rm9200_systemck[i].id] = hw;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(at91rm9200_periphck); i++) {
|
||||
hw = at91_clk_register_peripheral(regmap,
|
||||
at91rm9200_periphck[i].n,
|
||||
"masterck",
|
||||
at91rm9200_periphck[i].id);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
at91rm9200_pmc->phws[at91rm9200_periphck[i].id] = hw;
|
||||
}
|
||||
|
||||
of_clk_add_hw_provider(np, of_clk_hw_pmc_get, at91rm9200_pmc);
|
||||
|
||||
return;
|
||||
|
||||
err_free:
|
||||
pmc_data_free(at91rm9200_pmc);
|
||||
}
|
||||
/*
|
||||
* While the TCB can be used as the clocksource, the system timer is most likely
|
||||
* to be used instead. However, the pinctrl driver doesn't support probe
|
||||
* deferring properly. Once this is fixed, this can be switched to a platform
|
||||
* driver.
|
||||
*/
|
||||
CLK_OF_DECLARE_DRIVER(at91rm9200_pmc, "atmel,at91rm9200-pmc",
|
||||
at91rm9200_pmc_setup);
|
220
drivers/clk/at91/at91sam9g45.c
Normal file
220
drivers/clk/at91/at91sam9g45.c
Normal file
|
@ -0,0 +1,220 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <dt-bindings/clock/at91.h>
|
||||
|
||||
#include "pmc.h"
|
||||
|
||||
static const struct clk_master_characteristics mck_characteristics = {
|
||||
.output = { .min = 0, .max = 133333333 },
|
||||
.divisors = { 1, 2, 4, 3 },
|
||||
};
|
||||
|
||||
static u8 plla_out[] = { 0, 1, 2, 3, 0, 1, 2, 3 };
|
||||
|
||||
static u16 plla_icpll[] = { 0, 0, 0, 0, 1, 1, 1, 1 };
|
||||
|
||||
static const struct clk_range plla_outputs[] = {
|
||||
{ .min = 745000000, .max = 800000000 },
|
||||
{ .min = 695000000, .max = 750000000 },
|
||||
{ .min = 645000000, .max = 700000000 },
|
||||
{ .min = 595000000, .max = 650000000 },
|
||||
{ .min = 545000000, .max = 600000000 },
|
||||
{ .min = 495000000, .max = 555000000 },
|
||||
{ .min = 445000000, .max = 500000000 },
|
||||
{ .min = 400000000, .max = 450000000 },
|
||||
};
|
||||
|
||||
static const struct clk_pll_characteristics plla_characteristics = {
|
||||
.input = { .min = 2000000, .max = 32000000 },
|
||||
.num_output = ARRAY_SIZE(plla_outputs),
|
||||
.output = plla_outputs,
|
||||
.icpll = plla_icpll,
|
||||
.out = plla_out,
|
||||
};
|
||||
|
||||
static const struct {
|
||||
char *n;
|
||||
char *p;
|
||||
u8 id;
|
||||
} at91sam9g45_systemck[] = {
|
||||
{ .n = "ddrck", .p = "masterck", .id = 2 },
|
||||
{ .n = "uhpck", .p = "usbck", .id = 6 },
|
||||
{ .n = "pck0", .p = "prog0", .id = 8 },
|
||||
{ .n = "pck1", .p = "prog1", .id = 9 },
|
||||
};
|
||||
|
||||
static const struct clk_pcr_layout at91sam9g45_pcr_layout = {
|
||||
.offset = 0x10c,
|
||||
.cmd = BIT(12),
|
||||
.pid_mask = GENMASK(5, 0),
|
||||
.div_mask = GENMASK(17, 16),
|
||||
};
|
||||
|
||||
struct pck {
|
||||
char *n;
|
||||
u8 id;
|
||||
};
|
||||
|
||||
static const struct pck at91sam9g45_periphck[] = {
|
||||
{ .n = "pioA_clk", .id = 2, },
|
||||
{ .n = "pioB_clk", .id = 3, },
|
||||
{ .n = "pioC_clk", .id = 4, },
|
||||
{ .n = "pioDE_clk", .id = 5, },
|
||||
{ .n = "trng_clk", .id = 6, },
|
||||
{ .n = "usart0_clk", .id = 7, },
|
||||
{ .n = "usart1_clk", .id = 8, },
|
||||
{ .n = "usart2_clk", .id = 9, },
|
||||
{ .n = "usart3_clk", .id = 10, },
|
||||
{ .n = "mci0_clk", .id = 11, },
|
||||
{ .n = "twi0_clk", .id = 12, },
|
||||
{ .n = "twi1_clk", .id = 13, },
|
||||
{ .n = "spi0_clk", .id = 14, },
|
||||
{ .n = "spi1_clk", .id = 15, },
|
||||
{ .n = "ssc0_clk", .id = 16, },
|
||||
{ .n = "ssc1_clk", .id = 17, },
|
||||
{ .n = "tcb0_clk", .id = 18, },
|
||||
{ .n = "pwm_clk", .id = 19, },
|
||||
{ .n = "adc_clk", .id = 20, },
|
||||
{ .n = "dma0_clk", .id = 21, },
|
||||
{ .n = "uhphs_clk", .id = 22, },
|
||||
{ .n = "lcd_clk", .id = 23, },
|
||||
{ .n = "ac97_clk", .id = 24, },
|
||||
{ .n = "macb0_clk", .id = 25, },
|
||||
{ .n = "isi_clk", .id = 26, },
|
||||
{ .n = "udphs_clk", .id = 27, },
|
||||
{ .n = "aestdessha_clk", .id = 28, },
|
||||
{ .n = "mci1_clk", .id = 29, },
|
||||
{ .n = "vdec_clk", .id = 30, },
|
||||
};
|
||||
|
||||
static void __init at91sam9g45_pmc_setup(struct device_node *np)
|
||||
{
|
||||
const char *slck_name, *mainxtal_name;
|
||||
struct pmc_data *at91sam9g45_pmc;
|
||||
const char *parent_names[6];
|
||||
struct regmap *regmap;
|
||||
struct clk_hw *hw;
|
||||
int i;
|
||||
bool bypass;
|
||||
|
||||
i = of_property_match_string(np, "clock-names", "slow_clk");
|
||||
if (i < 0)
|
||||
return;
|
||||
|
||||
slck_name = of_clk_get_parent_name(np, i);
|
||||
|
||||
i = of_property_match_string(np, "clock-names", "main_xtal");
|
||||
if (i < 0)
|
||||
return;
|
||||
mainxtal_name = of_clk_get_parent_name(np, i);
|
||||
|
||||
regmap = syscon_node_to_regmap(np);
|
||||
if (IS_ERR(regmap))
|
||||
return;
|
||||
|
||||
at91sam9g45_pmc = pmc_data_allocate(PMC_MAIN + 1,
|
||||
nck(at91sam9g45_systemck),
|
||||
nck(at91sam9g45_periphck), 0);
|
||||
if (!at91sam9g45_pmc)
|
||||
return;
|
||||
|
||||
bypass = of_property_read_bool(np, "atmel,osc-bypass");
|
||||
|
||||
hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name,
|
||||
bypass);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
hw = at91_clk_register_rm9200_main(regmap, "mainck", "main_osc");
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
at91sam9g45_pmc->chws[PMC_MAIN] = hw;
|
||||
|
||||
hw = at91_clk_register_pll(regmap, "pllack", "mainck", 0,
|
||||
&at91rm9200_pll_layout, &plla_characteristics);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
hw = at91_clk_register_plldiv(regmap, "plladivck", "pllack");
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
hw = at91_clk_register_utmi(regmap, NULL, "utmick", "mainck");
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
at91sam9g45_pmc->chws[PMC_UTMI] = hw;
|
||||
|
||||
parent_names[0] = slck_name;
|
||||
parent_names[1] = "mainck";
|
||||
parent_names[2] = "plladivck";
|
||||
parent_names[3] = "utmick";
|
||||
hw = at91_clk_register_master(regmap, "masterck", 4, parent_names,
|
||||
&at91rm9200_master_layout,
|
||||
&mck_characteristics);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
at91sam9g45_pmc->chws[PMC_MCK] = hw;
|
||||
|
||||
parent_names[0] = "plladivck";
|
||||
parent_names[1] = "utmick";
|
||||
hw = at91sam9x5_clk_register_usb(regmap, "usbck", parent_names, 2);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
parent_names[0] = slck_name;
|
||||
parent_names[1] = "mainck";
|
||||
parent_names[2] = "plladivck";
|
||||
parent_names[3] = "utmick";
|
||||
parent_names[4] = "masterck";
|
||||
for (i = 0; i < 2; i++) {
|
||||
char name[6];
|
||||
|
||||
snprintf(name, sizeof(name), "prog%d", i);
|
||||
|
||||
hw = at91_clk_register_programmable(regmap, name,
|
||||
parent_names, 5, i,
|
||||
&at91sam9g45_programmable_layout);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(at91sam9g45_systemck); i++) {
|
||||
hw = at91_clk_register_system(regmap, at91sam9g45_systemck[i].n,
|
||||
at91sam9g45_systemck[i].p,
|
||||
at91sam9g45_systemck[i].id);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
at91sam9g45_pmc->shws[at91sam9g45_systemck[i].id] = hw;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(at91sam9g45_periphck); i++) {
|
||||
hw = at91_clk_register_peripheral(regmap,
|
||||
at91sam9g45_periphck[i].n,
|
||||
"masterck",
|
||||
at91sam9g45_periphck[i].id);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
at91sam9g45_pmc->phws[at91sam9g45_periphck[i].id] = hw;
|
||||
}
|
||||
|
||||
of_clk_add_hw_provider(np, of_clk_hw_pmc_get, at91sam9g45_pmc);
|
||||
|
||||
return;
|
||||
|
||||
err_free:
|
||||
pmc_data_free(at91sam9g45_pmc);
|
||||
}
|
||||
/*
|
||||
* The TCB is used as the clocksource so its clock is needed early. This means
|
||||
* this can't be a platform driver.
|
||||
*/
|
||||
CLK_OF_DECLARE_DRIVER(at91sam9g45_pmc, "atmel,at91sam9g45-pmc",
|
||||
at91sam9g45_pmc_setup);
|
238
drivers/clk/at91/at91sam9n12.c
Normal file
238
drivers/clk/at91/at91sam9n12.c
Normal file
|
@ -0,0 +1,238 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <dt-bindings/clock/at91.h>
|
||||
|
||||
#include "pmc.h"
|
||||
|
||||
static const struct clk_master_characteristics mck_characteristics = {
|
||||
.output = { .min = 0, .max = 133333333 },
|
||||
.divisors = { 1, 2, 4, 3 },
|
||||
.have_div3_pres = 1,
|
||||
};
|
||||
|
||||
static u8 plla_out[] = { 0, 1, 2, 3, 0, 1, 2, 3 };
|
||||
|
||||
static u16 plla_icpll[] = { 0, 0, 0, 0, 1, 1, 1, 1 };
|
||||
|
||||
static const struct clk_range plla_outputs[] = {
|
||||
{ .min = 745000000, .max = 800000000 },
|
||||
{ .min = 695000000, .max = 750000000 },
|
||||
{ .min = 645000000, .max = 700000000 },
|
||||
{ .min = 595000000, .max = 650000000 },
|
||||
{ .min = 545000000, .max = 600000000 },
|
||||
{ .min = 495000000, .max = 555000000 },
|
||||
{ .min = 445000000, .max = 500000000 },
|
||||
{ .min = 400000000, .max = 450000000 },
|
||||
};
|
||||
|
||||
static const struct clk_pll_characteristics plla_characteristics = {
|
||||
.input = { .min = 2000000, .max = 32000000 },
|
||||
.num_output = ARRAY_SIZE(plla_outputs),
|
||||
.output = plla_outputs,
|
||||
.icpll = plla_icpll,
|
||||
.out = plla_out,
|
||||
};
|
||||
|
||||
static u8 pllb_out[] = { 0 };
|
||||
|
||||
static const struct clk_range pllb_outputs[] = {
|
||||
{ .min = 30000000, .max = 100000000 },
|
||||
};
|
||||
|
||||
static const struct clk_pll_characteristics pllb_characteristics = {
|
||||
.input = { .min = 2000000, .max = 32000000 },
|
||||
.num_output = ARRAY_SIZE(pllb_outputs),
|
||||
.output = pllb_outputs,
|
||||
.out = pllb_out,
|
||||
};
|
||||
|
||||
static const struct {
|
||||
char *n;
|
||||
char *p;
|
||||
u8 id;
|
||||
} at91sam9n12_systemck[] = {
|
||||
{ .n = "ddrck", .p = "masterck", .id = 2 },
|
||||
{ .n = "lcdck", .p = "masterck", .id = 3 },
|
||||
{ .n = "uhpck", .p = "usbck", .id = 6 },
|
||||
{ .n = "udpck", .p = "usbck", .id = 7 },
|
||||
{ .n = "pck0", .p = "prog0", .id = 8 },
|
||||
{ .n = "pck1", .p = "prog1", .id = 9 },
|
||||
};
|
||||
|
||||
static const struct clk_pcr_layout at91sam9n12_pcr_layout = {
|
||||
.offset = 0x10c,
|
||||
.cmd = BIT(12),
|
||||
.pid_mask = GENMASK(5, 0),
|
||||
.div_mask = GENMASK(17, 16),
|
||||
};
|
||||
|
||||
struct pck {
|
||||
char *n;
|
||||
u8 id;
|
||||
};
|
||||
|
||||
static const struct pck at91sam9n12_periphck[] = {
|
||||
{ .n = "pioAB_clk", .id = 2, },
|
||||
{ .n = "pioCD_clk", .id = 3, },
|
||||
{ .n = "fuse_clk", .id = 4, },
|
||||
{ .n = "usart0_clk", .id = 5, },
|
||||
{ .n = "usart1_clk", .id = 6, },
|
||||
{ .n = "usart2_clk", .id = 7, },
|
||||
{ .n = "usart3_clk", .id = 8, },
|
||||
{ .n = "twi0_clk", .id = 9, },
|
||||
{ .n = "twi1_clk", .id = 10, },
|
||||
{ .n = "mci0_clk", .id = 12, },
|
||||
{ .n = "spi0_clk", .id = 13, },
|
||||
{ .n = "spi1_clk", .id = 14, },
|
||||
{ .n = "uart0_clk", .id = 15, },
|
||||
{ .n = "uart1_clk", .id = 16, },
|
||||
{ .n = "tcb_clk", .id = 17, },
|
||||
{ .n = "pwm_clk", .id = 18, },
|
||||
{ .n = "adc_clk", .id = 19, },
|
||||
{ .n = "dma0_clk", .id = 20, },
|
||||
{ .n = "uhphs_clk", .id = 22, },
|
||||
{ .n = "udphs_clk", .id = 23, },
|
||||
{ .n = "lcdc_clk", .id = 25, },
|
||||
{ .n = "sha_clk", .id = 27, },
|
||||
{ .n = "ssc0_clk", .id = 28, },
|
||||
{ .n = "aes_clk", .id = 29, },
|
||||
{ .n = "trng_clk", .id = 30, },
|
||||
};
|
||||
|
||||
static void __init at91sam9n12_pmc_setup(struct device_node *np)
|
||||
{
|
||||
struct clk_range range = CLK_RANGE(0, 0);
|
||||
const char *slck_name, *mainxtal_name;
|
||||
struct pmc_data *at91sam9n12_pmc;
|
||||
const char *parent_names[6];
|
||||
struct regmap *regmap;
|
||||
struct clk_hw *hw;
|
||||
int i;
|
||||
bool bypass;
|
||||
|
||||
i = of_property_match_string(np, "clock-names", "slow_clk");
|
||||
if (i < 0)
|
||||
return;
|
||||
|
||||
slck_name = of_clk_get_parent_name(np, i);
|
||||
|
||||
i = of_property_match_string(np, "clock-names", "main_xtal");
|
||||
if (i < 0)
|
||||
return;
|
||||
mainxtal_name = of_clk_get_parent_name(np, i);
|
||||
|
||||
regmap = syscon_node_to_regmap(np);
|
||||
if (IS_ERR(regmap))
|
||||
return;
|
||||
|
||||
at91sam9n12_pmc = pmc_data_allocate(PMC_MAIN + 1,
|
||||
nck(at91sam9n12_systemck), 31, 0);
|
||||
if (!at91sam9n12_pmc)
|
||||
return;
|
||||
|
||||
hw = at91_clk_register_main_rc_osc(regmap, "main_rc_osc", 12000000,
|
||||
50000000);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
bypass = of_property_read_bool(np, "atmel,osc-bypass");
|
||||
|
||||
hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name,
|
||||
bypass);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
parent_names[0] = "main_rc_osc";
|
||||
parent_names[1] = "main_osc";
|
||||
hw = at91_clk_register_sam9x5_main(regmap, "mainck", parent_names, 2);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
at91sam9n12_pmc->chws[PMC_MAIN] = hw;
|
||||
|
||||
hw = at91_clk_register_pll(regmap, "pllack", "mainck", 0,
|
||||
&at91rm9200_pll_layout, &plla_characteristics);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
hw = at91_clk_register_plldiv(regmap, "plladivck", "pllack");
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
hw = at91_clk_register_pll(regmap, "pllbck", "mainck", 1,
|
||||
&at91rm9200_pll_layout, &pllb_characteristics);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
parent_names[0] = slck_name;
|
||||
parent_names[1] = "mainck";
|
||||
parent_names[2] = "plladivck";
|
||||
parent_names[3] = "pllbck";
|
||||
hw = at91_clk_register_master(regmap, "masterck", 4, parent_names,
|
||||
&at91sam9x5_master_layout,
|
||||
&mck_characteristics);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
at91sam9n12_pmc->chws[PMC_MCK] = hw;
|
||||
|
||||
hw = at91sam9n12_clk_register_usb(regmap, "usbck", "pllbck");
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
parent_names[0] = slck_name;
|
||||
parent_names[1] = "mainck";
|
||||
parent_names[2] = "plladivck";
|
||||
parent_names[3] = "pllbck";
|
||||
parent_names[4] = "masterck";
|
||||
for (i = 0; i < 2; i++) {
|
||||
char name[6];
|
||||
|
||||
snprintf(name, sizeof(name), "prog%d", i);
|
||||
|
||||
hw = at91_clk_register_programmable(regmap, name,
|
||||
parent_names, 5, i,
|
||||
&at91sam9x5_programmable_layout);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(at91sam9n12_systemck); i++) {
|
||||
hw = at91_clk_register_system(regmap, at91sam9n12_systemck[i].n,
|
||||
at91sam9n12_systemck[i].p,
|
||||
at91sam9n12_systemck[i].id);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
at91sam9n12_pmc->shws[at91sam9n12_systemck[i].id] = hw;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(at91sam9n12_periphck); i++) {
|
||||
hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
|
||||
&at91sam9n12_pcr_layout,
|
||||
at91sam9n12_periphck[i].n,
|
||||
"masterck",
|
||||
at91sam9n12_periphck[i].id,
|
||||
&range);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
at91sam9n12_pmc->phws[at91sam9n12_periphck[i].id] = hw;
|
||||
}
|
||||
|
||||
of_clk_add_hw_provider(np, of_clk_hw_pmc_get, at91sam9n12_pmc);
|
||||
|
||||
return;
|
||||
|
||||
err_free:
|
||||
pmc_data_free(at91sam9n12_pmc);
|
||||
}
|
||||
/*
|
||||
* The TCB is used as the clocksource so its clock is needed early. This means
|
||||
* this can't be a platform driver.
|
||||
*/
|
||||
CLK_OF_DECLARE_DRIVER(at91sam9n12_pmc, "atmel,at91sam9n12-pmc",
|
||||
at91sam9n12_pmc_setup);
|
|
@ -25,6 +25,7 @@ struct at91sam9x5_clk_usb {
|
|||
struct clk_hw hw;
|
||||
struct regmap *regmap;
|
||||
u32 usbs_mask;
|
||||
u8 num_parents;
|
||||
};
|
||||
|
||||
#define to_at91sam9x5_clk_usb(hw) \
|
||||
|
@ -75,6 +76,9 @@ static int at91sam9x5_clk_usb_determine_rate(struct clk_hw *hw,
|
|||
tmp_parent_rate = req->rate * div;
|
||||
tmp_parent_rate = clk_hw_round_rate(parent,
|
||||
tmp_parent_rate);
|
||||
if (!tmp_parent_rate)
|
||||
continue;
|
||||
|
||||
tmp_rate = DIV_ROUND_CLOSEST(tmp_parent_rate, div);
|
||||
if (tmp_rate < req->rate)
|
||||
tmp_diff = req->rate - tmp_rate;
|
||||
|
@ -107,7 +111,7 @@ static int at91sam9x5_clk_usb_set_parent(struct clk_hw *hw, u8 index)
|
|||
{
|
||||
struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
|
||||
|
||||
if (index > 1)
|
||||
if (index >= usb->num_parents)
|
||||
return -EINVAL;
|
||||
|
||||
regmap_update_bits(usb->regmap, AT91_PMC_USB, usb->usbs_mask, index);
|
||||
|
@ -211,7 +215,8 @@ _at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name,
|
|||
|
||||
usb->hw.init = &init;
|
||||
usb->regmap = regmap;
|
||||
usb->usbs_mask = SAM9X5_USBS_MASK;
|
||||
usb->usbs_mask = usbs_mask;
|
||||
usb->num_parents = num_parents;
|
||||
|
||||
hw = &usb->hw;
|
||||
ret = clk_hw_register(NULL, &usb->hw);
|
||||
|
|
|
@ -124,7 +124,6 @@ static const struct {
|
|||
char *n;
|
||||
u8 id;
|
||||
struct clk_range r;
|
||||
bool pll;
|
||||
} sam9x60_gck[] = {
|
||||
{ .n = "flex0_gclk", .id = 5, },
|
||||
{ .n = "flex1_gclk", .id = 6, },
|
||||
|
@ -144,11 +143,9 @@ static const struct {
|
|||
{ .n = "sdmmc1_gclk", .id = 26, .r = { .min = 0, .max = 105000000 }, },
|
||||
{ .n = "flex11_gclk", .id = 32, },
|
||||
{ .n = "flex12_gclk", .id = 33, },
|
||||
{ .n = "i2s_gclk", .id = 34, .r = { .min = 0, .max = 105000000 },
|
||||
.pll = true, },
|
||||
{ .n = "i2s_gclk", .id = 34, .r = { .min = 0, .max = 105000000 }, },
|
||||
{ .n = "pit64b_gclk", .id = 37, },
|
||||
{ .n = "classd_gclk", .id = 42, .r = { .min = 0, .max = 100000000 },
|
||||
.pll = true, },
|
||||
{ .n = "classd_gclk", .id = 42, .r = { .min = 0, .max = 100000000 }, },
|
||||
{ .n = "tcb1_gclk", .id = 45, },
|
||||
{ .n = "dbgu_gclk", .id = 47, },
|
||||
};
|
||||
|
@ -237,9 +234,8 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
|
|||
|
||||
parent_names[0] = "pllack";
|
||||
parent_names[1] = "upllck";
|
||||
parent_names[2] = "mainck";
|
||||
parent_names[3] = "mainck";
|
||||
hw = sam9x60_clk_register_usb(regmap, "usbck", parent_names, 4);
|
||||
parent_names[2] = "main_osc";
|
||||
hw = sam9x60_clk_register_usb(regmap, "usbck", parent_names, 3);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
|
@ -290,7 +286,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
|
|||
sam9x60_gck[i].n,
|
||||
parent_names, 6,
|
||||
sam9x60_gck[i].id,
|
||||
sam9x60_gck[i].pll,
|
||||
false,
|
||||
&sam9x60_gck[i].r);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
|
240
drivers/clk/at91/sama5d3.c
Normal file
240
drivers/clk/at91/sama5d3.c
Normal file
|
@ -0,0 +1,240 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <dt-bindings/clock/at91.h>
|
||||
|
||||
#include "pmc.h"
|
||||
|
||||
static const struct clk_master_characteristics mck_characteristics = {
|
||||
.output = { .min = 0, .max = 166000000 },
|
||||
.divisors = { 1, 2, 4, 3 },
|
||||
};
|
||||
|
||||
static u8 plla_out[] = { 0 };
|
||||
|
||||
static u16 plla_icpll[] = { 0 };
|
||||
|
||||
static const struct clk_range plla_outputs[] = {
|
||||
{ .min = 400000000, .max = 1000000000 },
|
||||
};
|
||||
|
||||
static const struct clk_pll_characteristics plla_characteristics = {
|
||||
.input = { .min = 8000000, .max = 50000000 },
|
||||
.num_output = ARRAY_SIZE(plla_outputs),
|
||||
.output = plla_outputs,
|
||||
.icpll = plla_icpll,
|
||||
.out = plla_out,
|
||||
};
|
||||
|
||||
static const struct clk_pcr_layout sama5d3_pcr_layout = {
|
||||
.offset = 0x10c,
|
||||
.cmd = BIT(12),
|
||||
.pid_mask = GENMASK(6, 0),
|
||||
.div_mask = GENMASK(17, 16),
|
||||
};
|
||||
|
||||
static const struct {
|
||||
char *n;
|
||||
char *p;
|
||||
u8 id;
|
||||
} sama5d3_systemck[] = {
|
||||
{ .n = "ddrck", .p = "masterck", .id = 2 },
|
||||
{ .n = "lcdck", .p = "masterck", .id = 3 },
|
||||
{ .n = "smdck", .p = "smdclk", .id = 4 },
|
||||
{ .n = "uhpck", .p = "usbck", .id = 6 },
|
||||
{ .n = "udpck", .p = "usbck", .id = 7 },
|
||||
{ .n = "pck0", .p = "prog0", .id = 8 },
|
||||
{ .n = "pck1", .p = "prog1", .id = 9 },
|
||||
{ .n = "pck2", .p = "prog2", .id = 10 },
|
||||
};
|
||||
|
||||
static const struct {
|
||||
char *n;
|
||||
u8 id;
|
||||
struct clk_range r;
|
||||
} sama5d3_periphck[] = {
|
||||
{ .n = "dbgu_clk", .id = 2, },
|
||||
{ .n = "hsmc_clk", .id = 5, },
|
||||
{ .n = "pioA_clk", .id = 6, },
|
||||
{ .n = "pioB_clk", .id = 7, },
|
||||
{ .n = "pioC_clk", .id = 8, },
|
||||
{ .n = "pioD_clk", .id = 9, },
|
||||
{ .n = "pioE_clk", .id = 10, },
|
||||
{ .n = "usart0_clk", .id = 12, .r = { .min = 0, .max = 83000000 }, },
|
||||
{ .n = "usart1_clk", .id = 13, .r = { .min = 0, .max = 83000000 }, },
|
||||
{ .n = "usart2_clk", .id = 14, .r = { .min = 0, .max = 83000000 }, },
|
||||
{ .n = "usart3_clk", .id = 15, .r = { .min = 0, .max = 83000000 }, },
|
||||
{ .n = "uart0_clk", .id = 16, .r = { .min = 0, .max = 83000000 }, },
|
||||
{ .n = "uart1_clk", .id = 17, .r = { .min = 0, .max = 83000000 }, },
|
||||
{ .n = "twi0_clk", .id = 18, .r = { .min = 0, .max = 41500000 }, },
|
||||
{ .n = "twi1_clk", .id = 19, .r = { .min = 0, .max = 41500000 }, },
|
||||
{ .n = "twi2_clk", .id = 20, .r = { .min = 0, .max = 41500000 }, },
|
||||
{ .n = "mci0_clk", .id = 21, },
|
||||
{ .n = "mci1_clk", .id = 22, },
|
||||
{ .n = "mci2_clk", .id = 23, },
|
||||
{ .n = "spi0_clk", .id = 24, .r = { .min = 0, .max = 166000000 }, },
|
||||
{ .n = "spi1_clk", .id = 25, .r = { .min = 0, .max = 166000000 }, },
|
||||
{ .n = "tcb0_clk", .id = 26, .r = { .min = 0, .max = 166000000 }, },
|
||||
{ .n = "tcb1_clk", .id = 27, .r = { .min = 0, .max = 166000000 }, },
|
||||
{ .n = "pwm_clk", .id = 28, },
|
||||
{ .n = "adc_clk", .id = 29, .r = { .min = 0, .max = 83000000 }, },
|
||||
{ .n = "dma0_clk", .id = 30, },
|
||||
{ .n = "dma1_clk", .id = 31, },
|
||||
{ .n = "uhphs_clk", .id = 32, },
|
||||
{ .n = "udphs_clk", .id = 33, },
|
||||
{ .n = "macb0_clk", .id = 34, },
|
||||
{ .n = "macb1_clk", .id = 35, },
|
||||
{ .n = "lcdc_clk", .id = 36, },
|
||||
{ .n = "isi_clk", .id = 37, },
|
||||
{ .n = "ssc0_clk", .id = 38, .r = { .min = 0, .max = 83000000 }, },
|
||||
{ .n = "ssc1_clk", .id = 39, .r = { .min = 0, .max = 83000000 }, },
|
||||
{ .n = "can0_clk", .id = 40, .r = { .min = 0, .max = 83000000 }, },
|
||||
{ .n = "can1_clk", .id = 41, .r = { .min = 0, .max = 83000000 }, },
|
||||
{ .n = "sha_clk", .id = 42, },
|
||||
{ .n = "aes_clk", .id = 43, },
|
||||
{ .n = "tdes_clk", .id = 44, },
|
||||
{ .n = "trng_clk", .id = 45, },
|
||||
{ .n = "fuse_clk", .id = 48, },
|
||||
{ .n = "mpddr_clk", .id = 49, },
|
||||
};
|
||||
|
||||
static void __init sama5d3_pmc_setup(struct device_node *np)
|
||||
{
|
||||
const char *slck_name, *mainxtal_name;
|
||||
struct pmc_data *sama5d3_pmc;
|
||||
const char *parent_names[5];
|
||||
struct regmap *regmap;
|
||||
struct clk_hw *hw;
|
||||
int i;
|
||||
bool bypass;
|
||||
|
||||
i = of_property_match_string(np, "clock-names", "slow_clk");
|
||||
if (i < 0)
|
||||
return;
|
||||
|
||||
slck_name = of_clk_get_parent_name(np, i);
|
||||
|
||||
i = of_property_match_string(np, "clock-names", "main_xtal");
|
||||
if (i < 0)
|
||||
return;
|
||||
mainxtal_name = of_clk_get_parent_name(np, i);
|
||||
|
||||
regmap = syscon_node_to_regmap(np);
|
||||
if (IS_ERR(regmap))
|
||||
return;
|
||||
|
||||
sama5d3_pmc = pmc_data_allocate(PMC_MAIN + 1,
|
||||
nck(sama5d3_systemck),
|
||||
nck(sama5d3_periphck), 0);
|
||||
if (!sama5d3_pmc)
|
||||
return;
|
||||
|
||||
hw = at91_clk_register_main_rc_osc(regmap, "main_rc_osc", 12000000,
|
||||
50000000);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
bypass = of_property_read_bool(np, "atmel,osc-bypass");
|
||||
|
||||
hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name,
|
||||
bypass);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
parent_names[0] = "main_rc_osc";
|
||||
parent_names[1] = "main_osc";
|
||||
hw = at91_clk_register_sam9x5_main(regmap, "mainck", parent_names, 2);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
hw = at91_clk_register_pll(regmap, "pllack", "mainck", 0,
|
||||
&sama5d3_pll_layout, &plla_characteristics);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
hw = at91_clk_register_plldiv(regmap, "plladivck", "pllack");
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
hw = at91_clk_register_utmi(regmap, NULL, "utmick", "mainck");
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
sama5d3_pmc->chws[PMC_UTMI] = hw;
|
||||
|
||||
parent_names[0] = slck_name;
|
||||
parent_names[1] = "mainck";
|
||||
parent_names[2] = "plladivck";
|
||||
parent_names[3] = "utmick";
|
||||
hw = at91_clk_register_master(regmap, "masterck", 4, parent_names,
|
||||
&at91sam9x5_master_layout,
|
||||
&mck_characteristics);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
sama5d3_pmc->chws[PMC_MCK] = hw;
|
||||
|
||||
parent_names[0] = "plladivck";
|
||||
parent_names[1] = "utmick";
|
||||
hw = at91sam9x5_clk_register_usb(regmap, "usbck", parent_names, 2);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
hw = at91sam9x5_clk_register_smd(regmap, "smdclk", parent_names, 2);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
parent_names[0] = slck_name;
|
||||
parent_names[1] = "mainck";
|
||||
parent_names[2] = "plladivck";
|
||||
parent_names[3] = "utmick";
|
||||
parent_names[4] = "masterck";
|
||||
for (i = 0; i < 3; i++) {
|
||||
char name[6];
|
||||
|
||||
snprintf(name, sizeof(name), "prog%d", i);
|
||||
|
||||
hw = at91_clk_register_programmable(regmap, name,
|
||||
parent_names, 5, i,
|
||||
&at91sam9x5_programmable_layout);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(sama5d3_systemck); i++) {
|
||||
hw = at91_clk_register_system(regmap, sama5d3_systemck[i].n,
|
||||
sama5d3_systemck[i].p,
|
||||
sama5d3_systemck[i].id);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
sama5d3_pmc->shws[sama5d3_systemck[i].id] = hw;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(sama5d3_periphck); i++) {
|
||||
hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
|
||||
&sama5d3_pcr_layout,
|
||||
sama5d3_periphck[i].n,
|
||||
"masterck",
|
||||
sama5d3_periphck[i].id,
|
||||
&sama5d3_periphck[i].r);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
sama5d3_pmc->phws[sama5d3_periphck[i].id] = hw;
|
||||
}
|
||||
|
||||
of_clk_add_hw_provider(np, of_clk_hw_pmc_get, sama5d3_pmc);
|
||||
|
||||
return;
|
||||
|
||||
err_free:
|
||||
pmc_data_free(sama5d3_pmc);
|
||||
}
|
||||
/*
|
||||
* The TCB is used as the clocksource so its clock is needed early. This means
|
||||
* this can't be a platform driver.
|
||||
*/
|
||||
CLK_OF_DECLARE_DRIVER(sama5d3_pmc, "atmel,sama5d3-pmc", sama5d3_pmc_setup);
|
|
@ -774,7 +774,7 @@ static void clk_core_rate_restore_protect(struct clk_core *core, int count)
|
|||
* clk_rate_exclusive_get - get exclusivity over the clk rate control
|
||||
* @clk: the clk over which the exclusity of rate control is requested
|
||||
*
|
||||
* clk_rate_exlusive_get() begins a critical section during which a clock
|
||||
* clk_rate_exclusive_get() begins a critical section during which a clock
|
||||
* consumer cannot tolerate any other consumer making any operation on the
|
||||
* clock which could result in a rate change or rate glitch. Exclusive clocks
|
||||
* cannot have their rate changed, either directly or indirectly due to changes
|
||||
|
|
|
@ -432,8 +432,10 @@ static void __init jz4770_cgu_init(struct device_node *np)
|
|||
|
||||
cgu = ingenic_cgu_new(jz4770_cgu_clocks,
|
||||
ARRAY_SIZE(jz4770_cgu_clocks), np);
|
||||
if (!cgu)
|
||||
if (!cgu) {
|
||||
pr_err("%s: failed to initialise CGU\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
retval = ingenic_cgu_register_clocks(cgu);
|
||||
if (retval)
|
||||
|
|
|
@ -9,14 +9,16 @@
|
|||
#include <linux/clk-provider.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <dt-bindings/clock/jz4780-cgu.h>
|
||||
#include "cgu.h"
|
||||
#include "pm.h"
|
||||
|
||||
/* CGU register offsets */
|
||||
#define CGU_REG_CLOCKCONTROL 0x00
|
||||
#define CGU_REG_PLLCONTROL 0x0c
|
||||
#define CGU_REG_LCR 0x04
|
||||
#define CGU_REG_APLL 0x10
|
||||
#define CGU_REG_MPLL 0x14
|
||||
#define CGU_REG_EPLL 0x18
|
||||
|
@ -46,8 +48,8 @@
|
|||
#define CGU_REG_CLOCKSTATUS 0xd4
|
||||
|
||||
/* bits within the OPCR register */
|
||||
#define OPCR_SPENDN0 (1 << 7)
|
||||
#define OPCR_SPENDN1 (1 << 6)
|
||||
#define OPCR_SPENDN0 BIT(7)
|
||||
#define OPCR_SPENDN1 BIT(6)
|
||||
|
||||
/* bits within the USBPCR register */
|
||||
#define USBPCR_USB_MODE BIT(31)
|
||||
|
@ -88,6 +90,13 @@
|
|||
#define USBVBFIL_IDDIGFIL_MASK (0xffff << USBVBFIL_IDDIGFIL_SHIFT)
|
||||
#define USBVBFIL_USBVBFIL_MASK (0xffff)
|
||||
|
||||
/* bits within the LCR register */
|
||||
#define LCR_PD_SCPU BIT(31)
|
||||
#define LCR_SCPUS BIT(27)
|
||||
|
||||
/* bits within the CLKGR1 register */
|
||||
#define CLKGR1_CORE1 BIT(15)
|
||||
|
||||
static struct ingenic_cgu *cgu;
|
||||
|
||||
static u8 jz4780_otg_phy_get_parent(struct clk_hw *hw)
|
||||
|
@ -205,6 +214,42 @@ static const struct clk_ops jz4780_otg_phy_ops = {
|
|||
.set_rate = jz4780_otg_phy_set_rate,
|
||||
};
|
||||
|
||||
static int jz4780_core1_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw);
|
||||
struct ingenic_cgu *cgu = ingenic_clk->cgu;
|
||||
const unsigned int timeout = 5000;
|
||||
unsigned long flags;
|
||||
int retval;
|
||||
u32 lcr, clkgr1;
|
||||
|
||||
spin_lock_irqsave(&cgu->lock, flags);
|
||||
|
||||
lcr = readl(cgu->base + CGU_REG_LCR);
|
||||
lcr &= ~LCR_PD_SCPU;
|
||||
writel(lcr, cgu->base + CGU_REG_LCR);
|
||||
|
||||
clkgr1 = readl(cgu->base + CGU_REG_CLKGR1);
|
||||
clkgr1 &= ~CLKGR1_CORE1;
|
||||
writel(clkgr1, cgu->base + CGU_REG_CLKGR1);
|
||||
|
||||
spin_unlock_irqrestore(&cgu->lock, flags);
|
||||
|
||||
/* wait for the CPU to be powered up */
|
||||
retval = readl_poll_timeout(cgu->base + CGU_REG_LCR, lcr,
|
||||
!(lcr & LCR_SCPUS), 10, timeout);
|
||||
if (retval == -ETIMEDOUT) {
|
||||
pr_err("%s: Wait for power up core1 timeout\n", __func__);
|
||||
return retval;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct clk_ops jz4780_core1_ops = {
|
||||
.enable = jz4780_core1_enable,
|
||||
};
|
||||
|
||||
static const s8 pll_od_encoding[16] = {
|
||||
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
|
||||
0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
|
||||
|
@ -699,9 +744,9 @@ static const struct ingenic_cgu_clk_info jz4780_cgu_clocks[] = {
|
|||
},
|
||||
|
||||
[JZ4780_CLK_CORE1] = {
|
||||
"core1", CGU_CLK_GATE,
|
||||
"core1", CGU_CLK_CUSTOM,
|
||||
.parents = { JZ4780_CLK_CPU, -1, -1, -1 },
|
||||
.gate = { CGU_REG_CLKGR1, 15 },
|
||||
.custom = { &jz4780_core1_ops },
|
||||
},
|
||||
|
||||
};
|
||||
|
|
|
@ -189,7 +189,7 @@ static long ingenic_tcu_round_rate(struct clk_hw *hw, unsigned long req_rate,
|
|||
u8 prescale;
|
||||
|
||||
if (req_rate > rate)
|
||||
return -EINVAL;
|
||||
return rate;
|
||||
|
||||
prescale = ingenic_tcu_get_prescale(rate, req_rate);
|
||||
|
||||
|
@ -317,10 +317,17 @@ static const struct ingenic_soc_info jz4770_soc_info = {
|
|||
.has_tcu_clk = false,
|
||||
};
|
||||
|
||||
static const struct ingenic_soc_info x1000_soc_info = {
|
||||
.num_channels = 8,
|
||||
.has_ost = false, /* X1000 has OST, but it not belong TCU */
|
||||
.has_tcu_clk = false,
|
||||
};
|
||||
|
||||
static const struct of_device_id ingenic_tcu_of_match[] __initconst = {
|
||||
{ .compatible = "ingenic,jz4740-tcu", .data = &jz4740_soc_info, },
|
||||
{ .compatible = "ingenic,jz4725b-tcu", .data = &jz4725b_soc_info, },
|
||||
{ .compatible = "ingenic,jz4770-tcu", .data = &jz4770_soc_info, },
|
||||
{ .compatible = "ingenic,x1000-tcu", .data = &x1000_soc_info, },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
|
@ -471,3 +478,4 @@ static void __init ingenic_tcu_init(struct device_node *np)
|
|||
CLK_OF_DECLARE_DRIVER(jz4740_cgu, "ingenic,jz4740-tcu", ingenic_tcu_init);
|
||||
CLK_OF_DECLARE_DRIVER(jz4725b_cgu, "ingenic,jz4725b-tcu", ingenic_tcu_init);
|
||||
CLK_OF_DECLARE_DRIVER(jz4770_cgu, "ingenic,jz4770-tcu", ingenic_tcu_init);
|
||||
CLK_OF_DECLARE_DRIVER(x1000_cgu, "ingenic,x1000-tcu", ingenic_tcu_init);
|
||||
|
|
|
@ -26,3 +26,11 @@ config TI_SCI_CLK_PROBE_FROM_FW
|
|||
This is mostly only useful for debugging purposes, and will
|
||||
increase the boot time of the device. If you want the clocks probed
|
||||
from firmware, say Y. Otherwise, say N.
|
||||
|
||||
config TI_SYSCON_CLK
|
||||
tristate "Syscon based clock driver for K2/K3 SoCs"
|
||||
depends on ARCH_KEYSTONE || ARCH_K3 || COMPILE_TEST
|
||||
default ARCH_KEYSTONE || ARCH_K3
|
||||
help
|
||||
This adds clock driver support for syscon based gate
|
||||
clocks on TI's K2 and K3 SoCs.
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
obj-$(CONFIG_COMMON_CLK_KEYSTONE) += pll.o gate.o
|
||||
obj-$(CONFIG_TI_SCI_CLK) += sci-clk.o
|
||||
obj-$(CONFIG_TI_SYSCON_CLK) += syscon-clk.o
|
||||
|
|
172
drivers/clk/keystone/syscon-clk.c
Normal file
172
drivers/clk/keystone/syscon-clk.c
Normal file
|
@ -0,0 +1,172 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
struct ti_syscon_gate_clk_priv {
|
||||
struct clk_hw hw;
|
||||
struct regmap *regmap;
|
||||
u32 reg;
|
||||
u32 idx;
|
||||
};
|
||||
|
||||
struct ti_syscon_gate_clk_data {
|
||||
char *name;
|
||||
u32 offset;
|
||||
u32 bit_idx;
|
||||
};
|
||||
|
||||
static struct
|
||||
ti_syscon_gate_clk_priv *to_ti_syscon_gate_clk_priv(struct clk_hw *hw)
|
||||
{
|
||||
return container_of(hw, struct ti_syscon_gate_clk_priv, hw);
|
||||
}
|
||||
|
||||
static int ti_syscon_gate_clk_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct ti_syscon_gate_clk_priv *priv = to_ti_syscon_gate_clk_priv(hw);
|
||||
|
||||
return regmap_write_bits(priv->regmap, priv->reg, priv->idx,
|
||||
priv->idx);
|
||||
}
|
||||
|
||||
static void ti_syscon_gate_clk_disable(struct clk_hw *hw)
|
||||
{
|
||||
struct ti_syscon_gate_clk_priv *priv = to_ti_syscon_gate_clk_priv(hw);
|
||||
|
||||
regmap_write_bits(priv->regmap, priv->reg, priv->idx, 0);
|
||||
}
|
||||
|
||||
static int ti_syscon_gate_clk_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
unsigned int val;
|
||||
struct ti_syscon_gate_clk_priv *priv = to_ti_syscon_gate_clk_priv(hw);
|
||||
|
||||
regmap_read(priv->regmap, priv->reg, &val);
|
||||
|
||||
return !!(val & priv->idx);
|
||||
}
|
||||
|
||||
static const struct clk_ops ti_syscon_gate_clk_ops = {
|
||||
.enable = ti_syscon_gate_clk_enable,
|
||||
.disable = ti_syscon_gate_clk_disable,
|
||||
.is_enabled = ti_syscon_gate_clk_is_enabled,
|
||||
};
|
||||
|
||||
static struct clk_hw
|
||||
*ti_syscon_gate_clk_register(struct device *dev, struct regmap *regmap,
|
||||
const struct ti_syscon_gate_clk_data *data)
|
||||
{
|
||||
struct ti_syscon_gate_clk_priv *priv;
|
||||
struct clk_init_data init;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init.name = data->name;
|
||||
init.ops = &ti_syscon_gate_clk_ops;
|
||||
init.parent_names = NULL;
|
||||
init.num_parents = 0;
|
||||
init.flags = 0;
|
||||
|
||||
priv->regmap = regmap;
|
||||
priv->reg = data->offset;
|
||||
priv->idx = BIT(data->bit_idx);
|
||||
priv->hw.init = &init;
|
||||
|
||||
ret = devm_clk_hw_register(dev, &priv->hw);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
return &priv->hw;
|
||||
}
|
||||
|
||||
static int ti_syscon_gate_clk_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct ti_syscon_gate_clk_data *data, *p;
|
||||
struct clk_hw_onecell_data *hw_data;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct regmap *regmap;
|
||||
int num_clks, i;
|
||||
|
||||
data = device_get_match_data(dev);
|
||||
if (!data)
|
||||
return -EINVAL;
|
||||
|
||||
regmap = syscon_node_to_regmap(dev->of_node);
|
||||
if (IS_ERR(regmap)) {
|
||||
if (PTR_ERR(regmap) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
dev_err(dev, "failed to find parent regmap\n");
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
num_clks = 0;
|
||||
for (p = data; p->name; p++)
|
||||
num_clks++;
|
||||
|
||||
hw_data = devm_kzalloc(dev, struct_size(hw_data, hws, num_clks),
|
||||
GFP_KERNEL);
|
||||
if (!hw_data)
|
||||
return -ENOMEM;
|
||||
|
||||
hw_data->num = num_clks;
|
||||
|
||||
for (i = 0; i < num_clks; i++) {
|
||||
hw_data->hws[i] = ti_syscon_gate_clk_register(dev, regmap,
|
||||
&data[i]);
|
||||
if (IS_ERR(hw_data->hws[i]))
|
||||
dev_warn(dev, "failed to register %s\n",
|
||||
data[i].name);
|
||||
}
|
||||
|
||||
return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
|
||||
hw_data);
|
||||
}
|
||||
|
||||
#define TI_SYSCON_CLK_GATE(_name, _offset, _bit_idx) \
|
||||
{ \
|
||||
.name = _name, \
|
||||
.offset = (_offset), \
|
||||
.bit_idx = (_bit_idx), \
|
||||
}
|
||||
|
||||
static const struct ti_syscon_gate_clk_data am654_clk_data[] = {
|
||||
TI_SYSCON_CLK_GATE("ehrpwm_tbclk0", 0x0, 0),
|
||||
TI_SYSCON_CLK_GATE("ehrpwm_tbclk1", 0x4, 0),
|
||||
TI_SYSCON_CLK_GATE("ehrpwm_tbclk2", 0x8, 0),
|
||||
TI_SYSCON_CLK_GATE("ehrpwm_tbclk3", 0xc, 0),
|
||||
TI_SYSCON_CLK_GATE("ehrpwm_tbclk4", 0x10, 0),
|
||||
TI_SYSCON_CLK_GATE("ehrpwm_tbclk5", 0x14, 0),
|
||||
{ /* Sentinel */ },
|
||||
};
|
||||
|
||||
static const struct of_device_id ti_syscon_gate_clk_ids[] = {
|
||||
{
|
||||
.compatible = "ti,am654-ehrpwm-tbclk",
|
||||
.data = &am654_clk_data,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ti_syscon_gate_clk_ids);
|
||||
|
||||
static struct platform_driver ti_syscon_gate_clk_driver = {
|
||||
.probe = ti_syscon_gate_clk_probe,
|
||||
.driver = {
|
||||
.name = "ti-syscon-gate-clk",
|
||||
.of_match_table = ti_syscon_gate_clk_ids,
|
||||
},
|
||||
};
|
||||
module_platform_driver(ti_syscon_gate_clk_driver);
|
||||
|
||||
MODULE_AUTHOR("Vignesh Raghavendra <vigneshr@ti.com>");
|
||||
MODULE_DESCRIPTION("Syscon backed gate-clock driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -8,7 +8,7 @@ obj-y += clk-apbc.o clk-apmu.o clk-frac.o clk-mix.o clk-gate.o clk.o
|
|||
obj-$(CONFIG_RESET_CONTROLLER) += reset.o
|
||||
|
||||
obj-$(CONFIG_MACH_MMP_DT) += clk-of-pxa168.o clk-of-pxa910.o
|
||||
obj-$(CONFIG_COMMON_CLK_MMP2) += clk-of-mmp2.o
|
||||
obj-$(CONFIG_COMMON_CLK_MMP2) += clk-of-mmp2.o clk-pll.o
|
||||
|
||||
obj-$(CONFIG_CPU_PXA168) += clk-pxa168.o
|
||||
obj-$(CONFIG_CPU_PXA910) += clk-pxa910.o
|
||||
|
|
|
@ -441,7 +441,7 @@ const struct clk_ops mmp_clk_mix_ops = {
|
|||
|
||||
struct clk *mmp_clk_register_mix(struct device *dev,
|
||||
const char *name,
|
||||
const char **parent_names,
|
||||
const char * const *parent_names,
|
||||
u8 num_parents,
|
||||
unsigned long flags,
|
||||
struct mmp_clk_mix_config *config,
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
*
|
||||
* Copyright (C) 2012 Marvell
|
||||
* Chao Xie <xiechao.mail@gmail.com>
|
||||
* Copyright (C) 2020 Lubomir Rintel <lkundrak@v3.sk>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
|
@ -48,17 +49,39 @@
|
|||
#define APMU_SDH1 0x58
|
||||
#define APMU_SDH2 0xe8
|
||||
#define APMU_SDH3 0xec
|
||||
#define APMU_SDH4 0x15c
|
||||
#define APMU_USB 0x5c
|
||||
#define APMU_DISP0 0x4c
|
||||
#define APMU_DISP1 0x110
|
||||
#define APMU_CCIC0 0x50
|
||||
#define APMU_CCIC1 0xf4
|
||||
#define APBC_THERMAL0 0x90
|
||||
#define APBC_THERMAL1 0x98
|
||||
#define APBC_THERMAL2 0x9c
|
||||
#define APBC_THERMAL3 0xa0
|
||||
#define APMU_USBHSIC0 0xf8
|
||||
#define APMU_USBHSIC1 0xfc
|
||||
#define MPMU_UART_PLL 0x14
|
||||
#define APMU_GPU 0xcc
|
||||
|
||||
#define MPMU_FCCR 0x8
|
||||
#define MPMU_POSR 0x10
|
||||
#define MPMU_UART_PLL 0x14
|
||||
#define MPMU_PLL2_CR 0x34
|
||||
/* MMP3 specific below */
|
||||
#define MPMU_PLL3_CR 0x50
|
||||
#define MPMU_PLL3_CTRL1 0x58
|
||||
#define MPMU_PLL1_CTRL 0x5c
|
||||
#define MPMU_PLL_DIFF_CTRL 0x68
|
||||
#define MPMU_PLL2_CTRL1 0x414
|
||||
|
||||
enum mmp2_clk_model {
|
||||
CLK_MODEL_MMP2,
|
||||
CLK_MODEL_MMP3,
|
||||
};
|
||||
|
||||
struct mmp2_clk_unit {
|
||||
struct mmp_clk_unit unit;
|
||||
enum mmp2_clk_model model;
|
||||
void __iomem *mpmu_base;
|
||||
void __iomem *apmu_base;
|
||||
void __iomem *apbc_base;
|
||||
|
@ -67,11 +90,22 @@ struct mmp2_clk_unit {
|
|||
static struct mmp_param_fixed_rate_clk fixed_rate_clks[] = {
|
||||
{MMP2_CLK_CLK32, "clk32", NULL, 0, 32768},
|
||||
{MMP2_CLK_VCTCXO, "vctcxo", NULL, 0, 26000000},
|
||||
{MMP2_CLK_PLL1, "pll1", NULL, 0, 800000000},
|
||||
{MMP2_CLK_PLL2, "pll2", NULL, 0, 960000000},
|
||||
{MMP2_CLK_USB_PLL, "usb_pll", NULL, 0, 480000000},
|
||||
};
|
||||
|
||||
static struct mmp_param_pll_clk pll_clks[] = {
|
||||
{MMP2_CLK_PLL1, "pll1", 797330000, MPMU_FCCR, 0x4000, MPMU_POSR, 0},
|
||||
{MMP2_CLK_PLL2, "pll2", 0, MPMU_PLL2_CR, 0x0300, MPMU_PLL2_CR, 10},
|
||||
};
|
||||
|
||||
static struct mmp_param_pll_clk mmp3_pll_clks[] = {
|
||||
{MMP2_CLK_PLL2, "pll1", 797330000, MPMU_FCCR, 0x4000, MPMU_POSR, 0, 26000000, MPMU_PLL1_CTRL, 25},
|
||||
{MMP2_CLK_PLL2, "pll2", 0, MPMU_PLL2_CR, 0x0300, MPMU_PLL2_CR, 10, 26000000, MPMU_PLL2_CTRL1, 25},
|
||||
{MMP3_CLK_PLL1_P, "pll1_p", 0, MPMU_PLL_DIFF_CTRL, 0x0010, 0, 0, 797330000, MPMU_PLL_DIFF_CTRL, 0},
|
||||
{MMP3_CLK_PLL2_P, "pll2_p", 0, MPMU_PLL_DIFF_CTRL, 0x0100, MPMU_PLL2_CR, 10, 26000000, MPMU_PLL_DIFF_CTRL, 5},
|
||||
{MMP3_CLK_PLL3, "pll3", 0, MPMU_PLL3_CR, 0x0300, MPMU_PLL3_CR, 10, 26000000, MPMU_PLL3_CTRL1, 25},
|
||||
};
|
||||
|
||||
static struct mmp_param_fixed_factor_clk fixed_factor_clks[] = {
|
||||
{MMP2_CLK_PLL1_2, "pll1_2", "pll1", 1, 2, 0},
|
||||
{MMP2_CLK_PLL1_4, "pll1_4", "pll1_2", 1, 2, 0},
|
||||
|
@ -113,6 +147,16 @@ static void mmp2_pll_init(struct mmp2_clk_unit *pxa_unit)
|
|||
mmp_register_fixed_rate_clks(unit, fixed_rate_clks,
|
||||
ARRAY_SIZE(fixed_rate_clks));
|
||||
|
||||
if (pxa_unit->model == CLK_MODEL_MMP3) {
|
||||
mmp_register_pll_clks(unit, mmp3_pll_clks,
|
||||
pxa_unit->mpmu_base,
|
||||
ARRAY_SIZE(mmp3_pll_clks));
|
||||
} else {
|
||||
mmp_register_pll_clks(unit, pll_clks,
|
||||
pxa_unit->mpmu_base,
|
||||
ARRAY_SIZE(pll_clks));
|
||||
}
|
||||
|
||||
mmp_register_fixed_factor_clks(unit, fixed_factor_clks,
|
||||
ARRAY_SIZE(fixed_factor_clks));
|
||||
|
||||
|
@ -127,16 +171,16 @@ static void mmp2_pll_init(struct mmp2_clk_unit *pxa_unit)
|
|||
static DEFINE_SPINLOCK(uart0_lock);
|
||||
static DEFINE_SPINLOCK(uart1_lock);
|
||||
static DEFINE_SPINLOCK(uart2_lock);
|
||||
static const char *uart_parent_names[] = {"uart_pll", "vctcxo"};
|
||||
static const char * const uart_parent_names[] = {"uart_pll", "vctcxo"};
|
||||
|
||||
static DEFINE_SPINLOCK(ssp0_lock);
|
||||
static DEFINE_SPINLOCK(ssp1_lock);
|
||||
static DEFINE_SPINLOCK(ssp2_lock);
|
||||
static DEFINE_SPINLOCK(ssp3_lock);
|
||||
static const char *ssp_parent_names[] = {"vctcxo_4", "vctcxo_2", "vctcxo", "pll1_16"};
|
||||
static const char * const ssp_parent_names[] = {"vctcxo_4", "vctcxo_2", "vctcxo", "pll1_16"};
|
||||
|
||||
static DEFINE_SPINLOCK(timer_lock);
|
||||
static const char *timer_parent_names[] = {"clk32", "vctcxo_4", "vctcxo_2", "vctcxo"};
|
||||
static const char * const timer_parent_names[] = {"clk32", "vctcxo_4", "vctcxo_2", "vctcxo"};
|
||||
|
||||
static DEFINE_SPINLOCK(reset_lock);
|
||||
|
||||
|
@ -176,6 +220,13 @@ static struct mmp_param_gate_clk apbc_gate_clks[] = {
|
|||
{MMP2_CLK_SSP2, "ssp2_clk", "ssp2_mux", CLK_SET_RATE_PARENT, APBC_SSP2, 0x7, 0x3, 0x0, 0, &ssp2_lock},
|
||||
{MMP2_CLK_SSP3, "ssp3_clk", "ssp3_mux", CLK_SET_RATE_PARENT, APBC_SSP3, 0x7, 0x3, 0x0, 0, &ssp3_lock},
|
||||
{MMP2_CLK_TIMER, "timer_clk", "timer_mux", CLK_SET_RATE_PARENT, APBC_TIMER, 0x7, 0x3, 0x0, 0, &timer_lock},
|
||||
{MMP2_CLK_THERMAL0, "thermal0_clk", "vctcxo", CLK_SET_RATE_PARENT, APBC_THERMAL0, 0x7, 0x3, 0x0, MMP_CLK_GATE_NEED_DELAY, &reset_lock},
|
||||
};
|
||||
|
||||
static struct mmp_param_gate_clk mmp3_apbc_gate_clks[] = {
|
||||
{MMP3_CLK_THERMAL1, "thermal1_clk", "vctcxo", CLK_SET_RATE_PARENT, APBC_THERMAL1, 0x7, 0x3, 0x0, MMP_CLK_GATE_NEED_DELAY, &reset_lock},
|
||||
{MMP3_CLK_THERMAL2, "thermal2_clk", "vctcxo", CLK_SET_RATE_PARENT, APBC_THERMAL2, 0x7, 0x3, 0x0, MMP_CLK_GATE_NEED_DELAY, &reset_lock},
|
||||
{MMP3_CLK_THERMAL3, "thermal3_clk", "vctcxo", CLK_SET_RATE_PARENT, APBC_THERMAL3, 0x7, 0x3, 0x0, MMP_CLK_GATE_NEED_DELAY, &reset_lock},
|
||||
};
|
||||
|
||||
static void mmp2_apb_periph_clk_init(struct mmp2_clk_unit *pxa_unit)
|
||||
|
@ -187,10 +238,15 @@ static void mmp2_apb_periph_clk_init(struct mmp2_clk_unit *pxa_unit)
|
|||
|
||||
mmp_register_gate_clks(unit, apbc_gate_clks, pxa_unit->apbc_base,
|
||||
ARRAY_SIZE(apbc_gate_clks));
|
||||
|
||||
if (pxa_unit->model == CLK_MODEL_MMP3) {
|
||||
mmp_register_gate_clks(unit, mmp3_apbc_gate_clks, pxa_unit->apbc_base,
|
||||
ARRAY_SIZE(mmp3_apbc_gate_clks));
|
||||
}
|
||||
}
|
||||
|
||||
static DEFINE_SPINLOCK(sdh_lock);
|
||||
static const char *sdh_parent_names[] = {"pll1_4", "pll2", "usb_pll", "pll1"};
|
||||
static const char * const sdh_parent_names[] = {"pll1_4", "pll2", "usb_pll", "pll1"};
|
||||
static struct mmp_clk_mix_config sdh_mix_config = {
|
||||
.reg_info = DEFINE_MIX_REG_INFO(4, 10, 2, 8, 32),
|
||||
};
|
||||
|
@ -201,11 +257,20 @@ static DEFINE_SPINLOCK(usbhsic1_lock);
|
|||
|
||||
static DEFINE_SPINLOCK(disp0_lock);
|
||||
static DEFINE_SPINLOCK(disp1_lock);
|
||||
static const char *disp_parent_names[] = {"pll1", "pll1_16", "pll2", "vctcxo"};
|
||||
static const char * const disp_parent_names[] = {"pll1", "pll1_16", "pll2", "vctcxo"};
|
||||
|
||||
static DEFINE_SPINLOCK(ccic0_lock);
|
||||
static DEFINE_SPINLOCK(ccic1_lock);
|
||||
static const char *ccic_parent_names[] = {"pll1_2", "pll1_16", "vctcxo"};
|
||||
static const char * const ccic_parent_names[] = {"pll1_2", "pll1_16", "vctcxo"};
|
||||
|
||||
static DEFINE_SPINLOCK(gpu_lock);
|
||||
static const char * const mmp2_gpu_gc_parent_names[] = {"pll1_2", "pll1_3", "pll2_2", "pll2_3", "pll2", "usb_pll"};
|
||||
static u32 mmp2_gpu_gc_parent_table[] = { 0x0000, 0x0040, 0x0080, 0x00c0, 0x1000, 0x1040 };
|
||||
static const char * const mmp2_gpu_bus_parent_names[] = {"pll1_4", "pll2", "pll2_2", "usb_pll"};
|
||||
static u32 mmp2_gpu_bus_parent_table[] = { 0x0000, 0x0020, 0x0030, 0x4020 };
|
||||
static const char * const mmp3_gpu_bus_parent_names[] = {"pll1_4", "pll1_6", "pll1_2", "pll2_2"};
|
||||
static const char * const mmp3_gpu_gc_parent_names[] = {"pll1", "pll2", "pll1_p", "pll2_p"};
|
||||
|
||||
static struct mmp_clk_mix_config ccic0_mix_config = {
|
||||
.reg_info = DEFINE_MIX_REG_INFO(4, 17, 2, 6, 32),
|
||||
};
|
||||
|
@ -218,6 +283,15 @@ static struct mmp_param_mux_clk apmu_mux_clks[] = {
|
|||
{MMP2_CLK_DISP1_MUX, "disp1_mux", disp_parent_names, ARRAY_SIZE(disp_parent_names), CLK_SET_RATE_PARENT, APMU_DISP1, 6, 2, 0, &disp1_lock},
|
||||
};
|
||||
|
||||
static struct mmp_param_mux_clk mmp3_apmu_mux_clks[] = {
|
||||
{0, "gpu_bus_mux", mmp3_gpu_bus_parent_names, ARRAY_SIZE(mmp3_gpu_bus_parent_names),
|
||||
CLK_SET_RATE_PARENT, APMU_GPU, 4, 2, 0, &gpu_lock},
|
||||
{0, "gpu_3d_mux", mmp3_gpu_gc_parent_names, ARRAY_SIZE(mmp3_gpu_gc_parent_names),
|
||||
CLK_SET_RATE_PARENT, APMU_GPU, 6, 2, 0, &gpu_lock},
|
||||
{0, "gpu_2d_mux", mmp3_gpu_gc_parent_names, ARRAY_SIZE(mmp3_gpu_gc_parent_names),
|
||||
CLK_SET_RATE_PARENT, APMU_GPU, 12, 2, 0, &gpu_lock},
|
||||
};
|
||||
|
||||
static struct mmp_param_div_clk apmu_div_clks[] = {
|
||||
{0, "disp0_div", "disp0_mux", CLK_SET_RATE_PARENT, APMU_DISP0, 8, 4, 0, &disp0_lock},
|
||||
{0, "disp0_sphy_div", "disp0_mux", CLK_SET_RATE_PARENT, APMU_DISP0, 15, 5, 0, &disp0_lock},
|
||||
|
@ -226,6 +300,11 @@ static struct mmp_param_div_clk apmu_div_clks[] = {
|
|||
{0, "ccic1_sphy_div", "ccic1_mix_clk", CLK_SET_RATE_PARENT, APMU_CCIC1, 10, 5, 0, &ccic1_lock},
|
||||
};
|
||||
|
||||
static struct mmp_param_div_clk mmp3_apmu_div_clks[] = {
|
||||
{0, "gpu_3d_div", "gpu_3d_mux", CLK_SET_RATE_PARENT, APMU_GPU, 24, 4, 0, &gpu_lock},
|
||||
{0, "gpu_2d_div", "gpu_2d_mux", CLK_SET_RATE_PARENT, APMU_GPU, 28, 4, 0, &gpu_lock},
|
||||
};
|
||||
|
||||
static struct mmp_param_gate_clk apmu_gate_clks[] = {
|
||||
{MMP2_CLK_USB, "usb_clk", "usb_pll", 0, APMU_USB, 0x9, 0x9, 0x0, 0, &usb_lock},
|
||||
{MMP2_CLK_USBHSIC0, "usbhsic0_clk", "usb_pll", 0, APMU_USBHSIC0, 0x1b, 0x1b, 0x0, 0, &usbhsic0_lock},
|
||||
|
@ -235,8 +314,8 @@ static struct mmp_param_gate_clk apmu_gate_clks[] = {
|
|||
{MMP2_CLK_SDH1, "sdh1_clk", "sdh_mix_clk", CLK_SET_RATE_PARENT, APMU_SDH1, 0x1b, 0x1b, 0x0, 0, &sdh_lock},
|
||||
{MMP2_CLK_SDH2, "sdh2_clk", "sdh_mix_clk", CLK_SET_RATE_PARENT, APMU_SDH2, 0x1b, 0x1b, 0x0, 0, &sdh_lock},
|
||||
{MMP2_CLK_SDH3, "sdh3_clk", "sdh_mix_clk", CLK_SET_RATE_PARENT, APMU_SDH3, 0x1b, 0x1b, 0x0, 0, &sdh_lock},
|
||||
{MMP2_CLK_DISP0, "disp0_clk", "disp0_div", CLK_SET_RATE_PARENT, APMU_DISP0, 0x09, 0x09, 0x0, 0, &disp0_lock},
|
||||
{MMP2_CLK_DISP0_LCDC, "disp0_lcdc_clk", "disp0_mux", CLK_SET_RATE_PARENT, APMU_DISP0, 0x12, 0x12, 0x0, 0, &disp0_lock},
|
||||
{MMP2_CLK_DISP0, "disp0_clk", "disp0_div", CLK_SET_RATE_PARENT, APMU_DISP0, 0x12, 0x12, 0x0, 0, &disp0_lock},
|
||||
{MMP2_CLK_DISP0_LCDC, "disp0_lcdc_clk", "disp0_mux", CLK_SET_RATE_PARENT, APMU_DISP0, 0x09, 0x09, 0x0, 0, &disp0_lock},
|
||||
{MMP2_CLK_DISP0_SPHY, "disp0_sphy_clk", "disp0_sphy_div", CLK_SET_RATE_PARENT, APMU_DISP0, 0x1024, 0x1024, 0x0, 0, &disp0_lock},
|
||||
{MMP2_CLK_DISP1, "disp1_clk", "disp1_div", CLK_SET_RATE_PARENT, APMU_DISP1, 0x09, 0x09, 0x0, 0, &disp1_lock},
|
||||
{MMP2_CLK_CCIC_ARBITER, "ccic_arbiter", "vctcxo", CLK_SET_RATE_PARENT, APMU_CCIC0, 0x1800, 0x1800, 0x0, 0, &ccic0_lock},
|
||||
|
@ -246,6 +325,17 @@ static struct mmp_param_gate_clk apmu_gate_clks[] = {
|
|||
{MMP2_CLK_CCIC1, "ccic1_clk", "ccic1_mix_clk", CLK_SET_RATE_PARENT, APMU_CCIC1, 0x1b, 0x1b, 0x0, 0, &ccic1_lock},
|
||||
{MMP2_CLK_CCIC1_PHY, "ccic1_phy_clk", "ccic1_mix_clk", CLK_SET_RATE_PARENT, APMU_CCIC1, 0x24, 0x24, 0x0, 0, &ccic1_lock},
|
||||
{MMP2_CLK_CCIC1_SPHY, "ccic1_sphy_clk", "ccic1_sphy_div", CLK_SET_RATE_PARENT, APMU_CCIC1, 0x300, 0x300, 0x0, 0, &ccic1_lock},
|
||||
{MMP2_CLK_GPU_BUS, "gpu_bus_clk", "gpu_bus_mux", CLK_SET_RATE_PARENT, APMU_GPU, 0xa, 0xa, 0x0, MMP_CLK_GATE_NEED_DELAY, &gpu_lock},
|
||||
};
|
||||
|
||||
static struct mmp_param_gate_clk mmp2_apmu_gate_clks[] = {
|
||||
{MMP2_CLK_GPU_3D, "gpu_3d_clk", "gpu_3d_mux", CLK_SET_RATE_PARENT, APMU_GPU, 0x5, 0x5, 0x0, MMP_CLK_GATE_NEED_DELAY, &gpu_lock},
|
||||
};
|
||||
|
||||
static struct mmp_param_gate_clk mmp3_apmu_gate_clks[] = {
|
||||
{MMP3_CLK_SDH4, "sdh4_clk", "sdh_mix_clk", CLK_SET_RATE_PARENT, APMU_SDH4, 0x1b, 0x1b, 0x0, 0, &sdh_lock},
|
||||
{MMP3_CLK_GPU_3D, "gpu_3d_clk", "gpu_3d_div", CLK_SET_RATE_PARENT, APMU_GPU, 0x5, 0x5, 0x0, MMP_CLK_GATE_NEED_DELAY, &gpu_lock},
|
||||
{MMP3_CLK_GPU_2D, "gpu_2d_clk", "gpu_2d_div", CLK_SET_RATE_PARENT, APMU_GPU, 0x1c0000, 0x1c0000, 0x0, MMP_CLK_GATE_NEED_DELAY, &gpu_lock},
|
||||
};
|
||||
|
||||
static void mmp2_axi_periph_clk_init(struct mmp2_clk_unit *pxa_unit)
|
||||
|
@ -281,6 +371,34 @@ static void mmp2_axi_periph_clk_init(struct mmp2_clk_unit *pxa_unit)
|
|||
|
||||
mmp_register_gate_clks(unit, apmu_gate_clks, pxa_unit->apmu_base,
|
||||
ARRAY_SIZE(apmu_gate_clks));
|
||||
|
||||
if (pxa_unit->model == CLK_MODEL_MMP3) {
|
||||
mmp_register_mux_clks(unit, mmp3_apmu_mux_clks, pxa_unit->apmu_base,
|
||||
ARRAY_SIZE(mmp3_apmu_mux_clks));
|
||||
|
||||
mmp_register_div_clks(unit, mmp3_apmu_div_clks, pxa_unit->apmu_base,
|
||||
ARRAY_SIZE(mmp3_apmu_div_clks));
|
||||
|
||||
mmp_register_gate_clks(unit, mmp3_apmu_gate_clks, pxa_unit->apmu_base,
|
||||
ARRAY_SIZE(mmp3_apmu_gate_clks));
|
||||
} else {
|
||||
clk_register_mux_table(NULL, "gpu_3d_mux", mmp2_gpu_gc_parent_names,
|
||||
ARRAY_SIZE(mmp2_gpu_gc_parent_names),
|
||||
CLK_SET_RATE_PARENT,
|
||||
pxa_unit->apmu_base + APMU_GPU,
|
||||
0, 0x10c0, 0,
|
||||
mmp2_gpu_gc_parent_table, &gpu_lock);
|
||||
|
||||
clk_register_mux_table(NULL, "gpu_bus_mux", mmp2_gpu_bus_parent_names,
|
||||
ARRAY_SIZE(mmp2_gpu_bus_parent_names),
|
||||
CLK_SET_RATE_PARENT,
|
||||
pxa_unit->apmu_base + APMU_GPU,
|
||||
0, 0x4030, 0,
|
||||
mmp2_gpu_bus_parent_table, &gpu_lock);
|
||||
|
||||
mmp_register_gate_clks(unit, mmp2_apmu_gate_clks, pxa_unit->apmu_base,
|
||||
ARRAY_SIZE(mmp2_apmu_gate_clks));
|
||||
}
|
||||
}
|
||||
|
||||
static void mmp2_clk_reset_init(struct device_node *np,
|
||||
|
@ -313,6 +431,11 @@ static void __init mmp2_clk_init(struct device_node *np)
|
|||
if (!pxa_unit)
|
||||
return;
|
||||
|
||||
if (of_device_is_compatible(np, "marvell,mmp3-clock"))
|
||||
pxa_unit->model = CLK_MODEL_MMP3;
|
||||
else
|
||||
pxa_unit->model = CLK_MODEL_MMP2;
|
||||
|
||||
pxa_unit->mpmu_base = of_iomap(np, 0);
|
||||
if (!pxa_unit->mpmu_base) {
|
||||
pr_err("failed to map mpmu registers\n");
|
||||
|
@ -352,3 +475,4 @@ free_memory:
|
|||
}
|
||||
|
||||
CLK_OF_DECLARE(mmp2_clk, "marvell,mmp2-clock", mmp2_clk_init);
|
||||
CLK_OF_DECLARE(mmp3_clk, "marvell,mmp3-clock", mmp2_clk_init);
|
||||
|
|
139
drivers/clk/mmp/clk-pll.c
Normal file
139
drivers/clk/mmp/clk-pll.c
Normal file
|
@ -0,0 +1,139 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* MMP PLL clock rate calculation
|
||||
*
|
||||
* Copyright (C) 2020 Lubomir Rintel <lkundrak@v3.sk>
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include "clk.h"
|
||||
|
||||
#define to_clk_mmp_pll(hw) container_of(hw, struct mmp_clk_pll, hw)
|
||||
|
||||
struct mmp_clk_pll {
|
||||
struct clk_hw hw;
|
||||
unsigned long default_rate;
|
||||
void __iomem *enable_reg;
|
||||
u32 enable;
|
||||
void __iomem *reg;
|
||||
u8 shift;
|
||||
|
||||
unsigned long input_rate;
|
||||
void __iomem *postdiv_reg;
|
||||
u8 postdiv_shift;
|
||||
};
|
||||
|
||||
static int mmp_clk_pll_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
struct mmp_clk_pll *pll = to_clk_mmp_pll(hw);
|
||||
u32 val;
|
||||
|
||||
val = readl_relaxed(pll->enable_reg);
|
||||
if ((val & pll->enable) == pll->enable)
|
||||
return 1;
|
||||
|
||||
/* Some PLLs, if not software controlled, output default clock. */
|
||||
if (pll->default_rate > 0)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long mmp_clk_pll_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct mmp_clk_pll *pll = to_clk_mmp_pll(hw);
|
||||
u32 fbdiv, refdiv, postdiv;
|
||||
u64 rate;
|
||||
u32 val;
|
||||
|
||||
val = readl_relaxed(pll->enable_reg);
|
||||
if ((val & pll->enable) != pll->enable)
|
||||
return pll->default_rate;
|
||||
|
||||
if (pll->reg) {
|
||||
val = readl_relaxed(pll->reg);
|
||||
fbdiv = (val >> pll->shift) & 0x1ff;
|
||||
refdiv = (val >> (pll->shift + 9)) & 0x1f;
|
||||
} else {
|
||||
fbdiv = 2;
|
||||
refdiv = 1;
|
||||
}
|
||||
|
||||
if (pll->postdiv_reg) {
|
||||
/* MMP3 clock rate calculation */
|
||||
static const u8 postdivs[] = {2, 3, 4, 5, 6, 8, 10, 12, 16};
|
||||
|
||||
val = readl_relaxed(pll->postdiv_reg);
|
||||
postdiv = (val >> pll->postdiv_shift) & 0x7;
|
||||
|
||||
rate = pll->input_rate;
|
||||
rate *= 2 * fbdiv;
|
||||
do_div(rate, refdiv);
|
||||
do_div(rate, postdivs[postdiv]);
|
||||
} else {
|
||||
/* MMP2 clock rate calculation */
|
||||
if (refdiv == 3) {
|
||||
rate = 19200000;
|
||||
} else if (refdiv == 4) {
|
||||
rate = 26000000;
|
||||
} else {
|
||||
pr_err("bad refdiv: %d (0x%08x)\n", refdiv, val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
rate *= fbdiv + 2;
|
||||
do_div(rate, refdiv + 2);
|
||||
}
|
||||
|
||||
return (unsigned long)rate;
|
||||
}
|
||||
|
||||
static const struct clk_ops mmp_clk_pll_ops = {
|
||||
.is_enabled = mmp_clk_pll_is_enabled,
|
||||
.recalc_rate = mmp_clk_pll_recalc_rate,
|
||||
};
|
||||
|
||||
struct clk *mmp_clk_register_pll(char *name,
|
||||
unsigned long default_rate,
|
||||
void __iomem *enable_reg, u32 enable,
|
||||
void __iomem *reg, u8 shift,
|
||||
unsigned long input_rate,
|
||||
void __iomem *postdiv_reg, u8 postdiv_shift)
|
||||
{
|
||||
struct mmp_clk_pll *pll;
|
||||
struct clk *clk;
|
||||
struct clk_init_data init;
|
||||
|
||||
pll = kzalloc(sizeof(*pll), GFP_KERNEL);
|
||||
if (!pll)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init.name = name;
|
||||
init.ops = &mmp_clk_pll_ops;
|
||||
init.flags = 0;
|
||||
init.parent_names = NULL;
|
||||
init.num_parents = 0;
|
||||
|
||||
pll->default_rate = default_rate;
|
||||
pll->enable_reg = enable_reg;
|
||||
pll->enable = enable;
|
||||
pll->reg = reg;
|
||||
pll->shift = shift;
|
||||
|
||||
pll->input_rate = input_rate;
|
||||
pll->postdiv_reg = postdiv_reg;
|
||||
pll->postdiv_shift = postdiv_shift;
|
||||
|
||||
pll->hw.init = &init;
|
||||
|
||||
clk = clk_register(NULL, &pll->hw);
|
||||
|
||||
if (IS_ERR(clk))
|
||||
kfree(pll);
|
||||
|
||||
return clk;
|
||||
}
|
|
@ -176,6 +176,37 @@ void mmp_register_div_clks(struct mmp_clk_unit *unit,
|
|||
}
|
||||
}
|
||||
|
||||
void mmp_register_pll_clks(struct mmp_clk_unit *unit,
|
||||
struct mmp_param_pll_clk *clks,
|
||||
void __iomem *base, int size)
|
||||
{
|
||||
struct clk *clk;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
void __iomem *reg = NULL;
|
||||
|
||||
if (clks[i].offset)
|
||||
reg = base + clks[i].offset;
|
||||
|
||||
clk = mmp_clk_register_pll(clks[i].name,
|
||||
clks[i].default_rate,
|
||||
base + clks[i].enable_offset,
|
||||
clks[i].enable,
|
||||
reg, clks[i].shift,
|
||||
clks[i].input_rate,
|
||||
base + clks[i].postdiv_offset,
|
||||
clks[i].postdiv_shift);
|
||||
if (IS_ERR(clk)) {
|
||||
pr_err("%s: failed to register clock %s\n",
|
||||
__func__, clks[i].name);
|
||||
continue;
|
||||
}
|
||||
if (clks[i].id)
|
||||
unit->clk_table[clks[i].id] = clk;
|
||||
}
|
||||
}
|
||||
|
||||
void mmp_clk_add(struct mmp_clk_unit *unit, unsigned int id,
|
||||
struct clk *clk)
|
||||
{
|
||||
|
|
|
@ -97,7 +97,7 @@ struct mmp_clk_mix {
|
|||
extern const struct clk_ops mmp_clk_mix_ops;
|
||||
extern struct clk *mmp_clk_register_mix(struct device *dev,
|
||||
const char *name,
|
||||
const char **parent_names,
|
||||
const char * const *parent_names,
|
||||
u8 num_parents,
|
||||
unsigned long flags,
|
||||
struct mmp_clk_mix_config *config,
|
||||
|
@ -124,9 +124,6 @@ extern struct clk *mmp_clk_register_gate(struct device *dev, const char *name,
|
|||
u32 val_disable, unsigned int gate_flags,
|
||||
spinlock_t *lock);
|
||||
|
||||
|
||||
extern struct clk *mmp_clk_register_pll2(const char *name,
|
||||
const char *parent_name, unsigned long flags);
|
||||
extern struct clk *mmp_clk_register_apbc(const char *name,
|
||||
const char *parent_name, void __iomem *base,
|
||||
unsigned int delay, unsigned int apbc_flags, spinlock_t *lock);
|
||||
|
@ -196,7 +193,7 @@ void mmp_register_gate_clks(struct mmp_clk_unit *unit,
|
|||
struct mmp_param_mux_clk {
|
||||
unsigned int id;
|
||||
char *name;
|
||||
const char **parent_name;
|
||||
const char * const *parent_name;
|
||||
u8 num_parents;
|
||||
unsigned long flags;
|
||||
unsigned long offset;
|
||||
|
@ -224,6 +221,30 @@ void mmp_register_div_clks(struct mmp_clk_unit *unit,
|
|||
struct mmp_param_div_clk *clks,
|
||||
void __iomem *base, int size);
|
||||
|
||||
struct mmp_param_pll_clk {
|
||||
unsigned int id;
|
||||
char *name;
|
||||
unsigned long default_rate;
|
||||
unsigned long enable_offset;
|
||||
u32 enable;
|
||||
unsigned long offset;
|
||||
u8 shift;
|
||||
/* MMP3 specific: */
|
||||
unsigned long input_rate;
|
||||
unsigned long postdiv_offset;
|
||||
unsigned long postdiv_shift;
|
||||
};
|
||||
void mmp_register_pll_clks(struct mmp_clk_unit *unit,
|
||||
struct mmp_param_pll_clk *clks,
|
||||
void __iomem *base, int size);
|
||||
|
||||
extern struct clk *mmp_clk_register_pll(char *name,
|
||||
unsigned long default_rate,
|
||||
void __iomem *enable_reg, u32 enable,
|
||||
void __iomem *reg, u8 shift,
|
||||
unsigned long input_rate,
|
||||
void __iomem *postdiv_reg, u8 postdiv_shift);
|
||||
|
||||
#define DEFINE_MIX_REG_INFO(w_d, s_d, w_m, s_m, fc) \
|
||||
{ \
|
||||
.width_div = (w_d), \
|
||||
|
|
|
@ -33,18 +33,6 @@
|
|||
|
||||
#define INTEGRATOR_AP_PCI_25_33_MHZ BIT(8)
|
||||
|
||||
/**
|
||||
* enum icst_control_type - the type of ICST control register
|
||||
*/
|
||||
enum icst_control_type {
|
||||
ICST_VERSATILE, /* The standard type, all control bits available */
|
||||
ICST_INTEGRATOR_AP_CM, /* Only 8 bits of VDW available */
|
||||
ICST_INTEGRATOR_AP_SYS, /* Only 8 bits of VDW available */
|
||||
ICST_INTEGRATOR_AP_PCI, /* Odd bit pattern storage */
|
||||
ICST_INTEGRATOR_CP_CM_CORE, /* Only 8 bits of VDW and 3 bits of OD */
|
||||
ICST_INTEGRATOR_CP_CM_MEM, /* Only 8 bits of VDW and 3 bits of OD */
|
||||
};
|
||||
|
||||
/**
|
||||
* struct clk_icst - ICST VCO clock wrapper
|
||||
* @hw: corresponding clock hardware entry
|
||||
|
@ -344,12 +332,12 @@ static const struct clk_ops icst_ops = {
|
|||
.set_rate = icst_set_rate,
|
||||
};
|
||||
|
||||
static struct clk *icst_clk_setup(struct device *dev,
|
||||
const struct clk_icst_desc *desc,
|
||||
const char *name,
|
||||
const char *parent_name,
|
||||
struct regmap *map,
|
||||
enum icst_control_type ctype)
|
||||
struct clk *icst_clk_setup(struct device *dev,
|
||||
const struct clk_icst_desc *desc,
|
||||
const char *name,
|
||||
const char *parent_name,
|
||||
struct regmap *map,
|
||||
enum icst_control_type ctype)
|
||||
{
|
||||
struct clk *clk;
|
||||
struct clk_icst *icst;
|
||||
|
@ -386,6 +374,7 @@ static struct clk *icst_clk_setup(struct device *dev,
|
|||
|
||||
return clk;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(icst_clk_setup);
|
||||
|
||||
struct clk *icst_clk_register(struct device *dev,
|
||||
const struct clk_icst_desc *desc,
|
||||
|
|
|
@ -1,4 +1,19 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
struct regmap;
|
||||
|
||||
/**
|
||||
* enum icst_control_type - the type of ICST control register
|
||||
*/
|
||||
enum icst_control_type {
|
||||
ICST_VERSATILE, /* The standard type, all control bits available */
|
||||
ICST_INTEGRATOR_AP_CM, /* Only 8 bits of VDW available */
|
||||
ICST_INTEGRATOR_AP_SYS, /* Only 8 bits of VDW available */
|
||||
ICST_INTEGRATOR_AP_PCI, /* Odd bit pattern storage */
|
||||
ICST_INTEGRATOR_CP_CM_CORE, /* Only 8 bits of VDW and 3 bits of OD */
|
||||
ICST_INTEGRATOR_CP_CM_MEM, /* Only 8 bits of VDW and 3 bits of OD */
|
||||
ICST_INTEGRATOR_IM_PD1, /* Like the Versatile, all control bits */
|
||||
};
|
||||
|
||||
/**
|
||||
* struct clk_icst_desc - descriptor for the ICST VCO
|
||||
* @params: ICST parameters
|
||||
|
@ -17,3 +32,10 @@ struct clk *icst_clk_register(struct device *dev,
|
|||
const char *name,
|
||||
const char *parent_name,
|
||||
void __iomem *base);
|
||||
|
||||
struct clk *icst_clk_setup(struct device *dev,
|
||||
const struct clk_icst_desc *desc,
|
||||
const char *name,
|
||||
const char *parent_name,
|
||||
struct regmap *map,
|
||||
enum icst_control_type ctype);
|
||||
|
|
|
@ -7,7 +7,11 @@
|
|||
#include <linux/clkdev.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/platform_data/clk-integrator.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include "icst.h"
|
||||
#include "clk-icst.h"
|
||||
|
@ -175,3 +179,78 @@ void integrator_impd1_clk_exit(unsigned int id)
|
|||
kfree(imc->pclkname);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(integrator_impd1_clk_exit);
|
||||
|
||||
static int integrator_impd1_clk_spawn(struct device *dev,
|
||||
struct device_node *parent,
|
||||
struct device_node *np)
|
||||
{
|
||||
struct regmap *map;
|
||||
struct clk *clk = ERR_PTR(-EINVAL);
|
||||
const char *name = np->name;
|
||||
const char *parent_name;
|
||||
const struct clk_icst_desc *desc;
|
||||
int ret;
|
||||
|
||||
map = syscon_node_to_regmap(parent);
|
||||
if (IS_ERR(map)) {
|
||||
pr_err("no regmap for syscon IM-PD1 ICST clock parent\n");
|
||||
return PTR_ERR(map);
|
||||
}
|
||||
|
||||
if (of_device_is_compatible(np, "arm,impd1-vco1")) {
|
||||
desc = &impd1_icst1_desc;
|
||||
} else if (of_device_is_compatible(np, "arm,impd1-vco2")) {
|
||||
desc = &impd1_icst2_desc;
|
||||
} else {
|
||||
dev_err(dev, "not a clock node %s\n", name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
parent_name = of_clk_get_parent_name(np, 0);
|
||||
clk = icst_clk_setup(NULL, desc, name, parent_name, map,
|
||||
ICST_INTEGRATOR_IM_PD1);
|
||||
if (!IS_ERR(clk)) {
|
||||
of_clk_add_provider(np, of_clk_src_simple_get, clk);
|
||||
ret = 0;
|
||||
} else {
|
||||
dev_err(dev, "error setting up IM-PD1 ICST clock\n");
|
||||
ret = PTR_ERR(clk);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int integrator_impd1_clk_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct device_node *child;
|
||||
int ret = 0;
|
||||
|
||||
for_each_available_child_of_node(np, child) {
|
||||
ret = integrator_impd1_clk_spawn(dev, np, child);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id impd1_syscon_match[] = {
|
||||
{ .compatible = "arm,im-pd1-syscon", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, impd1_syscon_match);
|
||||
|
||||
static struct platform_driver impd1_clk_driver = {
|
||||
.driver = {
|
||||
.name = "impd1-clk",
|
||||
.of_match_table = impd1_syscon_match,
|
||||
},
|
||||
.probe = integrator_impd1_clk_probe,
|
||||
};
|
||||
builtin_platform_driver(impd1_clk_driver);
|
||||
|
||||
MODULE_AUTHOR("Linus Walleij <linusw@kernel.org>");
|
||||
MODULE_DESCRIPTION("Arm IM-PD1 module clock driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
|
@ -26,6 +26,9 @@
|
|||
#define MMP2_CLK_VCTCXO_4 25
|
||||
#define MMP2_CLK_UART_PLL 26
|
||||
#define MMP2_CLK_USB_PLL 27
|
||||
#define MMP3_CLK_PLL1_P 28
|
||||
#define MMP3_CLK_PLL2_P 29
|
||||
#define MMP3_CLK_PLL3 30
|
||||
|
||||
/* apb periphrals */
|
||||
#define MMP2_CLK_TWSI0 60
|
||||
|
@ -50,6 +53,10 @@
|
|||
#define MMP2_CLK_SSP2 79
|
||||
#define MMP2_CLK_SSP3 80
|
||||
#define MMP2_CLK_TIMER 81
|
||||
#define MMP2_CLK_THERMAL0 82
|
||||
#define MMP3_CLK_THERMAL1 83
|
||||
#define MMP3_CLK_THERMAL2 84
|
||||
#define MMP3_CLK_THERMAL3 85
|
||||
|
||||
/* axi periphrals */
|
||||
#define MMP2_CLK_SDH0 101
|
||||
|
@ -74,6 +81,12 @@
|
|||
#define MMP2_CLK_DISP0_LCDC 120
|
||||
#define MMP2_CLK_USBHSIC0 121
|
||||
#define MMP2_CLK_USBHSIC1 122
|
||||
#define MMP2_CLK_GPU_BUS 123
|
||||
#define MMP3_CLK_GPU_BUS MMP2_CLK_GPU_BUS
|
||||
#define MMP2_CLK_GPU_3D 124
|
||||
#define MMP3_CLK_GPU_3D MMP2_CLK_GPU_3D
|
||||
#define MMP3_CLK_GPU_2D 125
|
||||
#define MMP3_CLK_SDH4 126
|
||||
|
||||
#define MMP2_NR_CLKS 200
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue