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