Blame view
drivers/mfd/t7l66xb.c
10.9 KB
1f192015c mfd: driver for t... |
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 |
/* * * Toshiba T7L66XB core mfd support * * Copyright (c) 2005, 2007, 2008 Ian Molton * Copyright (c) 2008 Dmitry Baryshkov * * 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. * * T7L66 features: * * Supported in this driver: * SD/MMC * SM/NAND flash controller * * As yet not supported * GPIO interface (on NAND pins) * Serial interface * TFT 'interface converter' * PCMCIA interface logic */ #include <linux/kernel.h> #include <linux/module.h> |
7acb706ca mfd: update TMIO ... |
27 |
#include <linux/err.h> |
1f192015c mfd: driver for t... |
28 |
#include <linux/io.h> |
5a0e3ad6a include cleanup: ... |
29 |
#include <linux/slab.h> |
1f192015c mfd: driver for t... |
30 |
#include <linux/irq.h> |
7acb706ca mfd: update TMIO ... |
31 |
#include <linux/clk.h> |
1f192015c mfd: driver for t... |
32 33 34 35 36 37 38 39 40 |
#include <linux/platform_device.h> #include <linux/mfd/core.h> #include <linux/mfd/tmio.h> #include <linux/mfd/t7l66xb.h> enum { T7L66XB_CELL_NAND, T7L66XB_CELL_MMC, }; |
64e8867ba mfd: tmio_mmc har... |
41 42 43 44 45 46 47 48 49 50 51 52 |
static const struct resource t7l66xb_mmc_resources[] = { { .start = 0x800, .end = 0x9ff, .flags = IORESOURCE_MEM, }, { .start = IRQ_T7L66XB_MMC, .end = IRQ_T7L66XB_MMC, .flags = IORESOURCE_IRQ, }, }; |
1f192015c mfd: driver for t... |
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
#define SCR_REVID 0x08 /* b Revision ID */ #define SCR_IMR 0x42 /* b Interrupt Mask */ #define SCR_DEV_CTL 0xe0 /* b Device control */ #define SCR_ISR 0xe1 /* b Interrupt Status */ #define SCR_GPO_OC 0xf0 /* b GPO output control */ #define SCR_GPO_OS 0xf1 /* b GPO output enable */ #define SCR_GPI_S 0xf2 /* w GPI status */ #define SCR_APDC 0xf8 /* b Active pullup down ctrl */ #define SCR_DEV_CTL_USB BIT(0) /* USB enable */ #define SCR_DEV_CTL_MMC BIT(1) /* MMC enable */ /*--------------------------------------------------------------------------*/ struct t7l66xb { void __iomem *scr; /* Lock to protect registers requiring read/modify/write ops. */ spinlock_t lock; struct resource rscr; |
7acb706ca mfd: update TMIO ... |
73 74 |
struct clk *clk48m; struct clk *clk32k; |
1f192015c mfd: driver for t... |
75 76 77 78 79 80 81 82 83 |
int irq; int irq_base; }; /*--------------------------------------------------------------------------*/ static int t7l66xb_mmc_enable(struct platform_device *mmc) { struct platform_device *dev = to_platform_device(mmc->dev.parent); |
1f192015c mfd: driver for t... |
84 85 86 |
struct t7l66xb *t7l66xb = platform_get_drvdata(dev); unsigned long flags; u8 dev_ctl; |
7acb706ca mfd: update TMIO ... |
87 |
clk_enable(t7l66xb->clk32k); |
1f192015c mfd: driver for t... |
88 89 90 91 92 93 94 95 |
spin_lock_irqsave(&t7l66xb->lock, flags); dev_ctl = tmio_ioread8(t7l66xb->scr + SCR_DEV_CTL); dev_ctl |= SCR_DEV_CTL_MMC; tmio_iowrite8(dev_ctl, t7l66xb->scr + SCR_DEV_CTL); spin_unlock_irqrestore(&t7l66xb->lock, flags); |
64e8867ba mfd: tmio_mmc har... |
96 97 |
tmio_core_mmc_enable(t7l66xb->scr + 0x200, 0, t7l66xb_mmc_resources[0].start & 0xfffe); |
1f192015c mfd: driver for t... |
98 99 100 101 102 103 |
return 0; } static int t7l66xb_mmc_disable(struct platform_device *mmc) { struct platform_device *dev = to_platform_device(mmc->dev.parent); |
1f192015c mfd: driver for t... |
104 105 106 107 108 109 110 111 112 113 114 |
struct t7l66xb *t7l66xb = platform_get_drvdata(dev); unsigned long flags; u8 dev_ctl; spin_lock_irqsave(&t7l66xb->lock, flags); dev_ctl = tmio_ioread8(t7l66xb->scr + SCR_DEV_CTL); dev_ctl &= ~SCR_DEV_CTL_MMC; tmio_iowrite8(dev_ctl, t7l66xb->scr + SCR_DEV_CTL); spin_unlock_irqrestore(&t7l66xb->lock, flags); |
7acb706ca mfd: update TMIO ... |
115 |
clk_disable(t7l66xb->clk32k); |
1f192015c mfd: driver for t... |
116 117 118 |
return 0; } |
64e8867ba mfd: tmio_mmc har... |
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
static void t7l66xb_mmc_pwr(struct platform_device *mmc, int state) { struct platform_device *dev = to_platform_device(mmc->dev.parent); struct t7l66xb *t7l66xb = platform_get_drvdata(dev); tmio_core_mmc_pwr(t7l66xb->scr + 0x200, 0, state); } static void t7l66xb_mmc_clk_div(struct platform_device *mmc, int state) { struct platform_device *dev = to_platform_device(mmc->dev.parent); struct t7l66xb *t7l66xb = platform_get_drvdata(dev); tmio_core_mmc_clk_div(t7l66xb->scr + 0x200, 0, state); } |
1f192015c mfd: driver for t... |
134 |
/*--------------------------------------------------------------------------*/ |
4d3792e05 mfd: fix tmio rel... |
135 |
static struct tmio_mmc_data t7166xb_mmc_data = { |
f0e46cc49 MFD,mmc: tmio_mmc... |
136 |
.hclk = 24000000, |
64e8867ba mfd: tmio_mmc har... |
137 138 |
.set_pwr = t7l66xb_mmc_pwr, .set_clk_div = t7l66xb_mmc_clk_div, |
1f192015c mfd: driver for t... |
139 |
}; |
3446d4bb9 mfd: Storage clas... |
140 |
static const struct resource t7l66xb_nand_resources[] = { |
1f192015c mfd: driver for t... |
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
{ .start = 0xc00, .end = 0xc07, .flags = IORESOURCE_MEM, }, { .start = 0x0100, .end = 0x01ff, .flags = IORESOURCE_MEM, }, { .start = IRQ_T7L66XB_NAND, .end = IRQ_T7L66XB_NAND, .flags = IORESOURCE_IRQ, }, }; static struct mfd_cell t7l66xb_cells[] = { [T7L66XB_CELL_MMC] = { .name = "tmio-mmc", .enable = t7l66xb_mmc_enable, .disable = t7l66xb_mmc_disable, |
ec71974f2 mmc: Use device p... |
163 164 |
.platform_data = &t7166xb_mmc_data, .pdata_size = sizeof(t7166xb_mmc_data), |
1f192015c mfd: driver for t... |
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
.num_resources = ARRAY_SIZE(t7l66xb_mmc_resources), .resources = t7l66xb_mmc_resources, }, [T7L66XB_CELL_NAND] = { .name = "tmio-nand", .num_resources = ARRAY_SIZE(t7l66xb_nand_resources), .resources = t7l66xb_nand_resources, }, }; /*--------------------------------------------------------------------------*/ /* Handle the T7L66XB interrupt mux */ static void t7l66xb_irq(unsigned int irq, struct irq_desc *desc) { |
d5bb12216 mfd: Cleanup irq ... |
180 |
struct t7l66xb *t7l66xb = irq_get_handler_data(irq); |
1f192015c mfd: driver for t... |
181 182 183 184 185 186 187 188 189 190 191 |
unsigned int isr; unsigned int i, irq_base; irq_base = t7l66xb->irq_base; while ((isr = tmio_ioread8(t7l66xb->scr + SCR_ISR) & ~tmio_ioread8(t7l66xb->scr + SCR_IMR))) for (i = 0; i < T7L66XB_NR_IRQS; i++) if (isr & (1 << i)) generic_handle_irq(irq_base + i); } |
a4e7feadc mfd: Convert t716... |
192 |
static void t7l66xb_irq_mask(struct irq_data *data) |
1f192015c mfd: driver for t... |
193 |
{ |
a4e7feadc mfd: Convert t716... |
194 |
struct t7l66xb *t7l66xb = irq_data_get_irq_chip_data(data); |
1f192015c mfd: driver for t... |
195 196 197 198 199 |
unsigned long flags; u8 imr; spin_lock_irqsave(&t7l66xb->lock, flags); imr = tmio_ioread8(t7l66xb->scr + SCR_IMR); |
a4e7feadc mfd: Convert t716... |
200 |
imr |= 1 << (data->irq - t7l66xb->irq_base); |
1f192015c mfd: driver for t... |
201 202 203 |
tmio_iowrite8(imr, t7l66xb->scr + SCR_IMR); spin_unlock_irqrestore(&t7l66xb->lock, flags); } |
a4e7feadc mfd: Convert t716... |
204 |
static void t7l66xb_irq_unmask(struct irq_data *data) |
1f192015c mfd: driver for t... |
205 |
{ |
a4e7feadc mfd: Convert t716... |
206 |
struct t7l66xb *t7l66xb = irq_data_get_irq_chip_data(data); |
1f192015c mfd: driver for t... |
207 208 209 210 211 |
unsigned long flags; u8 imr; spin_lock_irqsave(&t7l66xb->lock, flags); imr = tmio_ioread8(t7l66xb->scr + SCR_IMR); |
a4e7feadc mfd: Convert t716... |
212 |
imr &= ~(1 << (data->irq - t7l66xb->irq_base)); |
1f192015c mfd: driver for t... |
213 214 215 216 217 |
tmio_iowrite8(imr, t7l66xb->scr + SCR_IMR); spin_unlock_irqrestore(&t7l66xb->lock, flags); } static struct irq_chip t7l66xb_chip = { |
a4e7feadc mfd: Convert t716... |
218 219 220 221 |
.name = "t7l66xb", .irq_ack = t7l66xb_irq_mask, .irq_mask = t7l66xb_irq_mask, .irq_unmask = t7l66xb_irq_unmask, |
1f192015c mfd: driver for t... |
222 223 224 225 226 227 228 229 230 231 232 233 234 |
}; /*--------------------------------------------------------------------------*/ /* Install the IRQ handler */ static void t7l66xb_attach_irq(struct platform_device *dev) { struct t7l66xb *t7l66xb = platform_get_drvdata(dev); unsigned int irq, irq_base; irq_base = t7l66xb->irq_base; for (irq = irq_base; irq < irq_base + T7L66XB_NR_IRQS; irq++) { |
d6f7ce9f7 mfd: Fold irq_set... |
235 |
irq_set_chip_and_handler(irq, &t7l66xb_chip, handle_level_irq); |
d5bb12216 mfd: Cleanup irq ... |
236 |
irq_set_chip_data(irq, t7l66xb); |
1f192015c mfd: driver for t... |
237 238 239 240 |
#ifdef CONFIG_ARM set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); #endif } |
d5bb12216 mfd: Cleanup irq ... |
241 242 243 |
irq_set_irq_type(t7l66xb->irq, IRQ_TYPE_EDGE_FALLING); irq_set_handler_data(t7l66xb->irq, t7l66xb); irq_set_chained_handler(t7l66xb->irq, t7l66xb_irq); |
1f192015c mfd: driver for t... |
244 245 246 247 248 249 250 251 |
} static void t7l66xb_detach_irq(struct platform_device *dev) { struct t7l66xb *t7l66xb = platform_get_drvdata(dev); unsigned int irq, irq_base; irq_base = t7l66xb->irq_base; |
d5bb12216 mfd: Cleanup irq ... |
252 253 |
irq_set_chained_handler(t7l66xb->irq, NULL); irq_set_handler_data(t7l66xb->irq, NULL); |
1f192015c mfd: driver for t... |
254 255 256 257 258 |
for (irq = irq_base; irq < irq_base + T7L66XB_NR_IRQS; irq++) { #ifdef CONFIG_ARM set_irq_flags(irq, 0); #endif |
d5bb12216 mfd: Cleanup irq ... |
259 260 |
irq_set_chip(irq, NULL); irq_set_chip_data(irq, NULL); |
1f192015c mfd: driver for t... |
261 262 263 264 265 266 267 268 |
} } /*--------------------------------------------------------------------------*/ #ifdef CONFIG_PM static int t7l66xb_suspend(struct platform_device *dev, pm_message_t state) { |
7acb706ca mfd: update TMIO ... |
269 |
struct t7l66xb *t7l66xb = platform_get_drvdata(dev); |
1f192015c mfd: driver for t... |
270 271 272 273 |
struct t7l66xb_platform_data *pdata = dev->dev.platform_data; if (pdata && pdata->suspend) pdata->suspend(dev); |
7acb706ca mfd: update TMIO ... |
274 |
clk_disable(t7l66xb->clk48m); |
1f192015c mfd: driver for t... |
275 276 277 278 279 280 |
return 0; } static int t7l66xb_resume(struct platform_device *dev) { |
7acb706ca mfd: update TMIO ... |
281 |
struct t7l66xb *t7l66xb = platform_get_drvdata(dev); |
1f192015c mfd: driver for t... |
282 |
struct t7l66xb_platform_data *pdata = dev->dev.platform_data; |
7acb706ca mfd: update TMIO ... |
283 |
clk_enable(t7l66xb->clk48m); |
1f192015c mfd: driver for t... |
284 285 |
if (pdata && pdata->resume) pdata->resume(dev); |
64e8867ba mfd: tmio_mmc har... |
286 287 |
tmio_core_mmc_enable(t7l66xb->scr + 0x200, 0, t7l66xb_mmc_resources[0].start & 0xfffe); |
1f192015c mfd: driver for t... |
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 |
return 0; } #else #define t7l66xb_suspend NULL #define t7l66xb_resume NULL #endif /*--------------------------------------------------------------------------*/ static int t7l66xb_probe(struct platform_device *dev) { struct t7l66xb_platform_data *pdata = dev->dev.platform_data; struct t7l66xb *t7l66xb; struct resource *iomem, *rscr; int ret; |
9ad285d6d mfd: Check t7l66 ... |
303 304 |
if (pdata == NULL) return -EINVAL; |
1f192015c mfd: driver for t... |
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 |
iomem = platform_get_resource(dev, IORESOURCE_MEM, 0); if (!iomem) return -EINVAL; t7l66xb = kzalloc(sizeof *t7l66xb, GFP_KERNEL); if (!t7l66xb) return -ENOMEM; spin_lock_init(&t7l66xb->lock); platform_set_drvdata(dev, t7l66xb); ret = platform_get_irq(dev, 0); if (ret >= 0) t7l66xb->irq = ret; else goto err_noirq; t7l66xb->irq_base = pdata->irq_base; |
7acb706ca mfd: update TMIO ... |
324 325 326 327 328 329 330 331 332 |
t7l66xb->clk32k = clk_get(&dev->dev, "CLK_CK32K"); if (IS_ERR(t7l66xb->clk32k)) { ret = PTR_ERR(t7l66xb->clk32k); goto err_clk32k_get; } t7l66xb->clk48m = clk_get(&dev->dev, "CLK_CK48M"); if (IS_ERR(t7l66xb->clk48m)) { ret = PTR_ERR(t7l66xb->clk48m); |
7acb706ca mfd: update TMIO ... |
333 334 |
goto err_clk48m_get; } |
1f192015c mfd: driver for t... |
335 336 337 338 339 340 341 342 343 |
rscr = &t7l66xb->rscr; rscr->name = "t7l66xb-core"; rscr->start = iomem->start; rscr->end = iomem->start + 0xff; rscr->flags = IORESOURCE_MEM; ret = request_resource(iomem, rscr); if (ret) goto err_request_scr; |
c02e6a5ff mfd: Use resource... |
344 |
t7l66xb->scr = ioremap(rscr->start, resource_size(rscr)); |
1f192015c mfd: driver for t... |
345 346 347 348 |
if (!t7l66xb->scr) { ret = -ENOMEM; goto err_ioremap; } |
7acb706ca mfd: update TMIO ... |
349 |
clk_enable(t7l66xb->clk48m); |
1f192015c mfd: driver for t... |
350 351 352 353 354 355 356 357 358 359 360 361 |
if (pdata && pdata->enable) pdata->enable(dev); /* Mask all interrupts */ tmio_iowrite8(0xbf, t7l66xb->scr + SCR_IMR); printk(KERN_INFO "%s rev %d @ 0x%08lx, irq %d ", dev->name, tmio_ioread8(t7l66xb->scr + SCR_REVID), (unsigned long)iomem->start, t7l66xb->irq); t7l66xb_attach_irq(dev); |
7dc00a0d1 mtd: Use platform... |
362 363 |
t7l66xb_cells[T7L66XB_CELL_NAND].platform_data = pdata->nand_data; t7l66xb_cells[T7L66XB_CELL_NAND].pdata_size = sizeof(*pdata->nand_data); |
8a4fbe013 mfd: t7l66 MMC pl... |
364 |
|
56bf2bda0 mfd: Fix 7l66 and... |
365 366 367 |
ret = mfd_add_devices(&dev->dev, dev->id, t7l66xb_cells, ARRAY_SIZE(t7l66xb_cells), iomem, t7l66xb->irq_base); |
1f192015c mfd: driver for t... |
368 369 370 371 372 373 374 375 |
if (!ret) return 0; t7l66xb_detach_irq(dev); iounmap(t7l66xb->scr); err_ioremap: release_resource(&t7l66xb->rscr); |
1f192015c mfd: driver for t... |
376 |
err_request_scr: |
7acb706ca mfd: update TMIO ... |
377 378 379 380 381 |
clk_put(t7l66xb->clk48m); err_clk48m_get: clk_put(t7l66xb->clk32k); err_clk32k_get: err_noirq: |
0e820ab60 mfd: Correct use ... |
382 |
kfree(t7l66xb); |
1f192015c mfd: driver for t... |
383 384 385 386 387 388 389 390 391 392 |
return ret; } static int t7l66xb_remove(struct platform_device *dev) { struct t7l66xb_platform_data *pdata = dev->dev.platform_data; struct t7l66xb *t7l66xb = platform_get_drvdata(dev); int ret; ret = pdata->disable(dev); |
7acb706ca mfd: update TMIO ... |
393 394 |
clk_disable(t7l66xb->clk48m); clk_put(t7l66xb->clk48m); |
d2d272a96 mfd: Properly fre... |
395 396 |
clk_disable(t7l66xb->clk32k); clk_put(t7l66xb->clk32k); |
1f192015c mfd: driver for t... |
397 398 399 |
t7l66xb_detach_irq(dev); iounmap(t7l66xb->scr); release_resource(&t7l66xb->rscr); |
56bf2bda0 mfd: Fix 7l66 and... |
400 |
mfd_remove_devices(&dev->dev); |
1f192015c mfd: driver for t... |
401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 |
platform_set_drvdata(dev, NULL); kfree(t7l66xb); return ret; } static struct platform_driver t7l66xb_platform_driver = { .driver = { .name = "t7l66xb", .owner = THIS_MODULE, }, .suspend = t7l66xb_suspend, .resume = t7l66xb_resume, .probe = t7l66xb_probe, .remove = t7l66xb_remove, }; /*--------------------------------------------------------------------------*/ |
65349d60d mfd: Convert MFD ... |
420 |
module_platform_driver(t7l66xb_platform_driver); |
1f192015c mfd: driver for t... |
421 422 423 424 425 |
MODULE_DESCRIPTION("Toshiba T7L66XB core driver"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Ian Molton"); MODULE_ALIAS("platform:t7l66xb"); |