From patchwork Fri Sep 16 14:36:27 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Fabio Estevam X-Patchwork-Id: 9336099 X-Patchwork-Delegate: sboyd@codeaurora.org Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 45B2D60839 for ; Fri, 16 Sep 2016 15:05:45 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 3520D29FF8 for ; Fri, 16 Sep 2016 15:05:45 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 29BA929FFF; Fri, 16 Sep 2016 15:05:45 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id BA5A72A002 for ; Fri, 16 Sep 2016 15:05:43 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755654AbcIPPFn (ORCPT ); Fri, 16 Sep 2016 11:05:43 -0400 Received: from mail-cys01nam02on0051.outbound.protection.outlook.com ([104.47.37.51]:44640 "EHLO NAM02-CY1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1754330AbcIPPFk (ORCPT ); Fri, 16 Sep 2016 11:05:40 -0400 Received: from BY2PR03CA056.namprd03.prod.outlook.com (10.141.249.29) by BLUPR0301MB2002.namprd03.prod.outlook.com (10.164.22.16) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384_P384) id 15.1.619.10; Fri, 16 Sep 2016 14:32:50 +0000 Received: from BN1BFFO11FD004.protection.gbl (2a01:111:f400:7c10::1:184) by BY2PR03CA056.outlook.office365.com (2a01:111:e400:2c5d::29) with Microsoft SMTP Server (version=TLS1_0, cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA_P384) id 15.1.629.8 via Frontend Transport; Fri, 16 Sep 2016 14:32:49 +0000 Authentication-Results: spf=fail (sender IP is 192.88.168.50) smtp.mailfrom=nxp.com; nxp.com; dkim=none (message not signed) header.d=none; nxp.com; dmarc=fail action=none header.from=nxp.com; nxp.com; dkim=none (message not signed) header.d=none; Received-SPF: Fail (protection.outlook.com: domain of nxp.com does not designate 192.88.168.50 as permitted sender) receiver=protection.outlook.com; client-ip=192.88.168.50; helo=tx30smr01.am.freescale.net; Received: from tx30smr01.am.freescale.net (192.88.168.50) by BN1BFFO11FD004.mail.protection.outlook.com (10.58.144.67) with Microsoft SMTP Server (version=TLS1_0, cipher=TLS_RSA_WITH_AES_256_CBC_SHA) id 15.1.619.6 via Frontend Transport; Fri, 16 Sep 2016 14:32:48 +0000 Received: from fabio-OptiPlex-7010.am.freescale.net (fabio-OptiPlex-7010.am.freescale.net [10.29.244.178]) by tx30smr01.am.freescale.net (8.14.3/8.14.0) with ESMTP id u8GEWeFr026994; Fri, 16 Sep 2016 07:32:45 -0700 From: Fabio Estevam To: CC: , , , , , , , , , "Fabio Estevam" , Ranjani Vaidyanathan Subject: [PATCH 3/3] clk: imx6: Fix procedure to switch the parent of LDB_DI_CLK Date: Fri, 16 Sep 2016 11:36:27 -0300 Message-ID: <1474036587-13819-3-git-send-email-fabio.estevam@nxp.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1474036587-13819-1-git-send-email-fabio.estevam@nxp.com> References: <1474036587-13819-1-git-send-email-fabio.estevam@nxp.com> X-EOPAttributedMessage: 0 X-Matching-Connectors: 131185099684714612; (91ab9b29-cfa4-454e-5278-08d120cd25b8); () X-Forefront-Antispam-Report: CIP:192.88.168.50; IPV:NLI; CTRY:US; EFV:NLI; SFV:NSPM; SFS:(10009020)(6009001)(7916002)(2980300002)(1110001)(1109001)(339900001)(189002)(199003)(19580405001)(36756003)(85426001)(305945005)(7416002)(7846002)(97736004)(8676002)(81166006)(110136003)(189998001)(81156014)(19580395003)(87936001)(8666005)(5003940100001)(50466002)(586003)(2906002)(356003)(48376002)(4326007)(575784001)(47776003)(86362001)(77096005)(50986999)(76176999)(2950100001)(33646002)(15975445007)(8936002)(626004)(50226002)(68736007)(229853001)(2351001)(105606002)(92566002)(5660300001)(104016004)(106466001)(7059030); DIR:OUT; SFP:1101; SCL:1; SRVR:BLUPR0301MB2002; H:tx30smr01.am.freescale.net; FPR:; SPF:Fail; PTR:InfoDomainNonexistent; MX:1; A:1; LANG:en; X-Microsoft-Exchange-Diagnostics: 1; BN1BFFO11FD004; 1:EQV6FA2+SynnYPnyoRxeM/BJ0wBFvN4I2hUTU+HHhLL2Bk0aD87BRxtBp2MkdgObztb32nXuhnoNzNERBN54pViJTamfktACvb22jbc2tqyxPScVz1E1/3BU8xz2H71IUMmgVqx55Mys9YmiBuGUyupb5mbz3hHAoYJfdx0dACE/Nbuz4IjfCItIIaV3VJj/W+2pIXhAVgLnFl/Hxuc1Mp8SNoeyc2QEtEiMHZ1b0c1L0+MQdQVrS3/00kV6hHGltpTbQ6dwtQ2CxgSjFpsYaZTIWDZEI5QGI1gFr/pKltSjYz7awL1NLPS49hFRzvkccEq/r2Qy1kvyjooSKJjiOtF+v4bVRfFiqApGEta4N3bNAw6I0omwqKEScAoq81ZQXSaaDcT4svr9fs+R5G7kzhCCEIV54JR3OShLuHVoUnDk6bOcemPtW0TNIWZLmn8P2va+9ughT10roi8uaRCttvj8WtjK+jOBOG4ErWDzx9pRHzfRXUCcjYgHtTCQM3ItHnJGfgOb53BMTv9KQ4R2GR/Qsk/20rkLkZCFVhJ3nj4CzSg7//RaRaPc0rfK4NN0J90Nb2W15KGGg3UfXcSj/alSqwVYbytPArpoO3PLx13CZ3EMSdyw20qMhj4dpHCtbIZuaRpMb8sAOz32hC7V2aJMIwqoZQmAsa3M2V1jYwuzvudZXHN3VoN3kPZQTSycuSj+Th7+mlIsNjBOAL8nOUTWjRmE4b76u7zaWtQA2J8= MIME-Version: 1.0 X-MS-Office365-Filtering-Correlation-Id: e47a50fc-b037-47ea-55b2-08d3de3e54fa X-Microsoft-Exchange-Diagnostics: 1; BLUPR0301MB2002; 2:vRrm2iOjDrTZQmZ/zYQmwDdoeycbcNzn9mpUHy2vPdBIHHOCZwyc5+3yj/VfQKfZqmuBop6dGi40zxvE8X1ifKY1jMBHsYggSw7xe6YOdyrkaGtNZzs3pnNsCV5QmcN/W3XamS9Y+jMsH3b2zvmt75xLRnfZspm4UwHgM5DgZSvXK159CK6YSoE9/3gz//PC; 3:ke3vPScZYj//Z8yiTdxg8i8fNiUgPkP0hP7k9OQsHzEDVKrrFrMHHKlet+H/1WKX/hYP2ARJU+Lt5uUOVsDGmRRt2KnBc5pNVJtMhlv+WljFC0Cf3OpqjsYJW8ebR8ve4aWJJjUGGNyVciTrXjtMDeqybEf4IuuipY90gXRKtIaaVFk8ESMkDqg3I/t9xiTY81EqEqxgwYvYpo0iQpGHhJQWJJ8IhjEPd3zz6QE1sro= X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:;SRVR:BLUPR0301MB2002; X-Microsoft-Exchange-Diagnostics: 1; BLUPR0301MB2002; 25:cPCk3FOEKSA7MuBvsaTfKFcJPrnLPv3LiDaEAPQZHtTuE6606CZMNdCduXraJ7sJzesT2Mwv6gXVXNs4IbwZkJG+anYCib/lWMBd9r2BUvZUF5OPDDS8x+l0sJV2UkbeTjaQILSqJSGI0G1xxMUBeHHHOoc3mbsIu+eHwbeOQFzYGZePOjSJ5kog+UhpVZfjhfxAImrJjvgOU4Dl13YVVkY921UmAUMZ5NOK8YLA0Pne0Lhe+JcA3luHIaNmRDN97WEdYc6MqGBRTQq9QUry7FV4u5H+qfhwcOnSr+eTNFsCKxoa7oIameBdwb576M/S9BM527QJb+8gbVcHoSiWovu/j501zwILvWo92G7tzo1xOTTwYJWKjOZHGYG4TGhdqQfIdzrXnoqxlXN0kAzRM/SuFLCCuI6V7aZcWWE8TZnB42/F1gdTOnSyKNXzcrq+xtKq0wIOyVKJEbYi3EsnwcFW9X6ef4+SeIagAd7hSxlip3ZtphvuTB/rUtALLpHR26djrNytk3K/FuAr89UZ1vqyrqMURIF3CbCdoVrp1pTbOIjsSOscrSjcyBJ4y3XHD0/oyjho0SXirG3wrXgJlk6DW10Fly7HqP+OytyyBW5ImOpBvj7gkGc3FiVo5rzu2Jg1vpVrReypKw5PztkPfPnDDm4QcgAvuckCRW4531kUtDchAxivf2b2l/bCBUDzLGFavhO9cW/WtSSNuEFWP8FCDwETeYhg2rDHtEAuJ/k3OIZyKPDudGepbsF21p8VmmuEalRgGCCYiaP1DWH93XAIEZ6Ma23mi3m1GZ8tQytIuWteAUPZukulNPcP+IvJ X-Microsoft-Exchange-Diagnostics: 1; BLUPR0301MB2002; 31:Iw7aLvCBg50Eq/q9c58L3eIXqy1UuSnf3tpcQFa9h/are7bmB6/eT0FwMxSai9QL0Thn3nKrBfKgDJ/7sgFa5Vgphm3rQFKONwXVljJr81o7Choz6cmRuy0l6Vj8cCxolXQGxtd+uqEPTg4dZKL6WO2jbcUsJv+MKdjoE+MsgqdFQV+0DewYXkT1W4hgGme2OTEbOAdURzS1FVpnhWOrnoiQP3DxsY6V2KEibAB8iDg=; 4:ney2SgUu8NNJ1IHz3xfRxP8wu5r5B3LtHOy8EPPuCyEhsz9JTSv2E9mWMOqu2YKiO+RXOSNRqgpFiktYIHErJSw2SRXCtr04M0qoa5ngkSPBj7fJRQbPw7cl5d2irFT9Ws6yfG9gtnPhVs/hFUGzK186dWNM2eAb0ngTip7eOWZ7BIxqYSEqMBPSUpb+YOCu2dHx+6zj3u6sAgu//H9HQWVhW9pVc7Qm4i/1uO8i5PuzmN3gBwZwr02iEXGM4PTyTJxqa5T5Z7QvVVAwY25Xmtqw3nb7utBFj0e7ROFXRRlWE9ana8DBJrmYieD+5xS98nisr9NjLRRMM2ceaCSZO9chE+b7nB7dbNA2hb4mov2nXEGFSaWh+hKWVpKJGzFWaorv2KeWXaWjwdxLeN7K2d9Qp1i6Y5YBLSkWoKZRfc7ydLS3WJIwEXpI8ywS4xIqaJnt8qdzRUyDwQQ590PjSp/1vVFrjr3HaVDqnh16gtWABERQeBGDOKLAh+YZ3pZ4G/KpRhxA+vmLygRdxIjlbHHukIuexcDFidbx4OWVqNKuj0DGfM30Z5KPhAuZhvaq X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:(185117386973197)(264314650089876); X-Exchange-Antispam-Report-CFA-Test: BCL:0; PCL:0; RULEID:(6040176)(601004)(2401047)(13015025)(13018025)(8121501046)(5005006)(13017025)(13023025)(13024025)(10201501046)(3002001)(6055026); SRVR:BLUPR0301MB2002; BCL:0; PCL:0; RULEID:(400006); SRVR:BLUPR0301MB2002; X-Forefront-PRVS: 0067A8BA2A X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1; BLUPR0301MB2002; 23:0hv/fqRVD+1E72UDOZH5iCfTrefi3i/cfv/gGHS?= =?us-ascii?Q?gxz7xZBYaF/IQXmVXp1q0HlKuEwOdbJrUaMGElD+PC/puAa3uedmBAG5ccTN?= =?us-ascii?Q?7YuVwZ88HLzH68sM/zgQ14Kn3zPElGtxmiJcoG2FfP/zGiKJEaRNHclFKnLH?= =?us-ascii?Q?tY7LQu/oRDh8mW5SsgFYXTF8R9dq3UqqLNV7HnuLyHG6knqVHe8bsuXUrDOo?= =?us-ascii?Q?ZLTUzU+XekqbpQUwEtjWIJLORfeTk5uDjtTtFF8tJlQTFW/E/8VxdrMTXKph?= =?us-ascii?Q?iUs+d0/uCVAw7NX8q02jEWvq9qgGntIYMx3yXq8sYYVO9rDOVc/XuaixIfQs?= =?us-ascii?Q?NGEHNlw7g1tdp/u+qyiDvHrB5ZWiw51GnRUwGo8kjwoqEXp7p8YyM+ES6Kix?= =?us-ascii?Q?zKYIYWUkPExE/IfCd5hcE226MGs8tuULoked5DU73TvWLr72BtPHsd9G5lHG?= =?us-ascii?Q?KrW8dFAfeO42MmbOiJp5EcWQMmHbr8PoBnYHHaefRX2HEtfeLmwOGnwjOFQ/?= =?us-ascii?Q?iCk6LnNgjsUujSN4RZbA3FdMHz98pwnndFMw/sYYluRa67aX4UuC4LhfgoZX?= =?us-ascii?Q?a9xmQgFE/zc4iyUZdiJ8Vq1MIRcn+MFwRZiK+3NMRUq1YCcRG4vkxAOw58Ct?= =?us-ascii?Q?yhpRtw1lZkr8mgGy62G2M99C+lSvwXzUr30jxm0J06bc1gXe92obxqWH3dY0?= =?us-ascii?Q?SPtKNSauiSL4RVk78SCgdVThHigWekrXD0lJtG6eGeEL7fM3PNGpOo6cmOaW?= =?us-ascii?Q?tJ9bq1wSPuTZ6J3JyRtB6MjgNhStZS2qGjVZZRzYSyaT75eTnC3R/fZmnrY5?= =?us-ascii?Q?0L8VB92JO0zWpq5sYbS+1aruEK+J/8KvDKqMJXziOjyM/RfTTde1xVQcLlbd?= =?us-ascii?Q?8sdaLhAJgY2u48+mUvNI9fwWa4exsmFh9mMU6+NFy0gE+KzS7L9/io1UsPyT?= =?us-ascii?Q?YKpqZ5sUwXNWnL51Sw+YX3GjI+RDnAyqZWzZSZ/dXW9C7pfNFNLbg6CQJQeh?= =?us-ascii?Q?fNlHYqh/hKNdkGi4nrLuuT4VGypBjS/HxfSZf/FWqEK8qM5JDRPyq7BdDKBf?= =?us-ascii?Q?Et/2dn1H1sCGA5kVPzVnd1QJQ7jI9aNxGuozPjJbn/mBgKwjVgItnT/Oydv7?= =?us-ascii?Q?/84TRr6Qr36viWMg2lGIDmHKxb5/7kdHx6Aifl1CLLfuEPi5zWt+AFB5m7qD?= =?us-ascii?Q?yy/7+AOYIHQkf3I1x1B+X4vQH6FaXuQt+mzwmsyWgYFKvZ7nteSGDS8NBDg?= =?us-ascii?Q?=3D=3D?= X-Microsoft-Exchange-Diagnostics: 1; BLUPR0301MB2002; 6:k0ET0jW1QYqsRW73+Y7/BgFD32tCaaDH1YuniFWCfA9pWmvLRpcflzbEdZmwIk928ifCWBPya1i+dAy9ioQm+oM4KpdoLrlrnX4ggH7QYc8s2WB5Xcb+8mgrMkavrG0yN+yZwdLB9ALzDgk66d6F14tFtQz7P8hzuyTY9SoYONbB4jQBbeGTmaVIknF4QBA4GJNmHr7NhaLBT8qvtGnBo73hBI5uQ0YLYXSGHkw8TqO3Jh+CRl1ctUXf3y+zyZKUg6aMPSEORdeQ+qlIqtH5mZGukCqkajsRDGHtoYSZlNw=; 5:gjfYEjxZlPHsg4ZAcCdUS6B3Ndw4xhv7ctYx5A5qqVyCE/xW+MI2gZvGca4P+vEjunCXQl4LkCuA/DOEaBuyWtnqk5l2yeQ006wenwaj6zfQlH2WGcrpbsCHQJblRMh5dKgwNpDinaL0Zd/FmJeI77wOdPWrbqltSShvtxgqhVY=; 24:vYieWKGiarl7OiSMfCxxB0+UsF/QQSpAc+eVX/u7Ciy8vyXVi8Da/omIWfGu3rIpgEV3ktoRrZZCrWB6lvIT2qJ+WmTIrPCR79k7Fl9NUrg=; 7:bbCiPzd+LPtXO8G6g3kaPx6tk53vjW16PiZnXFk1vQYkhb6+jNLrkOR3w9Qta3LgdCPXdaKQBUPU10dm8PCeSzd8Kq0T3oKUL773ZErTPz0SktKJEJ3OXMDYizhNegHtq7bRzMLN9HY0riyYHCRoPr7jih+Ugb28foF2xhZh9b3+GR5rc2yPPjLD/GYxqWC8c9KLH9X6Jm7y2GixRsMl1SNLBg3He0Dj2EwjcgWUKg4JQOo2pLiR0g9Lc1MLJwDd SpamDiagnosticOutput: 1:99 SpamDiagnosticMetadata: NSPM X-MS-Exchange-CrossTenant-OriginalArrivalTime: 16 Sep 2016 14:32:48.0034 (UTC) X-MS-Exchange-CrossTenant-Id: 5afe0b00-7697-4969-b663-5eab37d5f47e X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=5afe0b00-7697-4969-b663-5eab37d5f47e; Ip=[192.88.168.50]; Helo=[tx30smr01.am.freescale.net] X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: BLUPR0301MB2002 Sender: linux-clk-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-clk@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Due to incorrect placement of the clock gate cell in the ldb_di[x]_clk tree, the glitchy parent mux of ldb_di[x]_clk can cause a glitch to enter the ldb_di_ipu_div divider. If the divider gets locked up, no ldb_di[x]_clk is generated, and the LVDS display will hang when the ipu_di_clk is sourced from ldb_di_clk. To fix the problem, both the new and current parent of the ldb_di_clk should be disabled before the switch. This patch ensures that correct steps are followed when ldb_di_clk parent is switched in the beginning of boot. The glitchy muxes are then registered as read-only. The clock parent can be selected using the assigned-clocks and assigned-clock-parents properties of the ccm device tree node: &clks { assigned-clocks = <&clks IMX6QDL_CLK_LDB_DI0_SEL>, <&clks IMX6QDL_CLK_LDB_DI1_SEL>; assigned-clock-parents = <&clks IMX6QDL_CLK_MMDC_CH1_AXI>, <&clks IMX6QDL_CLK_PLL5_VIDEO_DIV>; }; The issue is explained in detail in EB821 ("LDB Clock Switch Procedure & i.MX6 Asynchronous Clock Switching Guidelines") [1]. [1] http://www.nxp.com/files/32bit/doc/eng_bulletin/EB821.pdf Signed-off-by: Ranjani Vaidyanathan Signed-off-by: Fabio Estevam Signed-off-by: Philipp Zabel Reviewed-by: Akshay Bhat Tested-by Joshua Clayton Tested-by: Charles Kang Acked-by: Shawn Guo --- drivers/clk/imx/clk-imx6q.c | 264 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 259 insertions(+), 5 deletions(-) diff --git a/drivers/clk/imx/clk-imx6q.c b/drivers/clk/imx/clk-imx6q.c index 9bbc994..fc3ed87 100644 --- a/drivers/clk/imx/clk-imx6q.c +++ b/drivers/clk/imx/clk-imx6q.c @@ -156,9 +156,90 @@ static struct clk ** const uart_clks[] __initconst = { NULL }; +static int ldb_di_sel_by_clock_id(int clock_id) +{ + switch (clock_id) { + case IMX6QDL_CLK_PLL5_VIDEO_DIV: + if (clk_on_imx6q() && + imx_get_soc_revision() == IMX_CHIP_REVISION_1_0) + return -ENOENT; + return 0; + case IMX6QDL_CLK_PLL2_PFD0_352M: + return 1; + case IMX6QDL_CLK_PLL2_PFD2_396M: + return 2; + case IMX6QDL_CLK_MMDC_CH1_AXI: + return 3; + case IMX6QDL_CLK_PLL3_USB_OTG: + return 4; + default: + return -ENOENT; + } +} + +static void of_assigned_ldb_sels(struct device_node *node, + unsigned int *ldb_di0_sel, + unsigned int *ldb_di1_sel) +{ + struct of_phandle_args clkspec; + int index, rc, num_parents; + int parent, child, sel; + + num_parents = of_count_phandle_with_args(node, "assigned-clock-parents", + "#clock-cells"); + for (index = 0; index < num_parents; index++) { + rc = of_parse_phandle_with_args(node, "assigned-clock-parents", + "#clock-cells", index, &clkspec); + if (rc < 0) { + /* skip empty (null) phandles */ + if (rc == -ENOENT) + continue; + else + return; + } + if (clkspec.np != node || clkspec.args[0] >= IMX6QDL_CLK_END) { + pr_err("ccm: parent clock %d not in ccm\n", index); + return; + } + parent = clkspec.args[0]; + + rc = of_parse_phandle_with_args(node, "assigned-clocks", + "#clock-cells", index, &clkspec); + if (rc < 0) + return; + if (clkspec.np != node || clkspec.args[0] >= IMX6QDL_CLK_END) { + pr_err("ccm: child clock %d not in ccm\n", index); + return; + } + child = clkspec.args[0]; + + if (child != IMX6QDL_CLK_LDB_DI0_SEL && + child != IMX6QDL_CLK_LDB_DI1_SEL) + continue; + + sel = ldb_di_sel_by_clock_id(parent); + if (sel < 0) { + pr_err("ccm: invalid ldb_di%d parent clock: %d\n", + child == IMX6QDL_CLK_LDB_DI1_SEL, parent); + continue; + } + + if (child == IMX6QDL_CLK_LDB_DI0_SEL) + *ldb_di0_sel = sel; + if (child == IMX6QDL_CLK_LDB_DI1_SEL) + *ldb_di1_sel = sel; + } +} + #define CCM_CCDR 0x04 +#define CCM_CCSR 0x0c +#define CCM_CS2CDR 0x2c + +#define CCDR_MMDC_CH1_MASK BIT(16) +#define CCSR_PLL3_SW_CLK_SEL BIT(0) -#define CCDR_MMDC_CH1_MASK BIT(16) +#define CS2CDR_LDB_DI0_CLK_SEL_SHIFT 9 +#define CS2CDR_LDB_DI1_CLK_SEL_SHIFT 12 static void __init imx6q_mmdc_ch1_mask_handshake(void __iomem *ccm_base) { @@ -169,10 +250,173 @@ static void __init imx6q_mmdc_ch1_mask_handshake(void __iomem *ccm_base) writel_relaxed(reg, ccm_base + CCM_CCDR); } +/* + * The only way to disable the MMDC_CH1 clock is to move it to pll3_sw_clk + * via periph2_clk2_sel and then to disable pll3_sw_clk by selecting the + * bypass clock source, since there is no CG bit for mmdc_ch1. + */ +static void mmdc_ch1_disable(void __iomem *ccm_base) +{ + unsigned int reg; + + clk_set_parent(clk[IMX6QDL_CLK_PERIPH2_CLK2_SEL], + clk[IMX6QDL_CLK_PLL3_USB_OTG]); + + /* + * Handshake with mmdc_ch1 module must be masked when changing + * periph2_clk_sel. + */ + clk_set_parent(clk[IMX6QDL_CLK_PERIPH2], clk[IMX6QDL_CLK_PERIPH2_CLK2]); + + /* Disable pll3_sw_clk by selecting the bypass clock source */ + reg = readl_relaxed(ccm_base + CCM_CCSR); + reg |= CCSR_PLL3_SW_CLK_SEL; + writel_relaxed(reg, ccm_base + CCM_CCSR); +} + +static void mmdc_ch1_reenable(void __iomem *ccm_base) +{ + unsigned int reg; + + /* Enable pll3_sw_clk by disabling the bypass */ + reg = readl_relaxed(ccm_base + CCM_CCSR); + reg &= ~CCSR_PLL3_SW_CLK_SEL; + writel_relaxed(reg, ccm_base + CCM_CCSR); + + clk_set_parent(clk[IMX6QDL_CLK_PERIPH2], clk[IMX6QDL_CLK_PERIPH2_PRE]); +} + +/* + * We have to follow a strict procedure when changing the LDB clock source, + * otherwise we risk introducing a glitch that can lock up the LDB divider. + * Things to keep in mind: + * + * 1. The current and new parent clock inputs to the mux must be disabled. + * 2. The default clock input for ldb_di0/1_clk_sel is mmdc_ch1_axi, which + * has no CG bit. + * 3. pll2_pfd2_396m can not be gated if it is used as memory clock. + * 4. In the RTL implementation of the LDB_DI_CLK_SEL muxes the top four + * options are in one mux and the PLL3 option along with three unused + * inputs is in a second mux. There is a third mux with two inputs used + * to decide between the first and second 4-port mux: + * + * pll5_video_div 0 --|\ + * pll2_pfd0_352m 1 --| |_ + * pll2_pfd2_396m 2 --| | `-|\ + * mmdc_ch1_axi 3 --|/ | | + * | |-- + * pll3_usb_otg 4 --|\ | | + * 5 --| |_,-|/ + * 6 --| | + * 7 --|/ + * + * The ldb_di0/1_clk_sel[1:0] bits control both 4-port muxes at the same time. + * The ldb_di0/1_clk_sel[2] bit controls the 2-port mux. The code below + * switches the parent to the bottom mux first and then manipulates the top + * mux to ensure that no glitch will enter the divider. + */ +static void init_ldb_clks(struct device_node *np, void __iomem *ccm_base) +{ + unsigned int reg; + unsigned int sel[2][4]; + int i; + + reg = readl_relaxed(ccm_base + CCM_CS2CDR); + sel[0][0] = (reg >> CS2CDR_LDB_DI0_CLK_SEL_SHIFT) & 7; + sel[1][0] = (reg >> CS2CDR_LDB_DI1_CLK_SEL_SHIFT) & 7; + + sel[0][3] = sel[0][2] = sel[0][1] = sel[0][0]; + sel[1][3] = sel[1][2] = sel[1][1] = sel[1][0]; + + of_assigned_ldb_sels(np, &sel[0][3], &sel[1][3]); + + for (i = 0; i < 2; i++) { + /* Warn if a glitch might have been introduced already */ + if (sel[i][0] != 3) { + pr_warn("ccm: ldb_di%d_sel already changed from reset value: %d\n", + i, sel[i][0]); + } + + if (sel[i][0] == sel[i][3]) + continue; + + /* Only switch to or from pll2_pfd2_396m if it is disabled */ + if ((sel[i][0] == 2 || sel[i][3] == 2) && + (clk_get_parent(clk[IMX6QDL_CLK_PERIPH_PRE]) == + clk[IMX6QDL_CLK_PLL2_PFD2_396M])) { + pr_err("ccm: ldb_di%d_sel: couldn't disable pll2_pfd2_396m\n", + i); + sel[i][3] = sel[i][2] = sel[i][1] = sel[i][0]; + continue; + } + + /* First switch to the bottom mux */ + sel[i][1] = sel[i][0] | 4; + + /* Then configure the top mux before switching back to it */ + sel[i][2] = sel[i][3] | 4; + + pr_debug("ccm: switching ldb_di%d_sel: %d->%d->%d->%d\n", i, + sel[i][0], sel[i][1], sel[i][2], sel[i][3]); + } + + if (sel[0][0] == sel[0][3] && sel[1][0] == sel[1][3]) + return; + + mmdc_ch1_disable(ccm_base); + + for (i = 1; i < 4; i++) { + reg = readl_relaxed(ccm_base + CCM_CS2CDR); + reg &= ~((7 << CS2CDR_LDB_DI0_CLK_SEL_SHIFT) | + (7 << CS2CDR_LDB_DI1_CLK_SEL_SHIFT)); + reg |= ((sel[0][i] << CS2CDR_LDB_DI0_CLK_SEL_SHIFT) | + (sel[1][i] << CS2CDR_LDB_DI1_CLK_SEL_SHIFT)); + writel_relaxed(reg, ccm_base + CCM_CS2CDR); + } + + mmdc_ch1_reenable(ccm_base); +} + +#define CCM_ANALOG_PLL_VIDEO 0xa0 +#define CCM_ANALOG_PFD_480 0xf0 +#define CCM_ANALOG_PFD_528 0x100 + +#define PLL_ENABLE BIT(13) + +#define PFD0_CLKGATE BIT(7) +#define PFD1_CLKGATE BIT(15) +#define PFD2_CLKGATE BIT(23) +#define PFD3_CLKGATE BIT(31) + +static void disable_anatop_clocks(void __iomem *anatop_base) +{ + unsigned int reg; + + /* Make sure PLL2 PFDs 0-2 are gated */ + reg = readl_relaxed(anatop_base + CCM_ANALOG_PFD_528); + /* Cannot gate PFD2 if pll2_pfd2_396m is the parent of MMDC clock */ + if (clk_get_parent(clk[IMX6QDL_CLK_PERIPH_PRE]) == + clk[IMX6QDL_CLK_PLL2_PFD2_396M]) + reg |= PFD0_CLKGATE | PFD1_CLKGATE; + else + reg |= PFD0_CLKGATE | PFD1_CLKGATE | PFD2_CLKGATE; + writel_relaxed(reg, anatop_base + CCM_ANALOG_PFD_528); + + /* Make sure PLL3 PFDs 0-3 are gated */ + reg = readl_relaxed(anatop_base + CCM_ANALOG_PFD_480); + reg |= PFD0_CLKGATE | PFD1_CLKGATE | PFD2_CLKGATE | PFD3_CLKGATE; + writel_relaxed(reg, anatop_base + CCM_ANALOG_PFD_480); + + /* Make sure PLL5 is disabled */ + reg = readl_relaxed(anatop_base + CCM_ANALOG_PLL_VIDEO); + reg &= ~PLL_ENABLE; + writel_relaxed(reg, anatop_base + CCM_ANALOG_PLL_VIDEO); +} + static void __init imx6q_clocks_init(struct device_node *ccm_node) { struct device_node *np; - void __iomem *base; + void __iomem *anatop_base, *base; int i; int ret; @@ -185,7 +429,7 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) clk[IMX6QDL_CLK_ANACLK2] = imx_obtain_fixed_clock("anaclk2", 0); np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-anatop"); - base = of_iomap(np, 0); + anatop_base = base = of_iomap(np, 0); WARN_ON(!base); /* Audio/video PLL post dividers do not work on i.MX6q revision 1.0 */ @@ -310,8 +554,6 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) base = of_iomap(np, 0); WARN_ON(!base); - imx6q_mmdc_ch1_mask_handshake(base); - /* name reg shift width parent_names num_parents */ clk[IMX6QDL_CLK_STEP] = imx_clk_mux("step", base + 0xc, 8, 1, step_sels, ARRAY_SIZE(step_sels)); clk[IMX6QDL_CLK_PLL1_SW] = imx_clk_mux("pll1_sw", base + 0xc, 2, 1, pll1_sw_sels, ARRAY_SIZE(pll1_sw_sels)); @@ -340,6 +582,18 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) clk[IMX6QDL_CLK_GPU3D_SHADER_SEL] = imx_clk_mux("gpu3d_shader_sel", base + 0x18, 8, 2, gpu3d_shader_sels, ARRAY_SIZE(gpu3d_shader_sels)); clk[IMX6QDL_CLK_IPU1_SEL] = imx_clk_mux("ipu1_sel", base + 0x3c, 9, 2, ipu_sels, ARRAY_SIZE(ipu_sels)); clk[IMX6QDL_CLK_IPU2_SEL] = imx_clk_mux("ipu2_sel", base + 0x3c, 14, 2, ipu_sels, ARRAY_SIZE(ipu_sels)); + + disable_anatop_clocks(anatop_base); + + imx6q_mmdc_ch1_mask_handshake(base); + + /* + * The LDB_DI0/1_SEL muxes are registered read-only due to a hardware + * bug. Set the muxes to the requested values before registering the + * ldb_di_sel clocks. + */ + init_ldb_clks(np, base); + clk[IMX6QDL_CLK_LDB_DI0_SEL] = imx_clk_mux_ldb("ldb_di0_sel", base + 0x2c, 9, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels)); clk[IMX6QDL_CLK_LDB_DI1_SEL] = imx_clk_mux_ldb("ldb_di1_sel", base + 0x2c, 12, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels)); clk[IMX6QDL_CLK_IPU1_DI0_PRE_SEL] = imx_clk_mux_flags("ipu1_di0_pre_sel", base + 0x34, 6, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT);