The TZ1090 top level register region controls the majority of the SoC's clocking infrastructure. Most of the complexity comes from the description of the 2 main banks of clock muxes (TOP_CLKSWITCH, TOP_CLKSWITCH2), with 2 banks of clock gates (TOP_CLKENAB, TOP_CLKENAB2) which roughly speaking gate the outputs of the muxes. Also included are a variety of deleters, dividers, and PLLs. The only clock specific policy decisions applied (so far) are: - The UART divider which is flagged with CLK_SET_RATE_PARENT so that clock changes propagate up to the uart_sw mux so that the higher frequency system clock can be used as a source rather than the main external oscillator, in order to get a more accurate baud rate. - The main system clock divider which is flagged with CLK_DIVIDER_READ_ONLY to prevent the system clock being altered. Signed-off-by: James Hogan <james.hogan@xxxxxxxxxx> Cc: Mike Turquette <mturquette@xxxxxxxxxx> Cc: linux-metag@xxxxxxxxxxxxxxx --- Changes since v1 (patch 15): - New patch. - Convert explicit DT representation of clock infrastructure using generic bindings to several TZ1090 specific bindings representing groups of TZ1090 clocks. - Add divider specific flags (policy) which were previously in divider DT driver. - Tweak various clock names. A couple were wrong, some had the slightly redundant text "clk" in them, and others were output clocks so were better named without the "_en" which the output of clock gates tend to be called. - Add TOP_CLKEN register which gates system clock to PDC. - Tweak ascii art a little. --- drivers/clk/tz1090/Makefile | 1 + drivers/clk/tz1090/clk-tz1090-top.c | 364 ++++++++++++++++++++++++++++++++++++ 2 files changed, 365 insertions(+) create mode 100644 drivers/clk/tz1090/clk-tz1090-top.c diff --git a/drivers/clk/tz1090/Makefile b/drivers/clk/tz1090/Makefile index a762cdf..873a8f6 100644 --- a/drivers/clk/tz1090/Makefile +++ b/drivers/clk/tz1090/Makefile @@ -8,3 +8,4 @@ obj-y += clk-tz1090-mux-bank.o obj-y += clk-tz1090-pll.o obj-y += clk-tz1090-pdc.o +obj-y += clk-tz1090-top.o diff --git a/drivers/clk/tz1090/clk-tz1090-top.c b/drivers/clk/tz1090/clk-tz1090-top.c new file mode 100644 index 0000000..4f7316b --- /dev/null +++ b/drivers/clk/tz1090/clk-tz1090-top.c @@ -0,0 +1,364 @@ +/* + * Copyright (C) 2013-2014 Imagination Technologies Ltd. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * TZ1090 TOP Clocks + */ + +#include <dt-bindings/clock/tz1090-top.h> + +#include "clk.h" + +/* Register offsets into top level memory region */ +#define TOP_CLKEN 0x00 +#define TOP_CLKSTATUS 0x04 +#define TOP_CLKSWITCH 0x08 +#define TOP_CLKENAB 0x0c +#define TOP_CLKDELETE 0x10 +#define TOP_SYSCLK_DIV 0x14 +#define TOP_META_CLKDIV 0x18 +#define TOP_META_CLKDELETE 0x1c +#define TOP_AFE_DIV 0x20 +#define TOP_ADCPLL_DIV 0x24 +#define TOP_UARTCLK_DIV 0x28 +#define TOP_PDMCK_CTL 0x30 +#define TOP_SPICLK_DIV 0x34 +#define TOP_SPI1CLK_DIV 0x38 +#define TOP_I2SCLK_DIV 0x3c +#define TOP_USB_PLLDIV 0x40 +#define TOP_SDHOSTCLK_DIV 0x44 +#define TOP_RING_OP_DIV 0x48 +#define TOP_SYSPLL_CTL0 0x50 +#define TOP_ADCPLL_CTL0 0x58 +#define TOP_META_CLK 0x80 +#define TOP_CLKSWITCH2 0x88 +#define TOP_CLKENAB2 0x8c +#define TOP_I2S_DIV2 0x90 +#define TOP_META_TRACE_CLK_DIV 0x94 +#define TOP_PIXEL_CLK_DIV 0x98 +#define TOP_CLKOUT0_DIV 0x9c +#define TOP_CLKOUT1_DIV 0xa0 +#define TOP_UCC0_CLKDELETE 0xa4 +#define TOP_UCC1_CLKDELETE 0xa8 +#define TOP_DDR_CLKDIV 0xac + +/* + * CR_TOP_CLKSWITCH + * ================ + * ___________ _________ _____________ + * xtal1 ------o| sys_sw \______| sys_pll |_| sys_clk_div |__ + * xtal2 -------|___________/ 0 |_________| |_____________| | + * __________________________________________________| + * | ___________ + * xtal1 ----|-o|sysclk1_sw \____________________________ + * sys_clk_div `--|___________/ 1 sys_clk_x2_undeleted + * xtal1 ------o|clkout0_sw0\______ + * afe_progdiv1 -------|___________/ 2 | + * sys_undeleted ------o|clkout0_sw1\____ | + * if0_sw -------|___________/ 3 | | CR_TOP_CLKENAB + * ,-;==================='-' ============== + * | | ___________ ___________ + * clkout0_sw0 | `-o|clkout0_sw2\____ _| out0_inv \_____ + * xtal2 --|----|___________/ 4 | | |___________/ 4 + * | ___________________| |__________________ + * | | ___________ ___________ | + * clkout0_sw2 | `-o|clkout0_sw3\__________| out0_en \____| + * clkout0_sw1 `----|___________/ 5 |___________/ 5 + * xtal1 ------o|clkout1_sw0\______ . + * xtal2 -------|___________/ 6 | . + * sys_undeleted ------o|clkout1_sw1\____ | . + * if1_sw -------|___________/ 7 | | . + * ,-;==================='-' . + * | | ___________ ___________ + * adcpll_clk --|-|-o|clkout1_sw2\____ _| out1_inv \_____ + * clkout1_sw1 `-|--|___________/ 8 | | |___________/ 8 + * _|___________________| |__________________ + * | | ___________ ___________ | + * clkout1_sw0 | `-o|clkout1_sw3\__________| out1_en \____| + * clkout1_sw2 `----|___________/ 9 |___________/ 9 + * xtal1 ------o| i2s_sw2 \______ . + * sys_undeleted -------|___________/ 10 | . + * xtal2 ------o| i2s_sw0 \____ | . + * adcpll_clk -------|___________/ 11 | | . + * ,-;==================='-' . + * | | ___________ ___________ + * i2s_sw2 | `-o| i2s_sw1 \__________| i2s_en \_____ + * i2s_sw0 `----|___________/ 12 |___________/ 12 + * xtal1 ------o| scb_sw \__________| scb_en \_____ + * sys_undeleted -------|___________/ 13 |___________/ 13 + * xtal1 ------o| uart_sw \__________| uart_en \_____ + * sys_undeleted -------|___________/ 14 |___________/ 14 + * xtal1 ------o|ext_stc0_sw\__________|ext_stc0_en\_____ + * xtal2 -------|___________/ 16 |___________/ 16 + * xtal1 ------o|ext_stc1_sw\__________|ext_stc1_en\_____ + * xtal2 -------|___________/ 17 |___________/ 17 + * adcpll_clk ------o| usb_sw0 \____ . + * afe_progdiv3 -------|___________/ 18 | . + * ___________________| . + * | ___________ ___________ + * usb_sw3 ,-|-o| usb_sw1 \__________| usb_en \_____ + * usb_sw0 | `--|___________/ 19 |___________/ 19 + * xtal1 --|---o| afe_sw0 \________________ + * afe_sw1 ,-|----|___________/ 20 . | _____________ + * `-`=====================:-. . |___| afe_clk_div |________ + * ___________ | | . |_____________| afe_clk + * adcpll_en ,---o| afe_sw1 \____| | . + * xtal2 --|----|___________/ 21 | . ________ + * xtal1 --|---o|adcpll_sw0 \______|_____________| adcpll |_____________ + * xtal2 --|----|___________/ 22 | . |________| adcpll_clk + * xtal1 --|---o|adcpll_sw1 \____ | . + * xtal2 --|----|___________/ 23 | | . + * | ___________________| | . + * | | ___________ | . ________________ + * adcpll_en +-|-o|adcpll_sw2 \______|_____________| adcpll_clk_div |_____ + * adcpll_sw1 | `--|___________/ 24 | . |________________| + * |_______________________|_____________________ + * ___________ | ___________ | + * sys_undeleted -------|adcpll_sw3 \______|___| adcpll_en \_____| + * adcpll_clk -------|___________/ 25 | |___________/ 25 + * xtal1 -------| usb_sw2 \____ | + * xtal2 -------|___________/ 28 | | + * ___________________| | + * | ___________ | + * usb_sw2 `--| usb_sw3 \______| + * sys_undeleted -------|___________/ 29 + */ +MUX_BANK(tz1090_top_clkswitch, CLK_TOP_CLKSWITCH_BASE, TOP_CLKSWITCH, + /* bit in[0] in[1] out */ + MUX( 0, "@xtal1", "@xtal2", "sys_sw") + MUX( 1, "@xtal1", "sys_div", "sys_x2_undeleted") + MUX( 2, "@xtal1", "@afe_progdiv1", "out0_sw0") + MUX( 3, "sys_undeleted", "if0_sw", "out0_sw1") + MUX( 4, "out0_sw0", "@xtal2", "out0_sw2") + MUX( 5, "out0_sw2", "out0_sw1", "out0_sw3") + MUX( 6, "@xtal1", "@xtal2", "out1_sw0") + MUX( 7, "sys_undeleted", "if1_sw", "out1_sw1") + MUX( 8, "adcpll", "out1_sw1", "out1_sw2") + MUX( 9, "out1_sw0", "out1_sw2", "out1_sw3") + MUX(10, "@xtal1", "sys_undeleted", "i2s_sw2") + MUX(11, "@xtal2", "adcpll", "i2s_sw0") + MUX(12, "i2s_sw2", "i2s_sw0", "i2s_sw1") + MUX(13, "@xtal1", "sys_undeleted", "scb_sw") + MUX(14, "@xtal1", "sys_undeleted", "uart_sw") + /* bit 15 unused */ + MUX(16, "@xtal1", "@xtal2", "ext_stc0_sw") + MUX(17, "@xtal1", "@xtal2", "ext_stc1_sw") + MUX(18, "adcpll", "@afe_progdiv3", "usb_sw0") + MUX(19, "usb_sw3", "usb_sw0", "usb_sw1") + MUX(20, "@xtal1", "afe_sw1", "afe_sw0") + MUX(21, "adcpll_en", "@xtal2", "afe_sw1") + MUX(22, "@xtal1", "@xtal2", "adcpll_sw0") + MUX(23, "@xtal1", "@xtal2", "adcpll_sw1") + MUX(24, "adcpll_en", "adcpll_sw1", "adcpll_sw2") + MUX(25, "sys_undeleted", "adcpll", "adcpll_sw3") + /* bits 26..27 unused */ + MUX(28, "@xtal1", "@xtal2", "usb_sw2") + MUX(29, "usb_sw2", "sys_undeleted", "usb_sw3") + /* bits 30..31 unused */ +); + +GATE_BANK(tz1090_top_clkenab, CLK_TOP_CLKENAB_BASE, TOP_CLKENAB, + /* bit in out */ + /* bits 0..4 unused */ + GATE( 5, "out0_sw3", "out0_en") + /* bits 6..8 unused */ + GATE( 9, "out1_sw3", "out1_en") + /* bits 10..11 unused */ + GATE(12, "i2s_sw1", "i2s_en") + GATE(13, "scb_sw", "scb") + GATE(14, "uart_sw", "uart_en") + /* bit 15 unused */ + GATE(16, "ext_stc0_sw", "ext_stc0") + GATE(17, "ext_stc1_sw", "ext_stc1") + /* bit 18 unused */ + GATE(19, "usb_sw1", "usb_en") + /* bits 20..24 unused */ + GATE(25, "adcpll_sw3", "adcpll_en") + /* bits 26..31 unused */ +); + +/* + * CR_TOP_CLKSWITCH2 + * ================= + * ___________ + * xtal1 ------o| pixel_sw0 \______ + * pixel_sw3 ,----|___________/ 0 | + * sys_undeleted --+---o| pixel_sw1 \____ | + * pixel_sw4 | ,--|___________/ 1 | | + * `-`===================|=|=:-. CR_TOP_CLKENAB2 + * ,-;==================='-' | | =============== + * | | ___________ | | __________ + * pixel_sw0 | `-o| pixel_sw2 \________|_|___| pixel_en \_____ + * pixel_sw1 `----|___________/ 2 | | |__________/ 2 + * adcpll_clk ------o| pixel_sw3 \________| | . + * afe_progdiv3 -------|___________/ 3 | . + * usb_phy_clk ------o| pixel_sw4 \__________| . + * xtal2 -------|___________/ 4 __________ + * iqadc_sync ------o| if1_sw \______________| if1_en \_____ + * ext_adc_dac --+----|___________/ 5 |__________/ 5 + * afe_rxsync --|---o| if0_sw \______________| if0_en \_____ + * ext_adc_dac |`---|___________/ 6 _____|__________/ 6 + * |.________________________| ext_adc_dac_en \_____ + * | ___________ |________________/ 7 + * afe_txsync --|---o| dac0_sw \______________| dac0_en \_____ + * ext_adc_dac `----|___________/ 8 |__________/ 8 + * ucc1_clk_del ------o| ucc1_sw \______________| ucc1_en \_____ + * ucc0_clk_del --+----|___________/ 9 |__________/ 9 + * ucc0_clk_del `---o| ucc0_sw \______________| ucc0_en \_____ + * sys_clk -------|___________/ 10 |__________/ 10 + */ +MUX_BANK(tz1090_top_clkswitch2, CLK_TOP_CLKSWITCH2_BASE, TOP_CLKSWITCH2, + /* bit in[0] in[1] out */ + MUX( 0, "@xtal1", "pixel_sw3", "pixel_sw0") + MUX( 1, "sys_undeleted", "pixel_sw4", "pixel_sw1") + MUX( 2, "pixel_sw0", "pixel_sw1", "pixel_sw2") + MUX( 3, "adcpll", "@afe_progdiv3", "pixel_sw3") + MUX( 4, "usb_phy", "@xtal2", "pixel_sw4") + MUX( 5, "@iqadc_sync", "@ext_adc_dac", "if1_sw") + MUX( 6, "@afe_rxsync", "@ext_adc_dac", "if0_sw") + /* bit 7 unused */ + MUX( 8, "@afe_txsync", "@ext_adc_dac", "dac0_sw") + MUX( 9, "ucc1_del", "ucc0", "ucc1_sw") + MUX(10, "ucc0", "sys", "ucc0_sw") + /* bits 11..31 unused */ +); + +GATE_BANK(tz1090_top_clkenab2, CLK_TOP_CLKENAB2_BASE, TOP_CLKENAB2, + /* bit in out */ + /* bits 0..1 unused */ + GATE( 2, "pixel_sw2", "pixel_en") + /* bits 3..4 unused */ + GATE( 5, "if1_sw", "if1") + GATE( 6, "if0_sw", "if0") + GATE( 7, "@ext_adc_dac", "ext_adc_dac_en") + GATE( 8, "dac0_sw", "dac0") + GATE( 9, "ucc1_sw", "sys_ucc1") + GATE(10, "ucc0_sw", "sys_mtx") + /* bits 11..31 unused */ +); + +/* + * Deleters + * ======== + * + * sys_undeleted ---[ clkdelete ]--- sys_clk + * sys_x2_undeleted ---[ meta_clkdelete ]--- meta_core_clk + * sys_undeleted ---[ clkdelete ]--- ucc0_clk_del + * sys_undeleted ---[ clkdelete ]--- ucc1_clk_del + */ + +static const struct tz1090_clk_deleter tz1090_top_deleters[] __initconst = { + DEL(CLK_TOP_SYS, "sys_undeleted", "sys", TOP_CLKDELETE), + DEL(CLK_TOP_META, "sys_x2_undeleted", "meta", TOP_META_CLKDELETE), + DEL(CLK_TOP_UCC0, "sys_undeleted", "ucc0", TOP_UCC0_CLKDELETE), + DEL(CLK_TOP_UCC1_DEL, "sys_undeleted", "ucc1_del", TOP_UCC1_CLKDELETE), +}; + +/* + * Dividers + * ======== + * + * sys_pll ---[ sys_clk_div ]--- sys_div + * sys_x2_undeleted ---[ meta_clk_div ]--- sys_undeleted + * afe_sw0 ---[ afe_clk_div ]--- afe_clk + * adcpll_sw2 ---[ adcpll_clk_div ]--- adcpll_div + * uart_en ---[ uart_clk_div ]--- uart_clk + * sys_undeleted ---[ pdm_clk_div ]--- pdm_clk + * sys_undeleted ---[ spi0_clk_div ]--- spi0_clk + * sys_undeleted ---[ spi1_clk_div ]--- spi1_clk + * i2s_en ---[ i2sm_clk_div ]--- i2sm + * usb_en ---[ usbpll_clk_div ]--- usb_phy_clk + * sys_undeleted ---[ sdhost_clk_div ]--- sdhost_clk + * sys_undeleted ---[ ring_osc_clk_div ]--- ring_osc_clk + * i2sm_clk ---[ i2s_clk_div2 ]--- i2s_clk + * sys_undeleted ---[ meta_trace_clk_div ]--- meta_trace_clk + * pixel_en ---[ pixel_clk_div ]--- pixel_clk + * clkout0_en ---[ clkout0_clk_div ]--- clkout0 + * clkout1_en ---[ clkout1_clk_div ]--- clkout1 + * ddr_en ---[ ddr_clk_div ]--- ddr_clk + */ + +static const struct tz1090_clk_divider tz1090_top_dividers[] __initconst = { + DIV(CLK_TOP_SYS_DIV, "sys_pll", "sys_div", TOP_SYSCLK_DIV, 8), + /* + * CLK_DIVIDER_READ_ONLY: sys_undeleted is set up by the bootloader + * along with sys_pll, and has a whole bunch of derivative peripheral + * clocks. It would be really bad for it to change on the fly. + */ + DIV_FLAGS(CLK_TOP_SYS_UNDELETED, "sys_x2_undeleted", "sys_undeleted", TOP_META_CLKDIV, 2, + 0, CLK_DIVIDER_READ_ONLY), + DIV(CLK_TOP_AFE, "afe_sw0", "afe", TOP_AFE_DIV, 8), + DIV(CLK_TOP_ADCPLL_DIV, "adcpll_sw2", "adcpll_div", TOP_ADCPLL_DIV, 8), + /* + * CLK_SET_RATE_PARENT: UART clock changes must propagate up to uart_sw, + * which muxes between XTAL1 and sys_undeleted, in order to get enough + * precision. + */ + DIV_FLAGS(CLK_TOP_UART, "uart_en", "uart", TOP_UARTCLK_DIV, 8, + CLK_SET_RATE_PARENT, 0), + DIV(CLK_TOP_PDM, "sys_undeleted", "pdm", TOP_PDMCK_CTL, 3), + DIV(CLK_TOP_SPI0, "sys_undeleted", "spi0", TOP_SPICLK_DIV, 8), + DIV(CLK_TOP_SPI1, "sys_undeleted", "spi1", TOP_SPI1CLK_DIV, 8), + DIV(CLK_TOP_I2SM, "i2s_en", "i2sm", TOP_I2SCLK_DIV, 8), + DIV(CLK_TOP_USB_PHY, "usb_en", "usb_phy", TOP_USB_PLLDIV, 8), + DIV(CLK_TOP_SDHOST, "sys_undeleted", "sdhost", TOP_SDHOSTCLK_DIV, 8), + DIV(CLK_TOP_RING_OSC, "sys_undeleted", "ring_osc", TOP_RING_OP_DIV, 4), + DIV(CLK_TOP_I2S, "i2sm", "i2s", TOP_I2S_DIV2, 8), + DIV(CLK_TOP_META_TRACE, "sys_undeleted", "meta_trace", TOP_META_TRACE_CLK_DIV, 8), + DIV(CLK_TOP_PIXEL, "pixel_en", "pixel", TOP_PIXEL_CLK_DIV, 8), + DIV(CLK_TOP_OUT0, "out0_en", "out0", TOP_CLKOUT0_DIV, 8), + DIV(CLK_TOP_OUT1, "out1_en", "out1", TOP_CLKOUT1_DIV, 8), + DIV(CLK_TOP_DDR, "@ddr_en", "ddr", TOP_DDR_CLKDIV, 8), +}; + +/* + * PLLs + * ==== + * + * sys_sw ---[ sys_pll ]--- + * adcpll_sw0 ---[ adcpll ]--- + */ + +static const struct tz1090_clk_pll tz1090_top_plls[] __initconst = { + PLL(CLK_TOP_SYSPLL, "sys_sw", "sys_pll", TOP_SYSPLL_CTL0), + PLL(CLK_TOP_ADCPLL, "adcpll_sw0", "adcpll", TOP_ADCPLL_CTL0), +}; + +/* + * CR_TOP_CLKEN + * ============ + * + * sys ----[ pdc_sys_en ]--- 0 sys_pdc + */ +GATE_BANK(tz1090_top_clken, CLK_TOP_CLKEN_BASE, TOP_CLKEN, + /* bit in out */ + GATE( 0, "sys", "sys_pdc") + /* bits 1..31 unused */ +); + +static void __init tz1090_top_clk_init(struct device_node *np) +{ + struct tz1090_clk_provider *p; + + p = tz1090_clk_alloc_provider(np, CLK_TOP_MAX); + if (!p) + return; + + tz1090_clk_register_mux_bank(p, &tz1090_top_clkswitch); + tz1090_clk_register_mux_bank(p, &tz1090_top_clkswitch2); + tz1090_clk_register_gate_bank(p, &tz1090_top_clkenab); + tz1090_clk_register_gate_bank(p, &tz1090_top_clkenab2); + tz1090_clk_register_gate_bank(p, &tz1090_top_clken); + tz1090_clk_register_deleters(p, tz1090_top_deleters, + ARRAY_SIZE(tz1090_top_deleters)); + tz1090_clk_register_dividers(p, tz1090_top_dividers, + ARRAY_SIZE(tz1090_top_dividers)); + tz1090_clk_register_plls(p, tz1090_top_plls, + ARRAY_SIZE(tz1090_top_plls)); + + tz1090_clk_register_provider(p); +} +CLK_OF_DECLARE(tz1090_top_clk, "img,tz1090-top-clocks", tz1090_top_clk_init); -- 2.0.4 -- To unsubscribe from this list: send the line "unsubscribe linux-metag" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html