Linux Audio

Check our new training course

Loading...
v4.6
  1/*
  2 * Arasan Secure Digital Host Controller Interface.
  3 * Copyright (C) 2011 - 2012 Michal Simek <monstr@monstr.eu>
  4 * Copyright (c) 2012 Wind River Systems, Inc.
  5 * Copyright (C) 2013 Pengutronix e.K.
  6 * Copyright (C) 2013 Xilinx Inc.
  7 *
  8 * Based on sdhci-of-esdhc.c
  9 *
 10 * Copyright (c) 2007 Freescale Semiconductor, Inc.
 11 * Copyright (c) 2009 MontaVista Software, Inc.
 12 *
 13 * Authors: Xiaobo Xie <X.Xie@freescale.com>
 14 *	    Anton Vorontsov <avorontsov@ru.mvista.com>
 15 *
 16 * This program is free software; you can redistribute it and/or modify
 17 * it under the terms of the GNU General Public License as published by
 18 * the Free Software Foundation; either version 2 of the License, or (at
 19 * your option) any later version.
 20 */
 21
 22#include <linux/module.h>
 23#include <linux/of_device.h>
 24#include <linux/phy/phy.h>
 25#include "sdhci-pltfm.h"
 26
 27#define SDHCI_ARASAN_CLK_CTRL_OFFSET	0x2c
 28
 29#define CLK_CTRL_TIMEOUT_SHIFT		16
 30#define CLK_CTRL_TIMEOUT_MASK		(0xf << CLK_CTRL_TIMEOUT_SHIFT)
 31#define CLK_CTRL_TIMEOUT_MIN_EXP	13
 32
 33/**
 34 * struct sdhci_arasan_data
 35 * @clk_ahb:	Pointer to the AHB clock
 36 * @phy: Pointer to the generic phy
 37 */
 38struct sdhci_arasan_data {
 39	struct clk	*clk_ahb;
 40	struct phy	*phy;
 41};
 42
 43static unsigned int sdhci_arasan_get_timeout_clock(struct sdhci_host *host)
 44{
 45	u32 div;
 46	unsigned long freq;
 47	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
 48
 49	div = readl(host->ioaddr + SDHCI_ARASAN_CLK_CTRL_OFFSET);
 50	div = (div & CLK_CTRL_TIMEOUT_MASK) >> CLK_CTRL_TIMEOUT_SHIFT;
 51
 52	freq = clk_get_rate(pltfm_host->clk);
 53	freq /= 1 << (CLK_CTRL_TIMEOUT_MIN_EXP + div);
 54
 55	return freq;
 56}
 57
 58static struct sdhci_ops sdhci_arasan_ops = {
 59	.set_clock = sdhci_set_clock,
 60	.get_max_clock = sdhci_pltfm_clk_get_max_clock,
 61	.get_timeout_clock = sdhci_arasan_get_timeout_clock,
 62	.set_bus_width = sdhci_set_bus_width,
 63	.reset = sdhci_reset,
 64	.set_uhs_signaling = sdhci_set_uhs_signaling,
 65};
 66
 67static struct sdhci_pltfm_data sdhci_arasan_pdata = {
 68	.ops = &sdhci_arasan_ops,
 69	.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
 70	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
 71			SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
 72};
 73
 74#ifdef CONFIG_PM_SLEEP
 75/**
 76 * sdhci_arasan_suspend - Suspend method for the driver
 77 * @dev:	Address of the device structure
 78 * Returns 0 on success and error value on error
 79 *
 80 * Put the device in a low power state.
 81 */
 82static int sdhci_arasan_suspend(struct device *dev)
 83{
 84	struct platform_device *pdev = to_platform_device(dev);
 85	struct sdhci_host *host = platform_get_drvdata(pdev);
 86	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
 87	struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
 88	int ret;
 89
 90	ret = sdhci_suspend_host(host);
 91	if (ret)
 92		return ret;
 93
 94	if (!IS_ERR(sdhci_arasan->phy)) {
 95		ret = phy_power_off(sdhci_arasan->phy);
 96		if (ret) {
 97			dev_err(dev, "Cannot power off phy.\n");
 98			sdhci_resume_host(host);
 99			return ret;
100		}
101	}
102
103	clk_disable(pltfm_host->clk);
104	clk_disable(sdhci_arasan->clk_ahb);
105
106	return 0;
107}
108
109/**
110 * sdhci_arasan_resume - Resume method for the driver
111 * @dev:	Address of the device structure
112 * Returns 0 on success and error value on error
113 *
114 * Resume operation after suspend
115 */
116static int sdhci_arasan_resume(struct device *dev)
117{
118	struct platform_device *pdev = to_platform_device(dev);
119	struct sdhci_host *host = platform_get_drvdata(pdev);
120	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
121	struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
122	int ret;
123
124	ret = clk_enable(sdhci_arasan->clk_ahb);
125	if (ret) {
126		dev_err(dev, "Cannot enable AHB clock.\n");
127		return ret;
128	}
129
130	ret = clk_enable(pltfm_host->clk);
131	if (ret) {
132		dev_err(dev, "Cannot enable SD clock.\n");
 
133		return ret;
134	}
135
136	if (!IS_ERR(sdhci_arasan->phy)) {
137		ret = phy_power_on(sdhci_arasan->phy);
138		if (ret) {
139			dev_err(dev, "Cannot power on phy.\n");
140			return ret;
141		}
142	}
143
144	return sdhci_resume_host(host);
145}
146#endif /* ! CONFIG_PM_SLEEP */
147
148static SIMPLE_DEV_PM_OPS(sdhci_arasan_dev_pm_ops, sdhci_arasan_suspend,
149			 sdhci_arasan_resume);
150
151static int sdhci_arasan_probe(struct platform_device *pdev)
152{
153	int ret;
154	struct clk *clk_xin;
155	struct sdhci_host *host;
156	struct sdhci_pltfm_host *pltfm_host;
157	struct sdhci_arasan_data *sdhci_arasan;
158
159	host = sdhci_pltfm_init(pdev, &sdhci_arasan_pdata,
160				sizeof(*sdhci_arasan));
161	if (IS_ERR(host))
162		return PTR_ERR(host);
163
164	pltfm_host = sdhci_priv(host);
165	sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
166
167	sdhci_arasan->clk_ahb = devm_clk_get(&pdev->dev, "clk_ahb");
168	if (IS_ERR(sdhci_arasan->clk_ahb)) {
169		dev_err(&pdev->dev, "clk_ahb clock not found.\n");
170		ret = PTR_ERR(sdhci_arasan->clk_ahb);
171		goto err_pltfm_free;
172	}
173
174	clk_xin = devm_clk_get(&pdev->dev, "clk_xin");
175	if (IS_ERR(clk_xin)) {
176		dev_err(&pdev->dev, "clk_xin clock not found.\n");
177		ret = PTR_ERR(clk_xin);
178		goto err_pltfm_free;
179	}
180
181	ret = clk_prepare_enable(sdhci_arasan->clk_ahb);
182	if (ret) {
183		dev_err(&pdev->dev, "Unable to enable AHB clock.\n");
184		goto err_pltfm_free;
185	}
186
187	ret = clk_prepare_enable(clk_xin);
188	if (ret) {
189		dev_err(&pdev->dev, "Unable to enable SD clock.\n");
190		goto clk_dis_ahb;
191	}
192
193	sdhci_get_of_property(pdev);
194	pltfm_host->clk = clk_xin;
195
196	ret = mmc_of_parse(host->mmc);
197	if (ret) {
198		dev_err(&pdev->dev, "parsing dt failed (%u)\n", ret);
199		goto clk_disable_all;
200	}
201
202	sdhci_arasan->phy = ERR_PTR(-ENODEV);
203	if (of_device_is_compatible(pdev->dev.of_node,
204				    "arasan,sdhci-5.1")) {
205		sdhci_arasan->phy = devm_phy_get(&pdev->dev,
206						 "phy_arasan");
207		if (IS_ERR(sdhci_arasan->phy)) {
208			ret = PTR_ERR(sdhci_arasan->phy);
209			dev_err(&pdev->dev, "No phy for arasan,sdhci-5.1.\n");
210			goto clk_disable_all;
211		}
212
213		ret = phy_init(sdhci_arasan->phy);
214		if (ret < 0) {
215			dev_err(&pdev->dev, "phy_init err.\n");
216			goto clk_disable_all;
217		}
218
219		ret = phy_power_on(sdhci_arasan->phy);
220		if (ret < 0) {
221			dev_err(&pdev->dev, "phy_power_on err.\n");
222			goto err_phy_power;
223		}
224	}
225
226	ret = sdhci_add_host(host);
227	if (ret)
228		goto err_add_host;
 
 
229
230	return 0;
231
232err_add_host:
233	if (!IS_ERR(sdhci_arasan->phy))
234		phy_power_off(sdhci_arasan->phy);
235err_phy_power:
236	if (!IS_ERR(sdhci_arasan->phy))
237		phy_exit(sdhci_arasan->phy);
238clk_disable_all:
239	clk_disable_unprepare(clk_xin);
240clk_dis_ahb:
241	clk_disable_unprepare(sdhci_arasan->clk_ahb);
242err_pltfm_free:
243	sdhci_pltfm_free(pdev);
244	return ret;
245}
246
247static int sdhci_arasan_remove(struct platform_device *pdev)
248{
249	int ret;
250	struct sdhci_host *host = platform_get_drvdata(pdev);
251	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
252	struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
253	struct clk *clk_ahb = sdhci_arasan->clk_ahb;
254
255	if (!IS_ERR(sdhci_arasan->phy)) {
256		phy_power_off(sdhci_arasan->phy);
257		phy_exit(sdhci_arasan->phy);
258	}
259
260	ret = sdhci_pltfm_unregister(pdev);
261
262	clk_disable_unprepare(clk_ahb);
 
263
264	return ret;
265}
266
267static const struct of_device_id sdhci_arasan_of_match[] = {
268	{ .compatible = "arasan,sdhci-8.9a" },
269	{ .compatible = "arasan,sdhci-5.1" },
270	{ .compatible = "arasan,sdhci-4.9a" },
271	{ }
272};
273MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match);
274
275static struct platform_driver sdhci_arasan_driver = {
276	.driver = {
277		.name = "sdhci-arasan",
 
278		.of_match_table = sdhci_arasan_of_match,
279		.pm = &sdhci_arasan_dev_pm_ops,
280	},
281	.probe = sdhci_arasan_probe,
282	.remove = sdhci_arasan_remove,
283};
284
285module_platform_driver(sdhci_arasan_driver);
286
287MODULE_DESCRIPTION("Driver for the Arasan SDHCI Controller");
288MODULE_AUTHOR("Soeren Brinkmann <soren.brinkmann@xilinx.com>");
289MODULE_LICENSE("GPL");
v3.15
  1/*
  2 * Arasan Secure Digital Host Controller Interface.
  3 * Copyright (C) 2011 - 2012 Michal Simek <monstr@monstr.eu>
  4 * Copyright (c) 2012 Wind River Systems, Inc.
  5 * Copyright (C) 2013 Pengutronix e.K.
  6 * Copyright (C) 2013 Xilinx Inc.
  7 *
  8 * Based on sdhci-of-esdhc.c
  9 *
 10 * Copyright (c) 2007 Freescale Semiconductor, Inc.
 11 * Copyright (c) 2009 MontaVista Software, Inc.
 12 *
 13 * Authors: Xiaobo Xie <X.Xie@freescale.com>
 14 *	    Anton Vorontsov <avorontsov@ru.mvista.com>
 15 *
 16 * This program is free software; you can redistribute it and/or modify
 17 * it under the terms of the GNU General Public License as published by
 18 * the Free Software Foundation; either version 2 of the License, or (at
 19 * your option) any later version.
 20 */
 21
 22#include <linux/module.h>
 
 
 23#include "sdhci-pltfm.h"
 24
 25#define SDHCI_ARASAN_CLK_CTRL_OFFSET	0x2c
 26
 27#define CLK_CTRL_TIMEOUT_SHIFT		16
 28#define CLK_CTRL_TIMEOUT_MASK		(0xf << CLK_CTRL_TIMEOUT_SHIFT)
 29#define CLK_CTRL_TIMEOUT_MIN_EXP	13
 30
 31/**
 32 * struct sdhci_arasan_data
 33 * @clk_ahb:	Pointer to the AHB clock
 
 34 */
 35struct sdhci_arasan_data {
 36	struct clk	*clk_ahb;
 
 37};
 38
 39static unsigned int sdhci_arasan_get_timeout_clock(struct sdhci_host *host)
 40{
 41	u32 div;
 42	unsigned long freq;
 43	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
 44
 45	div = readl(host->ioaddr + SDHCI_ARASAN_CLK_CTRL_OFFSET);
 46	div = (div & CLK_CTRL_TIMEOUT_MASK) >> CLK_CTRL_TIMEOUT_SHIFT;
 47
 48	freq = clk_get_rate(pltfm_host->clk);
 49	freq /= 1 << (CLK_CTRL_TIMEOUT_MIN_EXP + div);
 50
 51	return freq;
 52}
 53
 54static struct sdhci_ops sdhci_arasan_ops = {
 
 55	.get_max_clock = sdhci_pltfm_clk_get_max_clock,
 56	.get_timeout_clock = sdhci_arasan_get_timeout_clock,
 
 
 
 57};
 58
 59static struct sdhci_pltfm_data sdhci_arasan_pdata = {
 60	.ops = &sdhci_arasan_ops,
 
 
 
 61};
 62
 63#ifdef CONFIG_PM_SLEEP
 64/**
 65 * sdhci_arasan_suspend - Suspend method for the driver
 66 * @dev:	Address of the device structure
 67 * Returns 0 on success and error value on error
 68 *
 69 * Put the device in a low power state.
 70 */
 71static int sdhci_arasan_suspend(struct device *dev)
 72{
 73	struct platform_device *pdev = to_platform_device(dev);
 74	struct sdhci_host *host = platform_get_drvdata(pdev);
 75	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
 76	struct sdhci_arasan_data *sdhci_arasan = pltfm_host->priv;
 77	int ret;
 78
 79	ret = sdhci_suspend_host(host);
 80	if (ret)
 81		return ret;
 82
 
 
 
 
 
 
 
 
 
 83	clk_disable(pltfm_host->clk);
 84	clk_disable(sdhci_arasan->clk_ahb);
 85
 86	return 0;
 87}
 88
 89/**
 90 * sdhci_arasan_resume - Resume method for the driver
 91 * @dev:	Address of the device structure
 92 * Returns 0 on success and error value on error
 93 *
 94 * Resume operation after suspend
 95 */
 96static int sdhci_arasan_resume(struct device *dev)
 97{
 98	struct platform_device *pdev = to_platform_device(dev);
 99	struct sdhci_host *host = platform_get_drvdata(pdev);
100	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
101	struct sdhci_arasan_data *sdhci_arasan = pltfm_host->priv;
102	int ret;
103
104	ret = clk_enable(sdhci_arasan->clk_ahb);
105	if (ret) {
106		dev_err(dev, "Cannot enable AHB clock.\n");
107		return ret;
108	}
109
110	ret = clk_enable(pltfm_host->clk);
111	if (ret) {
112		dev_err(dev, "Cannot enable SD clock.\n");
113		clk_disable(sdhci_arasan->clk_ahb);
114		return ret;
115	}
116
 
 
 
 
 
 
 
 
117	return sdhci_resume_host(host);
118}
119#endif /* ! CONFIG_PM_SLEEP */
120
121static SIMPLE_DEV_PM_OPS(sdhci_arasan_dev_pm_ops, sdhci_arasan_suspend,
122			 sdhci_arasan_resume);
123
124static int sdhci_arasan_probe(struct platform_device *pdev)
125{
126	int ret;
127	struct clk *clk_xin;
128	struct sdhci_host *host;
129	struct sdhci_pltfm_host *pltfm_host;
130	struct sdhci_arasan_data *sdhci_arasan;
131
132	sdhci_arasan = devm_kzalloc(&pdev->dev, sizeof(*sdhci_arasan),
133			GFP_KERNEL);
134	if (!sdhci_arasan)
135		return -ENOMEM;
 
 
 
136
137	sdhci_arasan->clk_ahb = devm_clk_get(&pdev->dev, "clk_ahb");
138	if (IS_ERR(sdhci_arasan->clk_ahb)) {
139		dev_err(&pdev->dev, "clk_ahb clock not found.\n");
140		return PTR_ERR(sdhci_arasan->clk_ahb);
 
141	}
142
143	clk_xin = devm_clk_get(&pdev->dev, "clk_xin");
144	if (IS_ERR(clk_xin)) {
145		dev_err(&pdev->dev, "clk_xin clock not found.\n");
146		return PTR_ERR(clk_xin);
 
147	}
148
149	ret = clk_prepare_enable(sdhci_arasan->clk_ahb);
150	if (ret) {
151		dev_err(&pdev->dev, "Unable to enable AHB clock.\n");
152		return ret;
153	}
154
155	ret = clk_prepare_enable(clk_xin);
156	if (ret) {
157		dev_err(&pdev->dev, "Unable to enable SD clock.\n");
158		goto clk_dis_ahb;
159	}
160
161	host = sdhci_pltfm_init(pdev, &sdhci_arasan_pdata, 0);
162	if (IS_ERR(host)) {
163		ret = PTR_ERR(host);
164		dev_err(&pdev->dev, "platform init failed (%u)\n", ret);
 
 
165		goto clk_disable_all;
166	}
167
168	sdhci_get_of_property(pdev);
169	pltfm_host = sdhci_priv(host);
170	pltfm_host->priv = sdhci_arasan;
171	pltfm_host->clk = clk_xin;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
172
173	ret = sdhci_add_host(host);
174	if (ret) {
175		dev_err(&pdev->dev, "platform register failed (%u)\n", ret);
176		goto err_pltfm_free;
177	}
178
179	return 0;
180
181err_pltfm_free:
182	sdhci_pltfm_free(pdev);
 
 
 
 
183clk_disable_all:
184	clk_disable_unprepare(clk_xin);
185clk_dis_ahb:
186	clk_disable_unprepare(sdhci_arasan->clk_ahb);
187
 
188	return ret;
189}
190
191static int sdhci_arasan_remove(struct platform_device *pdev)
192{
 
193	struct sdhci_host *host = platform_get_drvdata(pdev);
194	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
195	struct sdhci_arasan_data *sdhci_arasan = pltfm_host->priv;
 
 
 
 
 
 
 
 
196
197	clk_disable_unprepare(pltfm_host->clk);
198	clk_disable_unprepare(sdhci_arasan->clk_ahb);
199
200	return sdhci_pltfm_unregister(pdev);
201}
202
203static const struct of_device_id sdhci_arasan_of_match[] = {
204	{ .compatible = "arasan,sdhci-8.9a" },
 
 
205	{ }
206};
207MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match);
208
209static struct platform_driver sdhci_arasan_driver = {
210	.driver = {
211		.name = "sdhci-arasan",
212		.owner = THIS_MODULE,
213		.of_match_table = sdhci_arasan_of_match,
214		.pm = &sdhci_arasan_dev_pm_ops,
215	},
216	.probe = sdhci_arasan_probe,
217	.remove = sdhci_arasan_remove,
218};
219
220module_platform_driver(sdhci_arasan_driver);
221
222MODULE_DESCRIPTION("Driver for the Arasan SDHCI Controller");
223MODULE_AUTHOR("Soeren Brinkmann <soren.brinkmann@xilinx.com>");
224MODULE_LICENSE("GPL");