diff options
Diffstat (limited to 'arch/arm/plat-pxa/ssp.c')
-rw-r--r-- | arch/arm/plat-pxa/ssp.c | 171 |
1 files changed, 114 insertions, 57 deletions
diff --git a/arch/arm/plat-pxa/ssp.c b/arch/arm/plat-pxa/ssp.c index 8e11e96eab5e..c83f27b6bdda 100644 --- a/arch/arm/plat-pxa/ssp.c +++ b/arch/arm/plat-pxa/ssp.c | |||
@@ -30,6 +30,8 @@ | |||
30 | #include <linux/platform_device.h> | 30 | #include <linux/platform_device.h> |
31 | #include <linux/spi/pxa2xx_spi.h> | 31 | #include <linux/spi/pxa2xx_spi.h> |
32 | #include <linux/io.h> | 32 | #include <linux/io.h> |
33 | #include <linux/of.h> | ||
34 | #include <linux/of_device.h> | ||
33 | 35 | ||
34 | #include <asm/irq.h> | 36 | #include <asm/irq.h> |
35 | #include <mach/hardware.h> | 37 | #include <mach/hardware.h> |
@@ -60,6 +62,30 @@ struct ssp_device *pxa_ssp_request(int port, const char *label) | |||
60 | } | 62 | } |
61 | EXPORT_SYMBOL(pxa_ssp_request); | 63 | EXPORT_SYMBOL(pxa_ssp_request); |
62 | 64 | ||
65 | struct ssp_device *pxa_ssp_request_of(const struct device_node *of_node, | ||
66 | const char *label) | ||
67 | { | ||
68 | struct ssp_device *ssp = NULL; | ||
69 | |||
70 | mutex_lock(&ssp_lock); | ||
71 | |||
72 | list_for_each_entry(ssp, &ssp_list, node) { | ||
73 | if (ssp->of_node == of_node && ssp->use_count == 0) { | ||
74 | ssp->use_count++; | ||
75 | ssp->label = label; | ||
76 | break; | ||
77 | } | ||
78 | } | ||
79 | |||
80 | mutex_unlock(&ssp_lock); | ||
81 | |||
82 | if (&ssp->node == &ssp_list) | ||
83 | return NULL; | ||
84 | |||
85 | return ssp; | ||
86 | } | ||
87 | EXPORT_SYMBOL(pxa_ssp_request_of); | ||
88 | |||
63 | void pxa_ssp_free(struct ssp_device *ssp) | 89 | void pxa_ssp_free(struct ssp_device *ssp) |
64 | { | 90 | { |
65 | mutex_lock(&ssp_lock); | 91 | mutex_lock(&ssp_lock); |
@@ -72,96 +98,126 @@ void pxa_ssp_free(struct ssp_device *ssp) | |||
72 | } | 98 | } |
73 | EXPORT_SYMBOL(pxa_ssp_free); | 99 | EXPORT_SYMBOL(pxa_ssp_free); |
74 | 100 | ||
101 | #ifdef CONFIG_OF | ||
102 | static const struct of_device_id pxa_ssp_of_ids[] = { | ||
103 | { .compatible = "mrvl,pxa25x-ssp", .data = (void *) PXA25x_SSP }, | ||
104 | { .compatible = "mvrl,pxa25x-nssp", .data = (void *) PXA25x_NSSP }, | ||
105 | { .compatible = "mrvl,pxa27x-ssp", .data = (void *) PXA27x_SSP }, | ||
106 | { .compatible = "mrvl,pxa3xx-ssp", .data = (void *) PXA3xx_SSP }, | ||
107 | { .compatible = "mvrl,pxa168-ssp", .data = (void *) PXA168_SSP }, | ||
108 | { .compatible = "mrvl,pxa910-ssp", .data = (void *) PXA910_SSP }, | ||
109 | { .compatible = "mrvl,ce4100-ssp", .data = (void *) CE4100_SSP }, | ||
110 | { .compatible = "mrvl,lpss-ssp", .data = (void *) LPSS_SSP }, | ||
111 | { }, | ||
112 | }; | ||
113 | MODULE_DEVICE_TABLE(of, pxa_ssp_of_ids); | ||
114 | #endif | ||
115 | |||
75 | static int pxa_ssp_probe(struct platform_device *pdev) | 116 | static int pxa_ssp_probe(struct platform_device *pdev) |
76 | { | 117 | { |
77 | const struct platform_device_id *id = platform_get_device_id(pdev); | ||
78 | struct resource *res; | 118 | struct resource *res; |
79 | struct ssp_device *ssp; | 119 | struct ssp_device *ssp; |
80 | int ret = 0; | 120 | struct device *dev = &pdev->dev; |
81 | 121 | ||
82 | ssp = kzalloc(sizeof(struct ssp_device), GFP_KERNEL); | 122 | ssp = devm_kzalloc(dev, sizeof(struct ssp_device), GFP_KERNEL); |
83 | if (ssp == NULL) { | 123 | if (ssp == NULL) |
84 | dev_err(&pdev->dev, "failed to allocate memory"); | ||
85 | return -ENOMEM; | 124 | return -ENOMEM; |
86 | } | ||
87 | ssp->pdev = pdev; | ||
88 | 125 | ||
89 | ssp->clk = clk_get(&pdev->dev, NULL); | 126 | ssp->pdev = pdev; |
90 | if (IS_ERR(ssp->clk)) { | ||
91 | ret = PTR_ERR(ssp->clk); | ||
92 | goto err_free; | ||
93 | } | ||
94 | 127 | ||
95 | res = platform_get_resource(pdev, IORESOURCE_DMA, 0); | 128 | ssp->clk = devm_clk_get(dev, NULL); |
96 | if (res == NULL) { | 129 | if (IS_ERR(ssp->clk)) |
97 | dev_err(&pdev->dev, "no SSP RX DRCMR defined\n"); | 130 | return PTR_ERR(ssp->clk); |
98 | ret = -ENODEV; | 131 | |
99 | goto err_free_clk; | 132 | if (dev->of_node) { |
100 | } | 133 | struct of_phandle_args dma_spec; |
101 | ssp->drcmr_rx = res->start; | 134 | struct device_node *np = dev->of_node; |
135 | |||
136 | /* | ||
137 | * FIXME: we should allocate the DMA channel from this | ||
138 | * context and pass the channel down to the ssp users. | ||
139 | * For now, we lookup the rx and tx indices manually | ||
140 | */ | ||
141 | |||
142 | /* rx */ | ||
143 | of_parse_phandle_with_args(np, "dmas", "#dma-cells", | ||
144 | 0, &dma_spec); | ||
145 | ssp->drcmr_rx = dma_spec.args[0]; | ||
146 | of_node_put(dma_spec.np); | ||
147 | |||
148 | /* tx */ | ||
149 | of_parse_phandle_with_args(np, "dmas", "#dma-cells", | ||
150 | 1, &dma_spec); | ||
151 | ssp->drcmr_tx = dma_spec.args[0]; | ||
152 | of_node_put(dma_spec.np); | ||
153 | } else { | ||
154 | res = platform_get_resource(pdev, IORESOURCE_DMA, 0); | ||
155 | if (res == NULL) { | ||
156 | dev_err(dev, "no SSP RX DRCMR defined\n"); | ||
157 | return -ENODEV; | ||
158 | } | ||
159 | ssp->drcmr_rx = res->start; | ||
102 | 160 | ||
103 | res = platform_get_resource(pdev, IORESOURCE_DMA, 1); | 161 | res = platform_get_resource(pdev, IORESOURCE_DMA, 1); |
104 | if (res == NULL) { | 162 | if (res == NULL) { |
105 | dev_err(&pdev->dev, "no SSP TX DRCMR defined\n"); | 163 | dev_err(dev, "no SSP TX DRCMR defined\n"); |
106 | ret = -ENODEV; | 164 | return -ENODEV; |
107 | goto err_free_clk; | 165 | } |
166 | ssp->drcmr_tx = res->start; | ||
108 | } | 167 | } |
109 | ssp->drcmr_tx = res->start; | ||
110 | 168 | ||
111 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 169 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
112 | if (res == NULL) { | 170 | if (res == NULL) { |
113 | dev_err(&pdev->dev, "no memory resource defined\n"); | 171 | dev_err(dev, "no memory resource defined\n"); |
114 | ret = -ENODEV; | 172 | return -ENODEV; |
115 | goto err_free_clk; | ||
116 | } | 173 | } |
117 | 174 | ||
118 | res = request_mem_region(res->start, resource_size(res), | 175 | res = devm_request_mem_region(dev, res->start, resource_size(res), |
119 | pdev->name); | 176 | pdev->name); |
120 | if (res == NULL) { | 177 | if (res == NULL) { |
121 | dev_err(&pdev->dev, "failed to request memory resource\n"); | 178 | dev_err(dev, "failed to request memory resource\n"); |
122 | ret = -EBUSY; | 179 | return -EBUSY; |
123 | goto err_free_clk; | ||
124 | } | 180 | } |
125 | 181 | ||
126 | ssp->phys_base = res->start; | 182 | ssp->phys_base = res->start; |
127 | 183 | ||
128 | ssp->mmio_base = ioremap(res->start, resource_size(res)); | 184 | ssp->mmio_base = devm_ioremap(dev, res->start, resource_size(res)); |
129 | if (ssp->mmio_base == NULL) { | 185 | if (ssp->mmio_base == NULL) { |
130 | dev_err(&pdev->dev, "failed to ioremap() registers\n"); | 186 | dev_err(dev, "failed to ioremap() registers\n"); |
131 | ret = -ENODEV; | 187 | return -ENODEV; |
132 | goto err_free_mem; | ||
133 | } | 188 | } |
134 | 189 | ||
135 | ssp->irq = platform_get_irq(pdev, 0); | 190 | ssp->irq = platform_get_irq(pdev, 0); |
136 | if (ssp->irq < 0) { | 191 | if (ssp->irq < 0) { |
137 | dev_err(&pdev->dev, "no IRQ resource defined\n"); | 192 | dev_err(dev, "no IRQ resource defined\n"); |
138 | ret = -ENODEV; | 193 | return -ENODEV; |
139 | goto err_free_io; | 194 | } |
195 | |||
196 | if (dev->of_node) { | ||
197 | const struct of_device_id *id = | ||
198 | of_match_device(of_match_ptr(pxa_ssp_of_ids), dev); | ||
199 | ssp->type = (int) id->data; | ||
200 | } else { | ||
201 | const struct platform_device_id *id = | ||
202 | platform_get_device_id(pdev); | ||
203 | ssp->type = (int) id->driver_data; | ||
204 | |||
205 | /* PXA2xx/3xx SSP ports starts from 1 and the internal pdev->id | ||
206 | * starts from 0, do a translation here | ||
207 | */ | ||
208 | ssp->port_id = pdev->id + 1; | ||
140 | } | 209 | } |
141 | 210 | ||
142 | /* PXA2xx/3xx SSP ports starts from 1 and the internal pdev->id | ||
143 | * starts from 0, do a translation here | ||
144 | */ | ||
145 | ssp->port_id = pdev->id + 1; | ||
146 | ssp->use_count = 0; | 211 | ssp->use_count = 0; |
147 | ssp->type = (int)id->driver_data; | 212 | ssp->of_node = dev->of_node; |
148 | 213 | ||
149 | mutex_lock(&ssp_lock); | 214 | mutex_lock(&ssp_lock); |
150 | list_add(&ssp->node, &ssp_list); | 215 | list_add(&ssp->node, &ssp_list); |
151 | mutex_unlock(&ssp_lock); | 216 | mutex_unlock(&ssp_lock); |
152 | 217 | ||
153 | platform_set_drvdata(pdev, ssp); | 218 | platform_set_drvdata(pdev, ssp); |
154 | return 0; | ||
155 | 219 | ||
156 | err_free_io: | 220 | return 0; |
157 | iounmap(ssp->mmio_base); | ||
158 | err_free_mem: | ||
159 | release_mem_region(res->start, resource_size(res)); | ||
160 | err_free_clk: | ||
161 | clk_put(ssp->clk); | ||
162 | err_free: | ||
163 | kfree(ssp); | ||
164 | return ret; | ||
165 | } | 221 | } |
166 | 222 | ||
167 | static int pxa_ssp_remove(struct platform_device *pdev) | 223 | static int pxa_ssp_remove(struct platform_device *pdev) |
@@ -201,8 +257,9 @@ static struct platform_driver pxa_ssp_driver = { | |||
201 | .probe = pxa_ssp_probe, | 257 | .probe = pxa_ssp_probe, |
202 | .remove = pxa_ssp_remove, | 258 | .remove = pxa_ssp_remove, |
203 | .driver = { | 259 | .driver = { |
204 | .owner = THIS_MODULE, | 260 | .owner = THIS_MODULE, |
205 | .name = "pxa2xx-ssp", | 261 | .name = "pxa2xx-ssp", |
262 | .of_match_table = of_match_ptr(pxa_ssp_of_ids), | ||
206 | }, | 263 | }, |
207 | .id_table = ssp_id_table, | 264 | .id_table = ssp_id_table, |
208 | }; | 265 | }; |