Commit | Line | Data |
---|---|---|
90c62bf0 | 1 | /* |
d02a900b | 2 | * linux/arch/arm/mach-omap2/hsmmc.c |
90c62bf0 TL |
3 | * |
4 | * Copyright (C) 2007-2008 Texas Instruments | |
5 | * Copyright (C) 2008 Nokia Corporation | |
6 | * Author: Texas Instruments | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License version 2 as | |
10 | * published by the Free Software Foundation. | |
11 | */ | |
db0fefc5 AH |
12 | #include <linux/kernel.h> |
13 | #include <linux/slab.h> | |
14 | #include <linux/string.h> | |
90c62bf0 | 15 | #include <linux/delay.h> |
826c71a0 | 16 | #include <linux/mmc/host.h> |
55143438 | 17 | #include <linux/platform_data/hsmmc-omap.h> |
4b25408f | 18 | |
e4c060db | 19 | #include "soc.h" |
25c7d49e | 20 | #include "omap_device.h" |
90c62bf0 | 21 | |
d02a900b | 22 | #include "hsmmc.h" |
4814ced5 | 23 | #include "control.h" |
90c62bf0 | 24 | |
502ad2a6 | 25 | #if IS_ENABLED(CONFIG_MMC_OMAP_HS) |
90c62bf0 TL |
26 | |
27 | static u16 control_pbias_offset; | |
28 | static u16 control_devconf1_offset; | |
29 | ||
30 | #define HSMMC_NAME_LEN 9 | |
31 | ||
d1589f09 | 32 | static int __init omap_hsmmc_pdata_init(struct omap2_hsmmc_info *c, |
55143438 | 33 | struct omap_hsmmc_platform_data *mmc) |
4621d5f8 KK |
34 | { |
35 | char *hc_name; | |
36 | ||
6396bb22 | 37 | hc_name = kzalloc(HSMMC_NAME_LEN + 1, GFP_KERNEL); |
4621d5f8 | 38 | if (!hc_name) { |
4621d5f8 KK |
39 | kfree(hc_name); |
40 | return -ENOMEM; | |
41 | } | |
42 | ||
ba64792f | 43 | snprintf(hc_name, (HSMMC_NAME_LEN + 1), "mmc%islot%i", c->mmc, 1); |
326119c9 | 44 | mmc->name = hc_name; |
326119c9 | 45 | mmc->caps = c->caps; |
b30e321b | 46 | mmc->reg_offset = 0; |
4621d5f8 | 47 | |
4621d5f8 KK |
48 | return 0; |
49 | } | |
50 | ||
97899e55 | 51 | static int omap_hsmmc_done; |
3b972bf0 TL |
52 | |
53 | void omap_hsmmc_late_init(struct omap2_hsmmc_info *c) | |
54 | { | |
55 | struct platform_device *pdev; | |
3b972bf0 TL |
56 | int res; |
57 | ||
20547dfd | 58 | if (omap_hsmmc_done) |
3b972bf0 TL |
59 | return; |
60 | ||
20547dfd | 61 | omap_hsmmc_done = 1; |
3b972bf0 TL |
62 | |
63 | for (; c->mmc; c++) { | |
3b972bf0 TL |
64 | pdev = c->pdev; |
65 | if (!pdev) | |
66 | continue; | |
3b972bf0 TL |
67 | res = omap_device_register(pdev); |
68 | if (res) | |
ba64792f | 69 | pr_err("Could not late init MMC\n"); |
3b972bf0 TL |
70 | } |
71 | } | |
72 | ||
4621d5f8 KK |
73 | #define MAX_OMAP_MMC_HWMOD_NAME_LEN 16 |
74 | ||
6028505c | 75 | static void __init omap_hsmmc_init_one(struct omap2_hsmmc_info *hsmmcinfo, |
3b972bf0 | 76 | int ctrl_nr) |
4621d5f8 KK |
77 | { |
78 | struct omap_hwmod *oh; | |
3b972bf0 TL |
79 | struct omap_hwmod *ohs[1]; |
80 | struct omap_device *od; | |
3528c58e | 81 | struct platform_device *pdev; |
4621d5f8 | 82 | char oh_name[MAX_OMAP_MMC_HWMOD_NAME_LEN]; |
55143438 AF |
83 | struct omap_hsmmc_platform_data *mmc_data; |
84 | struct omap_hsmmc_dev_attr *mmc_dev_attr; | |
4621d5f8 | 85 | char *name; |
3b972bf0 | 86 | int res; |
4621d5f8 | 87 | |
55143438 | 88 | mmc_data = kzalloc(sizeof(*mmc_data), GFP_KERNEL); |
1a61a2a5 | 89 | if (!mmc_data) |
3b972bf0 | 90 | return; |
4621d5f8 | 91 | |
3b972bf0 TL |
92 | res = omap_hsmmc_pdata_init(hsmmcinfo, mmc_data); |
93 | if (res < 0) | |
94 | goto free_mmc; | |
95 | ||
0005ae73 | 96 | name = "omap_hsmmc"; |
3b972bf0 | 97 | res = snprintf(oh_name, MAX_OMAP_MMC_HWMOD_NAME_LEN, |
4621d5f8 | 98 | "mmc%d", ctrl_nr); |
3b972bf0 | 99 | WARN(res >= MAX_OMAP_MMC_HWMOD_NAME_LEN, |
4621d5f8 | 100 | "String buffer overflow in MMC%d device setup\n", ctrl_nr); |
3b972bf0 | 101 | |
4621d5f8 KK |
102 | oh = omap_hwmod_lookup(oh_name); |
103 | if (!oh) { | |
104 | pr_err("Could not look up %s\n", oh_name); | |
3b972bf0 | 105 | goto free_name; |
4621d5f8 | 106 | } |
3b972bf0 | 107 | ohs[0] = oh; |
4621d5f8 KK |
108 | if (oh->dev_attr != NULL) { |
109 | mmc_dev_attr = oh->dev_attr; | |
110 | mmc_data->controller_flags = mmc_dev_attr->flags; | |
111 | } | |
112 | ||
3b972bf0 TL |
113 | pdev = platform_device_alloc(name, ctrl_nr - 1); |
114 | if (!pdev) { | |
115 | pr_err("Could not allocate pdev for %s\n", name); | |
116 | goto free_name; | |
4621d5f8 | 117 | } |
3b972bf0 TL |
118 | dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id); |
119 | ||
c1d1cd59 | 120 | od = omap_device_alloc(pdev, ohs, 1); |
64de3a00 | 121 | if (IS_ERR(od)) { |
3b972bf0 TL |
122 | pr_err("Could not allocate od for %s\n", name); |
123 | goto put_pdev; | |
124 | } | |
125 | ||
126 | res = platform_device_add_data(pdev, mmc_data, | |
55143438 | 127 | sizeof(struct omap_hsmmc_platform_data)); |
3b972bf0 TL |
128 | if (res) { |
129 | pr_err("Could not add pdata for %s\n", name); | |
130 | goto put_pdev; | |
131 | } | |
132 | ||
133 | hsmmcinfo->pdev = pdev; | |
134 | ||
3b972bf0 TL |
135 | res = omap_device_register(pdev); |
136 | if (res) { | |
137 | pr_err("Could not register od for %s\n", name); | |
138 | goto free_od; | |
139 | } | |
140 | ||
141 | goto free_mmc; | |
142 | ||
143 | free_od: | |
144 | omap_device_delete(od); | |
145 | ||
146 | put_pdev: | |
147 | platform_device_put(pdev); | |
148 | ||
149 | free_name: | |
326119c9 | 150 | kfree(mmc_data->name); |
4621d5f8 | 151 | |
3b972bf0 | 152 | free_mmc: |
4621d5f8 KK |
153 | kfree(mmc_data); |
154 | } | |
90c62bf0 | 155 | |
d1589f09 | 156 | void __init omap_hsmmc_init(struct omap2_hsmmc_info *controllers) |
90c62bf0 | 157 | { |
97899e55 TL |
158 | if (omap_hsmmc_done) |
159 | return; | |
160 | ||
161 | omap_hsmmc_done = 1; | |
162 | ||
b30e321b TL |
163 | if (cpu_is_omap2430()) { |
164 | control_pbias_offset = OMAP243X_CONTROL_PBIAS_LITE; | |
165 | control_devconf1_offset = OMAP243X_CONTROL_DEVCONF1; | |
90c62bf0 | 166 | } else { |
b30e321b TL |
167 | control_pbias_offset = OMAP343X_CONTROL_PBIAS_LITE; |
168 | control_devconf1_offset = OMAP343X_CONTROL_DEVCONF1; | |
90c62bf0 TL |
169 | } |
170 | ||
4621d5f8 | 171 | for (; controllers->mmc; controllers++) |
3b972bf0 | 172 | omap_hsmmc_init_one(controllers, controllers->mmc); |
01971f65 | 173 | |
90c62bf0 TL |
174 | } |
175 | ||
176 | #endif |