1*b6d6454fSBen Dooks /* linux/drivers/mfd/sm501.c 2*b6d6454fSBen Dooks * 3*b6d6454fSBen Dooks * Copyright (C) 2006 Simtec Electronics 4*b6d6454fSBen Dooks * Ben Dooks <ben@simtec.co.uk> 5*b6d6454fSBen Dooks * Vincent Sanders <vince@simtec.co.uk> 6*b6d6454fSBen Dooks * 7*b6d6454fSBen Dooks * This program is free software; you can redistribute it and/or modify 8*b6d6454fSBen Dooks * it under the terms of the GNU General Public License version 2 as 9*b6d6454fSBen Dooks * published by the Free Software Foundation. 10*b6d6454fSBen Dooks * 11*b6d6454fSBen Dooks * SM501 MFD driver 12*b6d6454fSBen Dooks */ 13*b6d6454fSBen Dooks 14*b6d6454fSBen Dooks #include <linux/kernel.h> 15*b6d6454fSBen Dooks #include <linux/module.h> 16*b6d6454fSBen Dooks #include <linux/delay.h> 17*b6d6454fSBen Dooks #include <linux/init.h> 18*b6d6454fSBen Dooks #include <linux/list.h> 19*b6d6454fSBen Dooks #include <linux/device.h> 20*b6d6454fSBen Dooks #include <linux/platform_device.h> 21*b6d6454fSBen Dooks #include <linux/pci.h> 22*b6d6454fSBen Dooks 23*b6d6454fSBen Dooks #include <linux/sm501.h> 24*b6d6454fSBen Dooks #include <linux/sm501-regs.h> 25*b6d6454fSBen Dooks 26*b6d6454fSBen Dooks #include <asm/io.h> 27*b6d6454fSBen Dooks 28*b6d6454fSBen Dooks struct sm501_device { 29*b6d6454fSBen Dooks struct list_head list; 30*b6d6454fSBen Dooks struct platform_device pdev; 31*b6d6454fSBen Dooks }; 32*b6d6454fSBen Dooks 33*b6d6454fSBen Dooks struct sm501_devdata { 34*b6d6454fSBen Dooks spinlock_t reg_lock; 35*b6d6454fSBen Dooks struct mutex clock_lock; 36*b6d6454fSBen Dooks struct list_head devices; 37*b6d6454fSBen Dooks 38*b6d6454fSBen Dooks struct device *dev; 39*b6d6454fSBen Dooks struct resource *io_res; 40*b6d6454fSBen Dooks struct resource *mem_res; 41*b6d6454fSBen Dooks struct resource *regs_claim; 42*b6d6454fSBen Dooks struct sm501_platdata *platdata; 43*b6d6454fSBen Dooks 44*b6d6454fSBen Dooks int unit_power[20]; 45*b6d6454fSBen Dooks unsigned int pdev_id; 46*b6d6454fSBen Dooks unsigned int irq; 47*b6d6454fSBen Dooks void __iomem *regs; 48*b6d6454fSBen Dooks }; 49*b6d6454fSBen Dooks 50*b6d6454fSBen Dooks #define MHZ (1000 * 1000) 51*b6d6454fSBen Dooks 52*b6d6454fSBen Dooks #ifdef DEBUG 53*b6d6454fSBen Dooks static const unsigned int misc_div[] = { 54*b6d6454fSBen Dooks [0] = 1, 55*b6d6454fSBen Dooks [1] = 2, 56*b6d6454fSBen Dooks [2] = 4, 57*b6d6454fSBen Dooks [3] = 8, 58*b6d6454fSBen Dooks [4] = 16, 59*b6d6454fSBen Dooks [5] = 32, 60*b6d6454fSBen Dooks [6] = 64, 61*b6d6454fSBen Dooks [7] = 128, 62*b6d6454fSBen Dooks [8] = 3, 63*b6d6454fSBen Dooks [9] = 6, 64*b6d6454fSBen Dooks [10] = 12, 65*b6d6454fSBen Dooks [11] = 24, 66*b6d6454fSBen Dooks [12] = 48, 67*b6d6454fSBen Dooks [13] = 96, 68*b6d6454fSBen Dooks [14] = 192, 69*b6d6454fSBen Dooks [15] = 384, 70*b6d6454fSBen Dooks }; 71*b6d6454fSBen Dooks 72*b6d6454fSBen Dooks static const unsigned int px_div[] = { 73*b6d6454fSBen Dooks [0] = 1, 74*b6d6454fSBen Dooks [1] = 2, 75*b6d6454fSBen Dooks [2] = 4, 76*b6d6454fSBen Dooks [3] = 8, 77*b6d6454fSBen Dooks [4] = 16, 78*b6d6454fSBen Dooks [5] = 32, 79*b6d6454fSBen Dooks [6] = 64, 80*b6d6454fSBen Dooks [7] = 128, 81*b6d6454fSBen Dooks [8] = 3, 82*b6d6454fSBen Dooks [9] = 6, 83*b6d6454fSBen Dooks [10] = 12, 84*b6d6454fSBen Dooks [11] = 24, 85*b6d6454fSBen Dooks [12] = 48, 86*b6d6454fSBen Dooks [13] = 96, 87*b6d6454fSBen Dooks [14] = 192, 88*b6d6454fSBen Dooks [15] = 384, 89*b6d6454fSBen Dooks [16] = 5, 90*b6d6454fSBen Dooks [17] = 10, 91*b6d6454fSBen Dooks [18] = 20, 92*b6d6454fSBen Dooks [19] = 40, 93*b6d6454fSBen Dooks [20] = 80, 94*b6d6454fSBen Dooks [21] = 160, 95*b6d6454fSBen Dooks [22] = 320, 96*b6d6454fSBen Dooks [23] = 604, 97*b6d6454fSBen Dooks }; 98*b6d6454fSBen Dooks 99*b6d6454fSBen Dooks static unsigned long decode_div(unsigned long pll2, unsigned long val, 100*b6d6454fSBen Dooks unsigned int lshft, unsigned int selbit, 101*b6d6454fSBen Dooks unsigned long mask, const unsigned int *dtab) 102*b6d6454fSBen Dooks { 103*b6d6454fSBen Dooks if (val & selbit) 104*b6d6454fSBen Dooks pll2 = 288 * MHZ; 105*b6d6454fSBen Dooks 106*b6d6454fSBen Dooks return pll2 / dtab[(val >> lshft) & mask]; 107*b6d6454fSBen Dooks } 108*b6d6454fSBen Dooks 109*b6d6454fSBen Dooks #define fmt_freq(x) ((x) / MHZ), ((x) % MHZ), (x) 110*b6d6454fSBen Dooks 111*b6d6454fSBen Dooks /* sm501_dump_clk 112*b6d6454fSBen Dooks * 113*b6d6454fSBen Dooks * Print out the current clock configuration for the device 114*b6d6454fSBen Dooks */ 115*b6d6454fSBen Dooks 116*b6d6454fSBen Dooks static void sm501_dump_clk(struct sm501_devdata *sm) 117*b6d6454fSBen Dooks { 118*b6d6454fSBen Dooks unsigned long misct = readl(sm->regs + SM501_MISC_TIMING); 119*b6d6454fSBen Dooks unsigned long pm0 = readl(sm->regs + SM501_POWER_MODE_0_CLOCK); 120*b6d6454fSBen Dooks unsigned long pm1 = readl(sm->regs + SM501_POWER_MODE_1_CLOCK); 121*b6d6454fSBen Dooks unsigned long pmc = readl(sm->regs + SM501_POWER_MODE_CONTROL); 122*b6d6454fSBen Dooks unsigned long sdclk0, sdclk1; 123*b6d6454fSBen Dooks unsigned long pll2 = 0; 124*b6d6454fSBen Dooks 125*b6d6454fSBen Dooks switch (misct & 0x30) { 126*b6d6454fSBen Dooks case 0x00: 127*b6d6454fSBen Dooks pll2 = 336 * MHZ; 128*b6d6454fSBen Dooks break; 129*b6d6454fSBen Dooks case 0x10: 130*b6d6454fSBen Dooks pll2 = 288 * MHZ; 131*b6d6454fSBen Dooks break; 132*b6d6454fSBen Dooks case 0x20: 133*b6d6454fSBen Dooks pll2 = 240 * MHZ; 134*b6d6454fSBen Dooks break; 135*b6d6454fSBen Dooks case 0x30: 136*b6d6454fSBen Dooks pll2 = 192 * MHZ; 137*b6d6454fSBen Dooks break; 138*b6d6454fSBen Dooks } 139*b6d6454fSBen Dooks 140*b6d6454fSBen Dooks sdclk0 = (misct & (1<<12)) ? pll2 : 288 * MHZ; 141*b6d6454fSBen Dooks sdclk0 /= misc_div[((misct >> 8) & 0xf)]; 142*b6d6454fSBen Dooks 143*b6d6454fSBen Dooks sdclk1 = (misct & (1<<20)) ? pll2 : 288 * MHZ; 144*b6d6454fSBen Dooks sdclk1 /= misc_div[((misct >> 16) & 0xf)]; 145*b6d6454fSBen Dooks 146*b6d6454fSBen Dooks dev_dbg(sm->dev, "MISCT=%08lx, PM0=%08lx, PM1=%08lx\n", 147*b6d6454fSBen Dooks misct, pm0, pm1); 148*b6d6454fSBen Dooks 149*b6d6454fSBen Dooks dev_dbg(sm->dev, "PLL2 = %ld.%ld MHz (%ld), SDCLK0=%08lx, SDCLK1=%08lx\n", 150*b6d6454fSBen Dooks fmt_freq(pll2), sdclk0, sdclk1); 151*b6d6454fSBen Dooks 152*b6d6454fSBen Dooks dev_dbg(sm->dev, "SDRAM: PM0=%ld, PM1=%ld\n", sdclk0, sdclk1); 153*b6d6454fSBen Dooks 154*b6d6454fSBen Dooks dev_dbg(sm->dev, "PM0[%c]: " 155*b6d6454fSBen Dooks "P2 %ld.%ld MHz (%ld), V2 %ld.%ld (%ld), " 156*b6d6454fSBen Dooks x "M %ld.%ld (%ld), MX1 %ld.%ld (%ld)\n", 157*b6d6454fSBen Dooks (pmc & 3 ) == 0 ? '*' : '-', 158*b6d6454fSBen Dooks fmt_freq(decode_div(pll2, pm0, 24, 1<<29, 31, px_div)), 159*b6d6454fSBen Dooks fmt_freq(decode_div(pll2, pm0, 16, 1<<20, 15, misc_div)), 160*b6d6454fSBen Dooks fmt_freq(decode_div(pll2, pm0, 8, 1<<12, 15, misc_div)), 161*b6d6454fSBen Dooks fmt_freq(decode_div(pll2, pm0, 0, 1<<4, 15, misc_div))); 162*b6d6454fSBen Dooks 163*b6d6454fSBen Dooks dev_dbg(sm->dev, "PM1[%c]: " 164*b6d6454fSBen Dooks "P2 %ld.%ld MHz (%ld), V2 %ld.%ld (%ld), " 165*b6d6454fSBen Dooks "M %ld.%ld (%ld), MX1 %ld.%ld (%ld)\n", 166*b6d6454fSBen Dooks (pmc & 3 ) == 1 ? '*' : '-', 167*b6d6454fSBen Dooks fmt_freq(decode_div(pll2, pm1, 24, 1<<29, 31, px_div)), 168*b6d6454fSBen Dooks fmt_freq(decode_div(pll2, pm1, 16, 1<<20, 15, misc_div)), 169*b6d6454fSBen Dooks fmt_freq(decode_div(pll2, pm1, 8, 1<<12, 15, misc_div)), 170*b6d6454fSBen Dooks fmt_freq(decode_div(pll2, pm1, 0, 1<<4, 15, misc_div))); 171*b6d6454fSBen Dooks } 172*b6d6454fSBen Dooks #else 173*b6d6454fSBen Dooks static void sm501_dump_clk(struct sm501_devdata *sm) 174*b6d6454fSBen Dooks { 175*b6d6454fSBen Dooks } 176*b6d6454fSBen Dooks #endif 177*b6d6454fSBen Dooks 178*b6d6454fSBen Dooks /* sm501_sync_regs 179*b6d6454fSBen Dooks * 180*b6d6454fSBen Dooks * ensure the 181*b6d6454fSBen Dooks */ 182*b6d6454fSBen Dooks 183*b6d6454fSBen Dooks static void sm501_sync_regs(struct sm501_devdata *sm) 184*b6d6454fSBen Dooks { 185*b6d6454fSBen Dooks readl(sm->regs); 186*b6d6454fSBen Dooks } 187*b6d6454fSBen Dooks 188*b6d6454fSBen Dooks /* sm501_misc_control 189*b6d6454fSBen Dooks * 190*b6d6454fSBen Dooks * alters the misceleneous control parameters 191*b6d6454fSBen Dooks */ 192*b6d6454fSBen Dooks 193*b6d6454fSBen Dooks int sm501_misc_control(struct device *dev, 194*b6d6454fSBen Dooks unsigned long set, unsigned long clear) 195*b6d6454fSBen Dooks { 196*b6d6454fSBen Dooks struct sm501_devdata *sm = dev_get_drvdata(dev); 197*b6d6454fSBen Dooks unsigned long misc; 198*b6d6454fSBen Dooks unsigned long save; 199*b6d6454fSBen Dooks unsigned long to; 200*b6d6454fSBen Dooks 201*b6d6454fSBen Dooks spin_lock_irqsave(&sm->reg_lock, save); 202*b6d6454fSBen Dooks 203*b6d6454fSBen Dooks misc = readl(sm->regs + SM501_MISC_CONTROL); 204*b6d6454fSBen Dooks to = (misc & ~clear) | set; 205*b6d6454fSBen Dooks 206*b6d6454fSBen Dooks if (to != misc) { 207*b6d6454fSBen Dooks writel(to, sm->regs + SM501_MISC_CONTROL); 208*b6d6454fSBen Dooks sm501_sync_regs(sm); 209*b6d6454fSBen Dooks 210*b6d6454fSBen Dooks dev_dbg(sm->dev, "MISC_CONTROL %08lx\n", misc); 211*b6d6454fSBen Dooks } 212*b6d6454fSBen Dooks 213*b6d6454fSBen Dooks spin_unlock_irqrestore(&sm->reg_lock, save); 214*b6d6454fSBen Dooks return to; 215*b6d6454fSBen Dooks } 216*b6d6454fSBen Dooks 217*b6d6454fSBen Dooks EXPORT_SYMBOL_GPL(sm501_misc_control); 218*b6d6454fSBen Dooks 219*b6d6454fSBen Dooks /* sm501_modify_reg 220*b6d6454fSBen Dooks * 221*b6d6454fSBen Dooks * Modify a register in the SM501 which may be shared with other 222*b6d6454fSBen Dooks * drivers. 223*b6d6454fSBen Dooks */ 224*b6d6454fSBen Dooks 225*b6d6454fSBen Dooks unsigned long sm501_modify_reg(struct device *dev, 226*b6d6454fSBen Dooks unsigned long reg, 227*b6d6454fSBen Dooks unsigned long set, 228*b6d6454fSBen Dooks unsigned long clear) 229*b6d6454fSBen Dooks { 230*b6d6454fSBen Dooks struct sm501_devdata *sm = dev_get_drvdata(dev); 231*b6d6454fSBen Dooks unsigned long data; 232*b6d6454fSBen Dooks unsigned long save; 233*b6d6454fSBen Dooks 234*b6d6454fSBen Dooks spin_lock_irqsave(&sm->reg_lock, save); 235*b6d6454fSBen Dooks 236*b6d6454fSBen Dooks data = readl(sm->regs + reg); 237*b6d6454fSBen Dooks data |= set; 238*b6d6454fSBen Dooks data &= ~clear; 239*b6d6454fSBen Dooks 240*b6d6454fSBen Dooks writel(data, sm->regs + reg); 241*b6d6454fSBen Dooks sm501_sync_regs(sm); 242*b6d6454fSBen Dooks 243*b6d6454fSBen Dooks spin_unlock_irqrestore(&sm->reg_lock, save); 244*b6d6454fSBen Dooks 245*b6d6454fSBen Dooks return data; 246*b6d6454fSBen Dooks } 247*b6d6454fSBen Dooks 248*b6d6454fSBen Dooks EXPORT_SYMBOL_GPL(sm501_modify_reg); 249*b6d6454fSBen Dooks 250*b6d6454fSBen Dooks unsigned long sm501_gpio_get(struct device *dev, 251*b6d6454fSBen Dooks unsigned long gpio) 252*b6d6454fSBen Dooks { 253*b6d6454fSBen Dooks struct sm501_devdata *sm = dev_get_drvdata(dev); 254*b6d6454fSBen Dooks unsigned long result; 255*b6d6454fSBen Dooks unsigned long reg; 256*b6d6454fSBen Dooks 257*b6d6454fSBen Dooks reg = (gpio > 32) ? SM501_GPIO_DATA_HIGH : SM501_GPIO_DATA_LOW; 258*b6d6454fSBen Dooks result = readl(sm->regs + reg); 259*b6d6454fSBen Dooks 260*b6d6454fSBen Dooks result >>= (gpio & 31); 261*b6d6454fSBen Dooks return result & 1UL; 262*b6d6454fSBen Dooks } 263*b6d6454fSBen Dooks 264*b6d6454fSBen Dooks EXPORT_SYMBOL_GPL(sm501_gpio_get); 265*b6d6454fSBen Dooks 266*b6d6454fSBen Dooks void sm501_gpio_set(struct device *dev, 267*b6d6454fSBen Dooks unsigned long gpio, 268*b6d6454fSBen Dooks unsigned int to, 269*b6d6454fSBen Dooks unsigned int dir) 270*b6d6454fSBen Dooks { 271*b6d6454fSBen Dooks struct sm501_devdata *sm = dev_get_drvdata(dev); 272*b6d6454fSBen Dooks 273*b6d6454fSBen Dooks unsigned long bit = 1 << (gpio & 31); 274*b6d6454fSBen Dooks unsigned long base; 275*b6d6454fSBen Dooks unsigned long save; 276*b6d6454fSBen Dooks unsigned long val; 277*b6d6454fSBen Dooks 278*b6d6454fSBen Dooks base = (gpio > 32) ? SM501_GPIO_DATA_HIGH : SM501_GPIO_DATA_LOW; 279*b6d6454fSBen Dooks base += SM501_GPIO; 280*b6d6454fSBen Dooks 281*b6d6454fSBen Dooks spin_lock_irqsave(&sm->reg_lock, save); 282*b6d6454fSBen Dooks 283*b6d6454fSBen Dooks val = readl(sm->regs + base) & ~bit; 284*b6d6454fSBen Dooks if (to) 285*b6d6454fSBen Dooks val |= bit; 286*b6d6454fSBen Dooks writel(val, sm->regs + base); 287*b6d6454fSBen Dooks 288*b6d6454fSBen Dooks val = readl(sm->regs + SM501_GPIO_DDR_LOW) & ~bit; 289*b6d6454fSBen Dooks if (dir) 290*b6d6454fSBen Dooks val |= bit; 291*b6d6454fSBen Dooks 292*b6d6454fSBen Dooks writel(val, sm->regs + SM501_GPIO_DDR_LOW); 293*b6d6454fSBen Dooks sm501_sync_regs(sm); 294*b6d6454fSBen Dooks 295*b6d6454fSBen Dooks spin_unlock_irqrestore(&sm->reg_lock, save); 296*b6d6454fSBen Dooks 297*b6d6454fSBen Dooks } 298*b6d6454fSBen Dooks 299*b6d6454fSBen Dooks EXPORT_SYMBOL_GPL(sm501_gpio_set); 300*b6d6454fSBen Dooks 301*b6d6454fSBen Dooks 302*b6d6454fSBen Dooks /* sm501_unit_power 303*b6d6454fSBen Dooks * 304*b6d6454fSBen Dooks * alters the power active gate to set specific units on or off 305*b6d6454fSBen Dooks */ 306*b6d6454fSBen Dooks 307*b6d6454fSBen Dooks int sm501_unit_power(struct device *dev, unsigned int unit, unsigned int to) 308*b6d6454fSBen Dooks { 309*b6d6454fSBen Dooks struct sm501_devdata *sm = dev_get_drvdata(dev); 310*b6d6454fSBen Dooks unsigned long mode; 311*b6d6454fSBen Dooks unsigned long gate; 312*b6d6454fSBen Dooks unsigned long clock; 313*b6d6454fSBen Dooks 314*b6d6454fSBen Dooks mutex_lock(&sm->clock_lock); 315*b6d6454fSBen Dooks 316*b6d6454fSBen Dooks mode = readl(sm->regs + SM501_POWER_MODE_CONTROL); 317*b6d6454fSBen Dooks gate = readl(sm->regs + SM501_CURRENT_GATE); 318*b6d6454fSBen Dooks clock = readl(sm->regs + SM501_CURRENT_CLOCK); 319*b6d6454fSBen Dooks 320*b6d6454fSBen Dooks mode &= 3; /* get current power mode */ 321*b6d6454fSBen Dooks 322*b6d6454fSBen Dooks if (unit > ARRAY_SIZE(sm->unit_power)) { 323*b6d6454fSBen Dooks dev_err(dev, "%s: bad unit %d\n", __FUNCTION__, unit); 324*b6d6454fSBen Dooks goto already; 325*b6d6454fSBen Dooks } 326*b6d6454fSBen Dooks 327*b6d6454fSBen Dooks dev_dbg(sm->dev, "%s: unit %d, cur %d, to %d\n", __FUNCTION__, unit, 328*b6d6454fSBen Dooks sm->unit_power[unit], to); 329*b6d6454fSBen Dooks 330*b6d6454fSBen Dooks if (to == 0 && sm->unit_power[unit] == 0) { 331*b6d6454fSBen Dooks dev_err(sm->dev, "unit %d is already shutdown\n", unit); 332*b6d6454fSBen Dooks goto already; 333*b6d6454fSBen Dooks } 334*b6d6454fSBen Dooks 335*b6d6454fSBen Dooks sm->unit_power[unit] += to ? 1 : -1; 336*b6d6454fSBen Dooks to = sm->unit_power[unit] ? 1 : 0; 337*b6d6454fSBen Dooks 338*b6d6454fSBen Dooks if (to) { 339*b6d6454fSBen Dooks if (gate & (1 << unit)) 340*b6d6454fSBen Dooks goto already; 341*b6d6454fSBen Dooks gate |= (1 << unit); 342*b6d6454fSBen Dooks } else { 343*b6d6454fSBen Dooks if (!(gate & (1 << unit))) 344*b6d6454fSBen Dooks goto already; 345*b6d6454fSBen Dooks gate &= ~(1 << unit); 346*b6d6454fSBen Dooks } 347*b6d6454fSBen Dooks 348*b6d6454fSBen Dooks switch (mode) { 349*b6d6454fSBen Dooks case 1: 350*b6d6454fSBen Dooks writel(gate, sm->regs + SM501_POWER_MODE_0_GATE); 351*b6d6454fSBen Dooks writel(clock, sm->regs + SM501_POWER_MODE_0_CLOCK); 352*b6d6454fSBen Dooks mode = 0; 353*b6d6454fSBen Dooks break; 354*b6d6454fSBen Dooks case 2: 355*b6d6454fSBen Dooks case 0: 356*b6d6454fSBen Dooks writel(gate, sm->regs + SM501_POWER_MODE_1_GATE); 357*b6d6454fSBen Dooks writel(clock, sm->regs + SM501_POWER_MODE_1_CLOCK); 358*b6d6454fSBen Dooks mode = 1; 359*b6d6454fSBen Dooks break; 360*b6d6454fSBen Dooks 361*b6d6454fSBen Dooks default: 362*b6d6454fSBen Dooks return -1; 363*b6d6454fSBen Dooks } 364*b6d6454fSBen Dooks 365*b6d6454fSBen Dooks writel(mode, sm->regs + SM501_POWER_MODE_CONTROL); 366*b6d6454fSBen Dooks sm501_sync_regs(sm); 367*b6d6454fSBen Dooks 368*b6d6454fSBen Dooks dev_dbg(sm->dev, "gate %08lx, clock %08lx, mode %08lx\n", 369*b6d6454fSBen Dooks gate, clock, mode); 370*b6d6454fSBen Dooks 371*b6d6454fSBen Dooks msleep(16); 372*b6d6454fSBen Dooks 373*b6d6454fSBen Dooks already: 374*b6d6454fSBen Dooks mutex_unlock(&sm->clock_lock); 375*b6d6454fSBen Dooks return gate; 376*b6d6454fSBen Dooks } 377*b6d6454fSBen Dooks 378*b6d6454fSBen Dooks EXPORT_SYMBOL_GPL(sm501_unit_power); 379*b6d6454fSBen Dooks 380*b6d6454fSBen Dooks 381*b6d6454fSBen Dooks /* Perform a rounded division. */ 382*b6d6454fSBen Dooks static long sm501fb_round_div(long num, long denom) 383*b6d6454fSBen Dooks { 384*b6d6454fSBen Dooks /* n / d + 1 / 2 = (2n + d) / 2d */ 385*b6d6454fSBen Dooks return (2 * num + denom) / (2 * denom); 386*b6d6454fSBen Dooks } 387*b6d6454fSBen Dooks 388*b6d6454fSBen Dooks /* clock value structure. */ 389*b6d6454fSBen Dooks struct sm501_clock { 390*b6d6454fSBen Dooks unsigned long mclk; 391*b6d6454fSBen Dooks int divider; 392*b6d6454fSBen Dooks int shift; 393*b6d6454fSBen Dooks }; 394*b6d6454fSBen Dooks 395*b6d6454fSBen Dooks /* sm501_select_clock 396*b6d6454fSBen Dooks * 397*b6d6454fSBen Dooks * selects nearest discrete clock frequency the SM501 can achive 398*b6d6454fSBen Dooks * the maximum divisor is 3 or 5 399*b6d6454fSBen Dooks */ 400*b6d6454fSBen Dooks static unsigned long sm501_select_clock(unsigned long freq, 401*b6d6454fSBen Dooks struct sm501_clock *clock, 402*b6d6454fSBen Dooks int max_div) 403*b6d6454fSBen Dooks { 404*b6d6454fSBen Dooks unsigned long mclk; 405*b6d6454fSBen Dooks int divider; 406*b6d6454fSBen Dooks int shift; 407*b6d6454fSBen Dooks long diff; 408*b6d6454fSBen Dooks long best_diff = 999999999; 409*b6d6454fSBen Dooks 410*b6d6454fSBen Dooks /* Try 288MHz and 336MHz clocks. */ 411*b6d6454fSBen Dooks for (mclk = 288000000; mclk <= 336000000; mclk += 48000000) { 412*b6d6454fSBen Dooks /* try dividers 1 and 3 for CRT and for panel, 413*b6d6454fSBen Dooks try divider 5 for panel only.*/ 414*b6d6454fSBen Dooks 415*b6d6454fSBen Dooks for (divider = 1; divider <= max_div; divider += 2) { 416*b6d6454fSBen Dooks /* try all 8 shift values.*/ 417*b6d6454fSBen Dooks for (shift = 0; shift < 8; shift++) { 418*b6d6454fSBen Dooks /* Calculate difference to requested clock */ 419*b6d6454fSBen Dooks diff = sm501fb_round_div(mclk, divider << shift) - freq; 420*b6d6454fSBen Dooks if (diff < 0) 421*b6d6454fSBen Dooks diff = -diff; 422*b6d6454fSBen Dooks 423*b6d6454fSBen Dooks /* If it is less than the current, use it */ 424*b6d6454fSBen Dooks if (diff < best_diff) { 425*b6d6454fSBen Dooks best_diff = diff; 426*b6d6454fSBen Dooks 427*b6d6454fSBen Dooks clock->mclk = mclk; 428*b6d6454fSBen Dooks clock->divider = divider; 429*b6d6454fSBen Dooks clock->shift = shift; 430*b6d6454fSBen Dooks } 431*b6d6454fSBen Dooks } 432*b6d6454fSBen Dooks } 433*b6d6454fSBen Dooks } 434*b6d6454fSBen Dooks 435*b6d6454fSBen Dooks /* Return best clock. */ 436*b6d6454fSBen Dooks return clock->mclk / (clock->divider << clock->shift); 437*b6d6454fSBen Dooks } 438*b6d6454fSBen Dooks 439*b6d6454fSBen Dooks /* sm501_set_clock 440*b6d6454fSBen Dooks * 441*b6d6454fSBen Dooks * set one of the four clock sources to the closest available frequency to 442*b6d6454fSBen Dooks * the one specified 443*b6d6454fSBen Dooks */ 444*b6d6454fSBen Dooks 445*b6d6454fSBen Dooks unsigned long sm501_set_clock(struct device *dev, 446*b6d6454fSBen Dooks int clksrc, 447*b6d6454fSBen Dooks unsigned long req_freq) 448*b6d6454fSBen Dooks { 449*b6d6454fSBen Dooks struct sm501_devdata *sm = dev_get_drvdata(dev); 450*b6d6454fSBen Dooks unsigned long mode = readl(sm->regs + SM501_POWER_MODE_CONTROL); 451*b6d6454fSBen Dooks unsigned long gate = readl(sm->regs + SM501_CURRENT_GATE); 452*b6d6454fSBen Dooks unsigned long clock = readl(sm->regs + SM501_CURRENT_CLOCK); 453*b6d6454fSBen Dooks unsigned char reg; 454*b6d6454fSBen Dooks unsigned long sm501_freq; /* the actual frequency acheived */ 455*b6d6454fSBen Dooks 456*b6d6454fSBen Dooks struct sm501_clock to; 457*b6d6454fSBen Dooks 458*b6d6454fSBen Dooks /* find achivable discrete frequency and setup register value 459*b6d6454fSBen Dooks * accordingly, V2XCLK, MCLK and M1XCLK are the same P2XCLK 460*b6d6454fSBen Dooks * has an extra bit for the divider */ 461*b6d6454fSBen Dooks 462*b6d6454fSBen Dooks switch (clksrc) { 463*b6d6454fSBen Dooks case SM501_CLOCK_P2XCLK: 464*b6d6454fSBen Dooks /* This clock is divided in half so to achive the 465*b6d6454fSBen Dooks * requested frequency the value must be multiplied by 466*b6d6454fSBen Dooks * 2. This clock also has an additional pre divisor */ 467*b6d6454fSBen Dooks 468*b6d6454fSBen Dooks sm501_freq = (sm501_select_clock(2 * req_freq, &to, 5) / 2); 469*b6d6454fSBen Dooks reg=to.shift & 0x07;/* bottom 3 bits are shift */ 470*b6d6454fSBen Dooks if (to.divider == 3) 471*b6d6454fSBen Dooks reg |= 0x08; /* /3 divider required */ 472*b6d6454fSBen Dooks else if (to.divider == 5) 473*b6d6454fSBen Dooks reg |= 0x10; /* /5 divider required */ 474*b6d6454fSBen Dooks if (to.mclk != 288000000) 475*b6d6454fSBen Dooks reg |= 0x20; /* which mclk pll is source */ 476*b6d6454fSBen Dooks break; 477*b6d6454fSBen Dooks 478*b6d6454fSBen Dooks case SM501_CLOCK_V2XCLK: 479*b6d6454fSBen Dooks /* This clock is divided in half so to achive the 480*b6d6454fSBen Dooks * requested frequency the value must be multiplied by 2. */ 481*b6d6454fSBen Dooks 482*b6d6454fSBen Dooks sm501_freq = (sm501_select_clock(2 * req_freq, &to, 3) / 2); 483*b6d6454fSBen Dooks reg=to.shift & 0x07; /* bottom 3 bits are shift */ 484*b6d6454fSBen Dooks if (to.divider == 3) 485*b6d6454fSBen Dooks reg |= 0x08; /* /3 divider required */ 486*b6d6454fSBen Dooks if (to.mclk != 288000000) 487*b6d6454fSBen Dooks reg |= 0x10; /* which mclk pll is source */ 488*b6d6454fSBen Dooks break; 489*b6d6454fSBen Dooks 490*b6d6454fSBen Dooks case SM501_CLOCK_MCLK: 491*b6d6454fSBen Dooks case SM501_CLOCK_M1XCLK: 492*b6d6454fSBen Dooks /* These clocks are the same and not further divided */ 493*b6d6454fSBen Dooks 494*b6d6454fSBen Dooks sm501_freq = sm501_select_clock( req_freq, &to, 3); 495*b6d6454fSBen Dooks reg=to.shift & 0x07; /* bottom 3 bits are shift */ 496*b6d6454fSBen Dooks if (to.divider == 3) 497*b6d6454fSBen Dooks reg |= 0x08; /* /3 divider required */ 498*b6d6454fSBen Dooks if (to.mclk != 288000000) 499*b6d6454fSBen Dooks reg |= 0x10; /* which mclk pll is source */ 500*b6d6454fSBen Dooks break; 501*b6d6454fSBen Dooks 502*b6d6454fSBen Dooks default: 503*b6d6454fSBen Dooks return 0; /* this is bad */ 504*b6d6454fSBen Dooks } 505*b6d6454fSBen Dooks 506*b6d6454fSBen Dooks mutex_lock(&sm->clock_lock); 507*b6d6454fSBen Dooks 508*b6d6454fSBen Dooks mode = readl(sm->regs + SM501_POWER_MODE_CONTROL); 509*b6d6454fSBen Dooks gate = readl(sm->regs + SM501_CURRENT_GATE); 510*b6d6454fSBen Dooks clock = readl(sm->regs + SM501_CURRENT_CLOCK); 511*b6d6454fSBen Dooks 512*b6d6454fSBen Dooks clock = clock & ~(0xFF << clksrc); 513*b6d6454fSBen Dooks clock |= reg<<clksrc; 514*b6d6454fSBen Dooks 515*b6d6454fSBen Dooks mode &= 3; /* find current mode */ 516*b6d6454fSBen Dooks 517*b6d6454fSBen Dooks switch (mode) { 518*b6d6454fSBen Dooks case 1: 519*b6d6454fSBen Dooks writel(gate, sm->regs + SM501_POWER_MODE_0_GATE); 520*b6d6454fSBen Dooks writel(clock, sm->regs + SM501_POWER_MODE_0_CLOCK); 521*b6d6454fSBen Dooks mode = 0; 522*b6d6454fSBen Dooks break; 523*b6d6454fSBen Dooks case 2: 524*b6d6454fSBen Dooks case 0: 525*b6d6454fSBen Dooks writel(gate, sm->regs + SM501_POWER_MODE_1_GATE); 526*b6d6454fSBen Dooks writel(clock, sm->regs + SM501_POWER_MODE_1_CLOCK); 527*b6d6454fSBen Dooks mode = 1; 528*b6d6454fSBen Dooks break; 529*b6d6454fSBen Dooks 530*b6d6454fSBen Dooks default: 531*b6d6454fSBen Dooks mutex_unlock(&sm->clock_lock); 532*b6d6454fSBen Dooks return -1; 533*b6d6454fSBen Dooks } 534*b6d6454fSBen Dooks 535*b6d6454fSBen Dooks writel(mode, sm->regs + SM501_POWER_MODE_CONTROL); 536*b6d6454fSBen Dooks sm501_sync_regs(sm); 537*b6d6454fSBen Dooks 538*b6d6454fSBen Dooks dev_info(sm->dev, "gate %08lx, clock %08lx, mode %08lx\n", 539*b6d6454fSBen Dooks gate, clock, mode); 540*b6d6454fSBen Dooks 541*b6d6454fSBen Dooks msleep(16); 542*b6d6454fSBen Dooks mutex_unlock(&sm->clock_lock); 543*b6d6454fSBen Dooks 544*b6d6454fSBen Dooks sm501_dump_clk(sm); 545*b6d6454fSBen Dooks 546*b6d6454fSBen Dooks return sm501_freq; 547*b6d6454fSBen Dooks } 548*b6d6454fSBen Dooks 549*b6d6454fSBen Dooks EXPORT_SYMBOL_GPL(sm501_set_clock); 550*b6d6454fSBen Dooks 551*b6d6454fSBen Dooks /* sm501_find_clock 552*b6d6454fSBen Dooks * 553*b6d6454fSBen Dooks * finds the closest available frequency for a given clock 554*b6d6454fSBen Dooks */ 555*b6d6454fSBen Dooks 556*b6d6454fSBen Dooks unsigned long sm501_find_clock(int clksrc, 557*b6d6454fSBen Dooks unsigned long req_freq) 558*b6d6454fSBen Dooks { 559*b6d6454fSBen Dooks unsigned long sm501_freq; /* the frequency achiveable by the 501 */ 560*b6d6454fSBen Dooks struct sm501_clock to; 561*b6d6454fSBen Dooks 562*b6d6454fSBen Dooks switch (clksrc) { 563*b6d6454fSBen Dooks case SM501_CLOCK_P2XCLK: 564*b6d6454fSBen Dooks sm501_freq = (sm501_select_clock(2 * req_freq, &to, 5) / 2); 565*b6d6454fSBen Dooks break; 566*b6d6454fSBen Dooks 567*b6d6454fSBen Dooks case SM501_CLOCK_V2XCLK: 568*b6d6454fSBen Dooks sm501_freq = (sm501_select_clock(2 * req_freq, &to, 3) / 2); 569*b6d6454fSBen Dooks break; 570*b6d6454fSBen Dooks 571*b6d6454fSBen Dooks case SM501_CLOCK_MCLK: 572*b6d6454fSBen Dooks case SM501_CLOCK_M1XCLK: 573*b6d6454fSBen Dooks sm501_freq = sm501_select_clock(req_freq, &to, 3); 574*b6d6454fSBen Dooks break; 575*b6d6454fSBen Dooks 576*b6d6454fSBen Dooks default: 577*b6d6454fSBen Dooks sm501_freq = 0; /* error */ 578*b6d6454fSBen Dooks } 579*b6d6454fSBen Dooks 580*b6d6454fSBen Dooks return sm501_freq; 581*b6d6454fSBen Dooks } 582*b6d6454fSBen Dooks 583*b6d6454fSBen Dooks EXPORT_SYMBOL_GPL(sm501_find_clock); 584*b6d6454fSBen Dooks 585*b6d6454fSBen Dooks static struct sm501_device *to_sm_device(struct platform_device *pdev) 586*b6d6454fSBen Dooks { 587*b6d6454fSBen Dooks return container_of(pdev, struct sm501_device, pdev); 588*b6d6454fSBen Dooks } 589*b6d6454fSBen Dooks 590*b6d6454fSBen Dooks /* sm501_device_release 591*b6d6454fSBen Dooks * 592*b6d6454fSBen Dooks * A release function for the platform devices we create to allow us to 593*b6d6454fSBen Dooks * free any items we allocated 594*b6d6454fSBen Dooks */ 595*b6d6454fSBen Dooks 596*b6d6454fSBen Dooks static void sm501_device_release(struct device *dev) 597*b6d6454fSBen Dooks { 598*b6d6454fSBen Dooks kfree(to_sm_device(to_platform_device(dev))); 599*b6d6454fSBen Dooks } 600*b6d6454fSBen Dooks 601*b6d6454fSBen Dooks /* sm501_create_subdev 602*b6d6454fSBen Dooks * 603*b6d6454fSBen Dooks * Create a skeleton platform device with resources for passing to a 604*b6d6454fSBen Dooks * sub-driver 605*b6d6454fSBen Dooks */ 606*b6d6454fSBen Dooks 607*b6d6454fSBen Dooks static struct platform_device * 608*b6d6454fSBen Dooks sm501_create_subdev(struct sm501_devdata *sm, 609*b6d6454fSBen Dooks char *name, unsigned int res_count) 610*b6d6454fSBen Dooks { 611*b6d6454fSBen Dooks struct sm501_device *smdev; 612*b6d6454fSBen Dooks 613*b6d6454fSBen Dooks smdev = kzalloc(sizeof(struct sm501_device) + 614*b6d6454fSBen Dooks sizeof(struct resource) * res_count, GFP_KERNEL); 615*b6d6454fSBen Dooks if (!smdev) 616*b6d6454fSBen Dooks return NULL; 617*b6d6454fSBen Dooks 618*b6d6454fSBen Dooks smdev->pdev.dev.release = sm501_device_release; 619*b6d6454fSBen Dooks 620*b6d6454fSBen Dooks smdev->pdev.name = name; 621*b6d6454fSBen Dooks smdev->pdev.id = sm->pdev_id; 622*b6d6454fSBen Dooks smdev->pdev.resource = (struct resource *)(smdev+1); 623*b6d6454fSBen Dooks smdev->pdev.num_resources = res_count; 624*b6d6454fSBen Dooks 625*b6d6454fSBen Dooks smdev->pdev.dev.parent = sm->dev; 626*b6d6454fSBen Dooks 627*b6d6454fSBen Dooks return &smdev->pdev; 628*b6d6454fSBen Dooks } 629*b6d6454fSBen Dooks 630*b6d6454fSBen Dooks /* sm501_register_device 631*b6d6454fSBen Dooks * 632*b6d6454fSBen Dooks * Register a platform device created with sm501_create_subdev() 633*b6d6454fSBen Dooks */ 634*b6d6454fSBen Dooks 635*b6d6454fSBen Dooks static int sm501_register_device(struct sm501_devdata *sm, 636*b6d6454fSBen Dooks struct platform_device *pdev) 637*b6d6454fSBen Dooks { 638*b6d6454fSBen Dooks struct sm501_device *smdev = to_sm_device(pdev); 639*b6d6454fSBen Dooks int ptr; 640*b6d6454fSBen Dooks int ret; 641*b6d6454fSBen Dooks 642*b6d6454fSBen Dooks for (ptr = 0; ptr < pdev->num_resources; ptr++) { 643*b6d6454fSBen Dooks printk("%s[%d] flags %08lx: %08llx..%08llx\n", 644*b6d6454fSBen Dooks pdev->name, ptr, 645*b6d6454fSBen Dooks pdev->resource[ptr].flags, 646*b6d6454fSBen Dooks (unsigned long long)pdev->resource[ptr].start, 647*b6d6454fSBen Dooks (unsigned long long)pdev->resource[ptr].end); 648*b6d6454fSBen Dooks } 649*b6d6454fSBen Dooks 650*b6d6454fSBen Dooks ret = platform_device_register(pdev); 651*b6d6454fSBen Dooks 652*b6d6454fSBen Dooks if (ret >= 0) { 653*b6d6454fSBen Dooks dev_dbg(sm->dev, "registered %s\n", pdev->name); 654*b6d6454fSBen Dooks list_add_tail(&smdev->list, &sm->devices); 655*b6d6454fSBen Dooks } else 656*b6d6454fSBen Dooks dev_err(sm->dev, "error registering %s (%d)\n", 657*b6d6454fSBen Dooks pdev->name, ret); 658*b6d6454fSBen Dooks 659*b6d6454fSBen Dooks return ret; 660*b6d6454fSBen Dooks } 661*b6d6454fSBen Dooks 662*b6d6454fSBen Dooks /* sm501_create_subio 663*b6d6454fSBen Dooks * 664*b6d6454fSBen Dooks * Fill in an IO resource for a sub device 665*b6d6454fSBen Dooks */ 666*b6d6454fSBen Dooks 667*b6d6454fSBen Dooks static void sm501_create_subio(struct sm501_devdata *sm, 668*b6d6454fSBen Dooks struct resource *res, 669*b6d6454fSBen Dooks resource_size_t offs, 670*b6d6454fSBen Dooks resource_size_t size) 671*b6d6454fSBen Dooks { 672*b6d6454fSBen Dooks res->flags = IORESOURCE_MEM; 673*b6d6454fSBen Dooks res->parent = sm->io_res; 674*b6d6454fSBen Dooks res->start = sm->io_res->start + offs; 675*b6d6454fSBen Dooks res->end = res->start + size - 1; 676*b6d6454fSBen Dooks } 677*b6d6454fSBen Dooks 678*b6d6454fSBen Dooks /* sm501_create_mem 679*b6d6454fSBen Dooks * 680*b6d6454fSBen Dooks * Fill in an MEM resource for a sub device 681*b6d6454fSBen Dooks */ 682*b6d6454fSBen Dooks 683*b6d6454fSBen Dooks static void sm501_create_mem(struct sm501_devdata *sm, 684*b6d6454fSBen Dooks struct resource *res, 685*b6d6454fSBen Dooks resource_size_t *offs, 686*b6d6454fSBen Dooks resource_size_t size) 687*b6d6454fSBen Dooks { 688*b6d6454fSBen Dooks *offs -= size; /* adjust memory size */ 689*b6d6454fSBen Dooks 690*b6d6454fSBen Dooks res->flags = IORESOURCE_MEM; 691*b6d6454fSBen Dooks res->parent = sm->mem_res; 692*b6d6454fSBen Dooks res->start = sm->mem_res->start + *offs; 693*b6d6454fSBen Dooks res->end = res->start + size - 1; 694*b6d6454fSBen Dooks } 695*b6d6454fSBen Dooks 696*b6d6454fSBen Dooks /* sm501_create_irq 697*b6d6454fSBen Dooks * 698*b6d6454fSBen Dooks * Fill in an IRQ resource for a sub device 699*b6d6454fSBen Dooks */ 700*b6d6454fSBen Dooks 701*b6d6454fSBen Dooks static void sm501_create_irq(struct sm501_devdata *sm, 702*b6d6454fSBen Dooks struct resource *res) 703*b6d6454fSBen Dooks { 704*b6d6454fSBen Dooks res->flags = IORESOURCE_IRQ; 705*b6d6454fSBen Dooks res->parent = NULL; 706*b6d6454fSBen Dooks res->start = res->end = sm->irq; 707*b6d6454fSBen Dooks } 708*b6d6454fSBen Dooks 709*b6d6454fSBen Dooks static int sm501_register_usbhost(struct sm501_devdata *sm, 710*b6d6454fSBen Dooks resource_size_t *mem_avail) 711*b6d6454fSBen Dooks { 712*b6d6454fSBen Dooks struct platform_device *pdev; 713*b6d6454fSBen Dooks 714*b6d6454fSBen Dooks pdev = sm501_create_subdev(sm, "sm501-usb", 3); 715*b6d6454fSBen Dooks if (!pdev) 716*b6d6454fSBen Dooks return -ENOMEM; 717*b6d6454fSBen Dooks 718*b6d6454fSBen Dooks sm501_create_subio(sm, &pdev->resource[0], 0x40000, 0x20000); 719*b6d6454fSBen Dooks sm501_create_mem(sm, &pdev->resource[1], mem_avail, 256*1024); 720*b6d6454fSBen Dooks sm501_create_irq(sm, &pdev->resource[2]); 721*b6d6454fSBen Dooks 722*b6d6454fSBen Dooks return sm501_register_device(sm, pdev); 723*b6d6454fSBen Dooks } 724*b6d6454fSBen Dooks 725*b6d6454fSBen Dooks static int sm501_register_display(struct sm501_devdata *sm, 726*b6d6454fSBen Dooks resource_size_t *mem_avail) 727*b6d6454fSBen Dooks { 728*b6d6454fSBen Dooks struct platform_device *pdev; 729*b6d6454fSBen Dooks 730*b6d6454fSBen Dooks pdev = sm501_create_subdev(sm, "sm501-fb", 4); 731*b6d6454fSBen Dooks if (!pdev) 732*b6d6454fSBen Dooks return -ENOMEM; 733*b6d6454fSBen Dooks 734*b6d6454fSBen Dooks sm501_create_subio(sm, &pdev->resource[0], 0x80000, 0x10000); 735*b6d6454fSBen Dooks sm501_create_subio(sm, &pdev->resource[1], 0x100000, 0x50000); 736*b6d6454fSBen Dooks sm501_create_mem(sm, &pdev->resource[2], mem_avail, *mem_avail); 737*b6d6454fSBen Dooks sm501_create_irq(sm, &pdev->resource[3]); 738*b6d6454fSBen Dooks 739*b6d6454fSBen Dooks return sm501_register_device(sm, pdev); 740*b6d6454fSBen Dooks } 741*b6d6454fSBen Dooks 742*b6d6454fSBen Dooks /* sm501_dbg_regs 743*b6d6454fSBen Dooks * 744*b6d6454fSBen Dooks * Debug attribute to attach to parent device to show core registers 745*b6d6454fSBen Dooks */ 746*b6d6454fSBen Dooks 747*b6d6454fSBen Dooks static ssize_t sm501_dbg_regs(struct device *dev, 748*b6d6454fSBen Dooks struct device_attribute *attr, char *buff) 749*b6d6454fSBen Dooks { 750*b6d6454fSBen Dooks struct sm501_devdata *sm = dev_get_drvdata(dev) ; 751*b6d6454fSBen Dooks unsigned int reg; 752*b6d6454fSBen Dooks char *ptr = buff; 753*b6d6454fSBen Dooks int ret; 754*b6d6454fSBen Dooks 755*b6d6454fSBen Dooks for (reg = 0x00; reg < 0x70; reg += 4) { 756*b6d6454fSBen Dooks ret = sprintf(ptr, "%08x = %08x\n", 757*b6d6454fSBen Dooks reg, readl(sm->regs + reg)); 758*b6d6454fSBen Dooks ptr += ret; 759*b6d6454fSBen Dooks } 760*b6d6454fSBen Dooks 761*b6d6454fSBen Dooks return ptr - buff; 762*b6d6454fSBen Dooks } 763*b6d6454fSBen Dooks 764*b6d6454fSBen Dooks 765*b6d6454fSBen Dooks static DEVICE_ATTR(dbg_regs, 0666, sm501_dbg_regs, NULL); 766*b6d6454fSBen Dooks 767*b6d6454fSBen Dooks /* sm501_init_reg 768*b6d6454fSBen Dooks * 769*b6d6454fSBen Dooks * Helper function for the init code to setup a register 770*b6d6454fSBen Dooks */ 771*b6d6454fSBen Dooks 772*b6d6454fSBen Dooks static inline void sm501_init_reg(struct sm501_devdata *sm, 773*b6d6454fSBen Dooks unsigned long reg, 774*b6d6454fSBen Dooks struct sm501_reg_init *r) 775*b6d6454fSBen Dooks { 776*b6d6454fSBen Dooks unsigned long tmp; 777*b6d6454fSBen Dooks 778*b6d6454fSBen Dooks tmp = readl(sm->regs + reg); 779*b6d6454fSBen Dooks tmp |= r->set; 780*b6d6454fSBen Dooks tmp &= ~r->mask; 781*b6d6454fSBen Dooks writel(tmp, sm->regs + reg); 782*b6d6454fSBen Dooks } 783*b6d6454fSBen Dooks 784*b6d6454fSBen Dooks /* sm501_init_regs 785*b6d6454fSBen Dooks * 786*b6d6454fSBen Dooks * Setup core register values 787*b6d6454fSBen Dooks */ 788*b6d6454fSBen Dooks 789*b6d6454fSBen Dooks static void sm501_init_regs(struct sm501_devdata *sm, 790*b6d6454fSBen Dooks struct sm501_initdata *init) 791*b6d6454fSBen Dooks { 792*b6d6454fSBen Dooks sm501_misc_control(sm->dev, 793*b6d6454fSBen Dooks init->misc_control.set, 794*b6d6454fSBen Dooks init->misc_control.mask); 795*b6d6454fSBen Dooks 796*b6d6454fSBen Dooks sm501_init_reg(sm, SM501_MISC_TIMING, &init->misc_timing); 797*b6d6454fSBen Dooks sm501_init_reg(sm, SM501_GPIO31_0_CONTROL, &init->gpio_low); 798*b6d6454fSBen Dooks sm501_init_reg(sm, SM501_GPIO63_32_CONTROL, &init->gpio_high); 799*b6d6454fSBen Dooks 800*b6d6454fSBen Dooks if (init->mclk) { 801*b6d6454fSBen Dooks dev_info(sm->dev, "setting MCLK to %ld\n", init->mclk); 802*b6d6454fSBen Dooks sm501_set_clock(sm->dev, SM501_CLOCK_MCLK, init->mclk); 803*b6d6454fSBen Dooks } 804*b6d6454fSBen Dooks 805*b6d6454fSBen Dooks if (init->m1xclk) { 806*b6d6454fSBen Dooks dev_info(sm->dev, "setting M1XCLK to %ld\n", init->m1xclk); 807*b6d6454fSBen Dooks sm501_set_clock(sm->dev, SM501_CLOCK_M1XCLK, init->m1xclk); 808*b6d6454fSBen Dooks } 809*b6d6454fSBen Dooks } 810*b6d6454fSBen Dooks 811*b6d6454fSBen Dooks static unsigned int sm501_mem_local[] = { 812*b6d6454fSBen Dooks [0] = 4*1024*1024, 813*b6d6454fSBen Dooks [1] = 8*1024*1024, 814*b6d6454fSBen Dooks [2] = 16*1024*1024, 815*b6d6454fSBen Dooks [3] = 32*1024*1024, 816*b6d6454fSBen Dooks [4] = 64*1024*1024, 817*b6d6454fSBen Dooks [5] = 2*1024*1024, 818*b6d6454fSBen Dooks }; 819*b6d6454fSBen Dooks 820*b6d6454fSBen Dooks /* sm501_init_dev 821*b6d6454fSBen Dooks * 822*b6d6454fSBen Dooks * Common init code for an SM501 823*b6d6454fSBen Dooks */ 824*b6d6454fSBen Dooks 825*b6d6454fSBen Dooks static int sm501_init_dev(struct sm501_devdata *sm) 826*b6d6454fSBen Dooks { 827*b6d6454fSBen Dooks resource_size_t mem_avail; 828*b6d6454fSBen Dooks unsigned long dramctrl; 829*b6d6454fSBen Dooks int ret; 830*b6d6454fSBen Dooks 831*b6d6454fSBen Dooks mutex_init(&sm->clock_lock); 832*b6d6454fSBen Dooks spin_lock_init(&sm->reg_lock); 833*b6d6454fSBen Dooks 834*b6d6454fSBen Dooks INIT_LIST_HEAD(&sm->devices); 835*b6d6454fSBen Dooks 836*b6d6454fSBen Dooks dramctrl = readl(sm->regs + SM501_DRAM_CONTROL); 837*b6d6454fSBen Dooks 838*b6d6454fSBen Dooks mem_avail = sm501_mem_local[(dramctrl >> 13) & 0x7]; 839*b6d6454fSBen Dooks 840*b6d6454fSBen Dooks dev_info(sm->dev, "SM501 At %p: Version %08x, %ld Mb, IRQ %d\n", 841*b6d6454fSBen Dooks sm->regs, readl(sm->regs + SM501_DEVICEID), 842*b6d6454fSBen Dooks (unsigned long)mem_avail >> 20, sm->irq); 843*b6d6454fSBen Dooks 844*b6d6454fSBen Dooks dev_info(sm->dev, "CurrentGate %08x\n", readl(sm->regs+0x38)); 845*b6d6454fSBen Dooks dev_info(sm->dev, "CurrentClock %08x\n", readl(sm->regs+0x3c)); 846*b6d6454fSBen Dooks dev_info(sm->dev, "PowerModeControl %08x\n", readl(sm->regs+0x54)); 847*b6d6454fSBen Dooks 848*b6d6454fSBen Dooks ret = device_create_file(sm->dev, &dev_attr_dbg_regs); 849*b6d6454fSBen Dooks if (ret) 850*b6d6454fSBen Dooks dev_err(sm->dev, "failed to create debug regs file\n"); 851*b6d6454fSBen Dooks 852*b6d6454fSBen Dooks sm501_dump_clk(sm); 853*b6d6454fSBen Dooks 854*b6d6454fSBen Dooks /* check to see if we have some device initialisation */ 855*b6d6454fSBen Dooks 856*b6d6454fSBen Dooks if (sm->platdata) { 857*b6d6454fSBen Dooks struct sm501_platdata *pdata = sm->platdata; 858*b6d6454fSBen Dooks 859*b6d6454fSBen Dooks if (pdata->init) { 860*b6d6454fSBen Dooks sm501_init_regs(sm, sm->platdata->init); 861*b6d6454fSBen Dooks 862*b6d6454fSBen Dooks if (pdata->init->devices & SM501_USE_USB_HOST) 863*b6d6454fSBen Dooks sm501_register_usbhost(sm, &mem_avail); 864*b6d6454fSBen Dooks } 865*b6d6454fSBen Dooks } 866*b6d6454fSBen Dooks 867*b6d6454fSBen Dooks /* always create a framebuffer */ 868*b6d6454fSBen Dooks sm501_register_display(sm, &mem_avail); 869*b6d6454fSBen Dooks 870*b6d6454fSBen Dooks return 0; 871*b6d6454fSBen Dooks } 872*b6d6454fSBen Dooks 873*b6d6454fSBen Dooks static int sm501_plat_probe(struct platform_device *dev) 874*b6d6454fSBen Dooks { 875*b6d6454fSBen Dooks struct sm501_devdata *sm; 876*b6d6454fSBen Dooks int err; 877*b6d6454fSBen Dooks 878*b6d6454fSBen Dooks sm = kzalloc(sizeof(struct sm501_devdata), GFP_KERNEL); 879*b6d6454fSBen Dooks if (sm == NULL) { 880*b6d6454fSBen Dooks dev_err(&dev->dev, "no memory for device data\n"); 881*b6d6454fSBen Dooks err = -ENOMEM; 882*b6d6454fSBen Dooks goto err1; 883*b6d6454fSBen Dooks } 884*b6d6454fSBen Dooks 885*b6d6454fSBen Dooks sm->dev = &dev->dev; 886*b6d6454fSBen Dooks sm->pdev_id = dev->id; 887*b6d6454fSBen Dooks sm->irq = platform_get_irq(dev, 0); 888*b6d6454fSBen Dooks sm->io_res = platform_get_resource(dev, IORESOURCE_MEM, 1); 889*b6d6454fSBen Dooks sm->mem_res = platform_get_resource(dev, IORESOURCE_MEM, 0); 890*b6d6454fSBen Dooks sm->platdata = dev->dev.platform_data; 891*b6d6454fSBen Dooks 892*b6d6454fSBen Dooks if (sm->irq < 0) { 893*b6d6454fSBen Dooks dev_err(&dev->dev, "failed to get irq resource\n"); 894*b6d6454fSBen Dooks err = sm->irq; 895*b6d6454fSBen Dooks goto err_res; 896*b6d6454fSBen Dooks } 897*b6d6454fSBen Dooks 898*b6d6454fSBen Dooks if (sm->io_res == NULL || sm->mem_res == NULL) { 899*b6d6454fSBen Dooks dev_err(&dev->dev, "failed to get IO resource\n"); 900*b6d6454fSBen Dooks err = -ENOENT; 901*b6d6454fSBen Dooks goto err_res; 902*b6d6454fSBen Dooks } 903*b6d6454fSBen Dooks 904*b6d6454fSBen Dooks sm->regs_claim = request_mem_region(sm->io_res->start, 905*b6d6454fSBen Dooks 0x100, "sm501"); 906*b6d6454fSBen Dooks 907*b6d6454fSBen Dooks if (sm->regs_claim == NULL) { 908*b6d6454fSBen Dooks dev_err(&dev->dev, "cannot claim registers\n"); 909*b6d6454fSBen Dooks err= -EBUSY; 910*b6d6454fSBen Dooks goto err_res; 911*b6d6454fSBen Dooks } 912*b6d6454fSBen Dooks 913*b6d6454fSBen Dooks platform_set_drvdata(dev, sm); 914*b6d6454fSBen Dooks 915*b6d6454fSBen Dooks sm->regs = ioremap(sm->io_res->start, 916*b6d6454fSBen Dooks (sm->io_res->end - sm->io_res->start) - 1); 917*b6d6454fSBen Dooks 918*b6d6454fSBen Dooks if (sm->regs == NULL) { 919*b6d6454fSBen Dooks dev_err(&dev->dev, "cannot remap registers\n"); 920*b6d6454fSBen Dooks err = -EIO; 921*b6d6454fSBen Dooks goto err_claim; 922*b6d6454fSBen Dooks } 923*b6d6454fSBen Dooks 924*b6d6454fSBen Dooks return sm501_init_dev(sm); 925*b6d6454fSBen Dooks 926*b6d6454fSBen Dooks err_claim: 927*b6d6454fSBen Dooks release_resource(sm->regs_claim); 928*b6d6454fSBen Dooks kfree(sm->regs_claim); 929*b6d6454fSBen Dooks err_res: 930*b6d6454fSBen Dooks kfree(sm); 931*b6d6454fSBen Dooks err1: 932*b6d6454fSBen Dooks return err; 933*b6d6454fSBen Dooks 934*b6d6454fSBen Dooks } 935*b6d6454fSBen Dooks 936*b6d6454fSBen Dooks /* Initialisation data for PCI devices */ 937*b6d6454fSBen Dooks 938*b6d6454fSBen Dooks static struct sm501_initdata sm501_pci_initdata = { 939*b6d6454fSBen Dooks .gpio_high = { 940*b6d6454fSBen Dooks .set = 0x3F000000, /* 24bit panel */ 941*b6d6454fSBen Dooks .mask = 0x0, 942*b6d6454fSBen Dooks }, 943*b6d6454fSBen Dooks .misc_timing = { 944*b6d6454fSBen Dooks .set = 0x010100, /* SDRAM timing */ 945*b6d6454fSBen Dooks .mask = 0x1F1F00, 946*b6d6454fSBen Dooks }, 947*b6d6454fSBen Dooks .misc_control = { 948*b6d6454fSBen Dooks .set = SM501_MISC_PNL_24BIT, 949*b6d6454fSBen Dooks .mask = 0, 950*b6d6454fSBen Dooks }, 951*b6d6454fSBen Dooks 952*b6d6454fSBen Dooks .devices = SM501_USE_ALL, 953*b6d6454fSBen Dooks .mclk = 100 * MHZ, 954*b6d6454fSBen Dooks .m1xclk = 160 * MHZ, 955*b6d6454fSBen Dooks }; 956*b6d6454fSBen Dooks 957*b6d6454fSBen Dooks static struct sm501_platdata_fbsub sm501_pdata_fbsub = { 958*b6d6454fSBen Dooks .flags = (SM501FB_FLAG_USE_INIT_MODE | 959*b6d6454fSBen Dooks SM501FB_FLAG_USE_HWCURSOR | 960*b6d6454fSBen Dooks SM501FB_FLAG_USE_HWACCEL | 961*b6d6454fSBen Dooks SM501FB_FLAG_DISABLE_AT_EXIT), 962*b6d6454fSBen Dooks }; 963*b6d6454fSBen Dooks 964*b6d6454fSBen Dooks static struct sm501_platdata_fb sm501_fb_pdata = { 965*b6d6454fSBen Dooks .fb_route = SM501_FB_OWN, 966*b6d6454fSBen Dooks .fb_crt = &sm501_pdata_fbsub, 967*b6d6454fSBen Dooks .fb_pnl = &sm501_pdata_fbsub, 968*b6d6454fSBen Dooks }; 969*b6d6454fSBen Dooks 970*b6d6454fSBen Dooks static struct sm501_platdata sm501_pci_platdata = { 971*b6d6454fSBen Dooks .init = &sm501_pci_initdata, 972*b6d6454fSBen Dooks .fb = &sm501_fb_pdata, 973*b6d6454fSBen Dooks }; 974*b6d6454fSBen Dooks 975*b6d6454fSBen Dooks static int sm501_pci_probe(struct pci_dev *dev, 976*b6d6454fSBen Dooks const struct pci_device_id *id) 977*b6d6454fSBen Dooks { 978*b6d6454fSBen Dooks struct sm501_devdata *sm; 979*b6d6454fSBen Dooks int err; 980*b6d6454fSBen Dooks 981*b6d6454fSBen Dooks sm = kzalloc(sizeof(struct sm501_devdata), GFP_KERNEL); 982*b6d6454fSBen Dooks if (sm == NULL) { 983*b6d6454fSBen Dooks dev_err(&dev->dev, "no memory for device data\n"); 984*b6d6454fSBen Dooks err = -ENOMEM; 985*b6d6454fSBen Dooks goto err1; 986*b6d6454fSBen Dooks } 987*b6d6454fSBen Dooks 988*b6d6454fSBen Dooks /* set a default set of platform data */ 989*b6d6454fSBen Dooks dev->dev.platform_data = sm->platdata = &sm501_pci_platdata; 990*b6d6454fSBen Dooks 991*b6d6454fSBen Dooks /* set a hopefully unique id for our child platform devices */ 992*b6d6454fSBen Dooks sm->pdev_id = 32 + dev->devfn; 993*b6d6454fSBen Dooks 994*b6d6454fSBen Dooks pci_set_drvdata(dev, sm); 995*b6d6454fSBen Dooks 996*b6d6454fSBen Dooks err = pci_enable_device(dev); 997*b6d6454fSBen Dooks if (err) { 998*b6d6454fSBen Dooks dev_err(&dev->dev, "cannot enable device\n"); 999*b6d6454fSBen Dooks goto err2; 1000*b6d6454fSBen Dooks } 1001*b6d6454fSBen Dooks 1002*b6d6454fSBen Dooks sm->dev = &dev->dev; 1003*b6d6454fSBen Dooks sm->irq = dev->irq; 1004*b6d6454fSBen Dooks 1005*b6d6454fSBen Dooks #ifdef __BIG_ENDIAN 1006*b6d6454fSBen Dooks /* if the system is big-endian, we most probably have a 1007*b6d6454fSBen Dooks * translation in the IO layer making the PCI bus little endian 1008*b6d6454fSBen Dooks * so make the framebuffer swapped pixels */ 1009*b6d6454fSBen Dooks 1010*b6d6454fSBen Dooks sm501_fb_pdata.flags |= SM501_FBPD_SWAP_FB_ENDIAN; 1011*b6d6454fSBen Dooks #endif 1012*b6d6454fSBen Dooks 1013*b6d6454fSBen Dooks /* check our resources */ 1014*b6d6454fSBen Dooks 1015*b6d6454fSBen Dooks if (!(pci_resource_flags(dev, 0) & IORESOURCE_MEM)) { 1016*b6d6454fSBen Dooks dev_err(&dev->dev, "region #0 is not memory?\n"); 1017*b6d6454fSBen Dooks err = -EINVAL; 1018*b6d6454fSBen Dooks goto err3; 1019*b6d6454fSBen Dooks } 1020*b6d6454fSBen Dooks 1021*b6d6454fSBen Dooks if (!(pci_resource_flags(dev, 1) & IORESOURCE_MEM)) { 1022*b6d6454fSBen Dooks dev_err(&dev->dev, "region #1 is not memory?\n"); 1023*b6d6454fSBen Dooks err = -EINVAL; 1024*b6d6454fSBen Dooks goto err3; 1025*b6d6454fSBen Dooks } 1026*b6d6454fSBen Dooks 1027*b6d6454fSBen Dooks /* make our resources ready for sharing */ 1028*b6d6454fSBen Dooks 1029*b6d6454fSBen Dooks sm->io_res = &dev->resource[1]; 1030*b6d6454fSBen Dooks sm->mem_res = &dev->resource[0]; 1031*b6d6454fSBen Dooks 1032*b6d6454fSBen Dooks sm->regs_claim = request_mem_region(sm->io_res->start, 1033*b6d6454fSBen Dooks 0x100, "sm501"); 1034*b6d6454fSBen Dooks if (sm->regs_claim == NULL) { 1035*b6d6454fSBen Dooks dev_err(&dev->dev, "cannot claim registers\n"); 1036*b6d6454fSBen Dooks err= -EBUSY; 1037*b6d6454fSBen Dooks goto err3; 1038*b6d6454fSBen Dooks } 1039*b6d6454fSBen Dooks 1040*b6d6454fSBen Dooks sm->regs = ioremap(pci_resource_start(dev, 1), 1041*b6d6454fSBen Dooks pci_resource_len(dev, 1)); 1042*b6d6454fSBen Dooks 1043*b6d6454fSBen Dooks if (sm->regs == NULL) { 1044*b6d6454fSBen Dooks dev_err(&dev->dev, "cannot remap registers\n"); 1045*b6d6454fSBen Dooks err = -EIO; 1046*b6d6454fSBen Dooks goto err4; 1047*b6d6454fSBen Dooks } 1048*b6d6454fSBen Dooks 1049*b6d6454fSBen Dooks sm501_init_dev(sm); 1050*b6d6454fSBen Dooks return 0; 1051*b6d6454fSBen Dooks 1052*b6d6454fSBen Dooks err4: 1053*b6d6454fSBen Dooks release_resource(sm->regs_claim); 1054*b6d6454fSBen Dooks kfree(sm->regs_claim); 1055*b6d6454fSBen Dooks err3: 1056*b6d6454fSBen Dooks pci_disable_device(dev); 1057*b6d6454fSBen Dooks err2: 1058*b6d6454fSBen Dooks pci_set_drvdata(dev, NULL); 1059*b6d6454fSBen Dooks kfree(sm); 1060*b6d6454fSBen Dooks err1: 1061*b6d6454fSBen Dooks return err; 1062*b6d6454fSBen Dooks } 1063*b6d6454fSBen Dooks 1064*b6d6454fSBen Dooks static void sm501_remove_sub(struct sm501_devdata *sm, 1065*b6d6454fSBen Dooks struct sm501_device *smdev) 1066*b6d6454fSBen Dooks { 1067*b6d6454fSBen Dooks list_del(&smdev->list); 1068*b6d6454fSBen Dooks platform_device_unregister(&smdev->pdev); 1069*b6d6454fSBen Dooks } 1070*b6d6454fSBen Dooks 1071*b6d6454fSBen Dooks static void sm501_dev_remove(struct sm501_devdata *sm) 1072*b6d6454fSBen Dooks { 1073*b6d6454fSBen Dooks struct sm501_device *smdev, *tmp; 1074*b6d6454fSBen Dooks 1075*b6d6454fSBen Dooks list_for_each_entry_safe(smdev, tmp, &sm->devices, list) 1076*b6d6454fSBen Dooks sm501_remove_sub(sm, smdev); 1077*b6d6454fSBen Dooks 1078*b6d6454fSBen Dooks device_remove_file(sm->dev, &dev_attr_dbg_regs); 1079*b6d6454fSBen Dooks } 1080*b6d6454fSBen Dooks 1081*b6d6454fSBen Dooks static void sm501_pci_remove(struct pci_dev *dev) 1082*b6d6454fSBen Dooks { 1083*b6d6454fSBen Dooks struct sm501_devdata *sm = pci_get_drvdata(dev); 1084*b6d6454fSBen Dooks 1085*b6d6454fSBen Dooks sm501_dev_remove(sm); 1086*b6d6454fSBen Dooks iounmap(sm->regs); 1087*b6d6454fSBen Dooks 1088*b6d6454fSBen Dooks release_resource(sm->regs_claim); 1089*b6d6454fSBen Dooks kfree(sm->regs_claim); 1090*b6d6454fSBen Dooks 1091*b6d6454fSBen Dooks pci_set_drvdata(dev, NULL); 1092*b6d6454fSBen Dooks pci_disable_device(dev); 1093*b6d6454fSBen Dooks } 1094*b6d6454fSBen Dooks 1095*b6d6454fSBen Dooks static int sm501_plat_remove(struct platform_device *dev) 1096*b6d6454fSBen Dooks { 1097*b6d6454fSBen Dooks struct sm501_devdata *sm = platform_get_drvdata(dev); 1098*b6d6454fSBen Dooks 1099*b6d6454fSBen Dooks sm501_dev_remove(sm); 1100*b6d6454fSBen Dooks iounmap(sm->regs); 1101*b6d6454fSBen Dooks 1102*b6d6454fSBen Dooks release_resource(sm->regs_claim); 1103*b6d6454fSBen Dooks kfree(sm->regs_claim); 1104*b6d6454fSBen Dooks 1105*b6d6454fSBen Dooks return 0; 1106*b6d6454fSBen Dooks } 1107*b6d6454fSBen Dooks 1108*b6d6454fSBen Dooks static struct pci_device_id sm501_pci_tbl[] = { 1109*b6d6454fSBen Dooks { 0x126f, 0x0501, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, 1110*b6d6454fSBen Dooks { 0, }, 1111*b6d6454fSBen Dooks }; 1112*b6d6454fSBen Dooks 1113*b6d6454fSBen Dooks MODULE_DEVICE_TABLE(pci, sm501_pci_tbl); 1114*b6d6454fSBen Dooks 1115*b6d6454fSBen Dooks static struct pci_driver sm501_pci_drv = { 1116*b6d6454fSBen Dooks .name = "sm501", 1117*b6d6454fSBen Dooks .id_table = sm501_pci_tbl, 1118*b6d6454fSBen Dooks .probe = sm501_pci_probe, 1119*b6d6454fSBen Dooks .remove = sm501_pci_remove, 1120*b6d6454fSBen Dooks }; 1121*b6d6454fSBen Dooks 1122*b6d6454fSBen Dooks static struct platform_driver sm501_plat_drv = { 1123*b6d6454fSBen Dooks .driver = { 1124*b6d6454fSBen Dooks .name = "sm501", 1125*b6d6454fSBen Dooks .owner = THIS_MODULE, 1126*b6d6454fSBen Dooks }, 1127*b6d6454fSBen Dooks .probe = sm501_plat_probe, 1128*b6d6454fSBen Dooks .remove = sm501_plat_remove, 1129*b6d6454fSBen Dooks }; 1130*b6d6454fSBen Dooks 1131*b6d6454fSBen Dooks static int __init sm501_base_init(void) 1132*b6d6454fSBen Dooks { 1133*b6d6454fSBen Dooks platform_driver_register(&sm501_plat_drv); 1134*b6d6454fSBen Dooks return pci_module_init(&sm501_pci_drv); 1135*b6d6454fSBen Dooks } 1136*b6d6454fSBen Dooks 1137*b6d6454fSBen Dooks static void __exit sm501_base_exit(void) 1138*b6d6454fSBen Dooks { 1139*b6d6454fSBen Dooks platform_driver_unregister(&sm501_plat_drv); 1140*b6d6454fSBen Dooks pci_unregister_driver(&sm501_pci_drv); 1141*b6d6454fSBen Dooks } 1142*b6d6454fSBen Dooks 1143*b6d6454fSBen Dooks module_init(sm501_base_init); 1144*b6d6454fSBen Dooks module_exit(sm501_base_exit); 1145*b6d6454fSBen Dooks 1146*b6d6454fSBen Dooks MODULE_DESCRIPTION("SM501 Core Driver"); 1147*b6d6454fSBen Dooks MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>, Vincent Sanders"); 1148*b6d6454fSBen Dooks MODULE_LICENSE("GPL v2"); 1149