summaryrefslogtreecommitdiff
path: root/drivers/mmc/host/sdhci-pltfm.h
blob: c31aa94c74e5a9331619b0ca5f370e8138b84c38 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
/*
 * Copyright 2010 MontaVista Software, LLC.
 *
 * Author: Anton Vorontsov <avorontsov@ru.mvista.com>
 *
 * 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.
 */

#ifndef _DRIVERS_MMC_SDHCI_PLTFM_H
#define _DRIVERS_MMC_SDHCI_PLTFM_H

#include <linux/clk.h>
#include <linux/platform_device.h>
#include "sdhci.h"

struct sdhci_pltfm_data {
	const struct sdhci_ops *ops;
	unsigned int quirks;
	unsigned int quirks2;
};

struct sdhci_pltfm_host {
	struct clk *clk;
	void *priv; /* to handle quirks across io-accessor calls */

	/* migrate from sdhci_of_host */
	unsigned int clock;
	u16 xfer_mode_shadow;
	enum endian_mode {
		LITTLE_ENDIAN_MODE,
		BIG_ENDIAN_MODE,
	} endian_mode;

	unsigned long private[0] ____cacheline_aligned;
};

#ifdef CONFIG_MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER
/*
 * These accessors are designed for big endian hosts doing I/O to
 * little endian controllers incorporating a 32-bit hardware byte swapper.
 */
static inline void sdhci_clrsetbits(struct sdhci_host *host, u32 mask,
				    u32 val, int reg)
{
	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
	void __iomem *base = host->ioaddr + (reg & ~0x3);
	u32 shift = (reg & 0x3) * 8;

	if (pltfm_host->endian_mode == BIG_ENDIAN_MODE)
		iowrite32be(((ioread32be(base) & ~(mask << shift)) |
			    (val << shift)), base);
	else
		iowrite32(((ioread32(base) & ~(mask << shift)) |
			  (val << shift)), base);
}

static inline u32 sdhci_32bs_readl(struct sdhci_host *host, int reg)
{
	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);

	if (pltfm_host->endian_mode == BIG_ENDIAN_MODE)
		return ioread32be(host->ioaddr + reg);
	else
		return ioread32(host->ioaddr + reg);
}

static inline u16 sdhci_32bs_readw(struct sdhci_host *host, int reg)
{
	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);

	if (pltfm_host->endian_mode == BIG_ENDIAN_MODE)
		return ioread16be(host->ioaddr + (reg ^ 0x2));
	else
		return ioread16(host->ioaddr + (reg ^ 0x2));
}

static inline u8 sdhci_32bs_readb(struct sdhci_host *host, int reg)
{
	return ioread8(host->ioaddr + (reg ^ 0x3));
}

static inline void sdhci_32bs_writel(struct sdhci_host *host,
		u32 val, int reg)
{
	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);

	if (pltfm_host->endian_mode == BIG_ENDIAN_MODE)
		iowrite32be(val, host->ioaddr + reg);
	else
		iowrite32(val, host->ioaddr + reg);
}

static inline void sdhci_32bs_writew(struct sdhci_host *host,
				       u16 val, int reg)
{
	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);

	switch (reg) {
	case SDHCI_TRANSFER_MODE:
		/*
		 * Postpone this write, we must do it together with a
		 * command write that is down below.
		 */
		pltfm_host->xfer_mode_shadow = val;
		return;
	case SDHCI_COMMAND:
		if (pltfm_host->endian_mode == BIG_ENDIAN_MODE)
			iowrite32be(val << 16 | pltfm_host->xfer_mode_shadow,
				    host->ioaddr + SDHCI_TRANSFER_MODE);
		else
			iowrite32(val << 16 | pltfm_host->xfer_mode_shadow,
				  host->ioaddr + SDHCI_TRANSFER_MODE);
		return;
	}
	sdhci_clrsetbits(host, 0xffff, val, reg);
}

static inline void sdhci_32bs_writeb(struct sdhci_host *host, u8 val, int reg)
{
	sdhci_clrsetbits(host, 0xff, val, reg);
}
#endif /* CONFIG_MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER */

extern void sdhci_get_of_property(struct platform_device *pdev);

extern struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev,
					  const struct sdhci_pltfm_data *pdata,
					  size_t priv_size);
extern void sdhci_pltfm_free(struct platform_device *pdev);

extern int sdhci_pltfm_register(struct platform_device *pdev,
				const struct sdhci_pltfm_data *pdata,
				size_t priv_size);
extern int sdhci_pltfm_unregister(struct platform_device *pdev);

extern unsigned int sdhci_pltfm_clk_get_max_clock(struct sdhci_host *host);

static inline void *sdhci_pltfm_priv(struct sdhci_pltfm_host *host)
{
	return (void *)host->private;
}

#ifdef CONFIG_PM
extern const struct dev_pm_ops sdhci_pltfm_pmops;
#define SDHCI_PLTFM_PMOPS (&sdhci_pltfm_pmops)
#else
#define SDHCI_PLTFM_PMOPS NULL
#endif

#endif /* _DRIVERS_MMC_SDHCI_PLTFM_H */