162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* linux/drivers/mfd/sm501.c 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2006 Simtec Electronics 562306a36Sopenharmony_ci * Ben Dooks <ben@simtec.co.uk> 662306a36Sopenharmony_ci * Vincent Sanders <vince@simtec.co.uk> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * SM501 MFD driver 962306a36Sopenharmony_ci*/ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/delay.h> 1462306a36Sopenharmony_ci#include <linux/init.h> 1562306a36Sopenharmony_ci#include <linux/list.h> 1662306a36Sopenharmony_ci#include <linux/device.h> 1762306a36Sopenharmony_ci#include <linux/platform_device.h> 1862306a36Sopenharmony_ci#include <linux/pci.h> 1962306a36Sopenharmony_ci#include <linux/platform_data/i2c-gpio.h> 2062306a36Sopenharmony_ci#include <linux/gpio/driver.h> 2162306a36Sopenharmony_ci#include <linux/gpio/machine.h> 2262306a36Sopenharmony_ci#include <linux/slab.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <linux/sm501.h> 2562306a36Sopenharmony_ci#include <linux/sm501-regs.h> 2662306a36Sopenharmony_ci#include <linux/serial_8250.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include <linux/io.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistruct sm501_device { 3162306a36Sopenharmony_ci struct list_head list; 3262306a36Sopenharmony_ci struct platform_device pdev; 3362306a36Sopenharmony_ci}; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistruct sm501_gpio; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#ifdef CONFIG_MFD_SM501_GPIO 3862306a36Sopenharmony_ci#include <linux/gpio.h> 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistruct sm501_gpio_chip { 4162306a36Sopenharmony_ci struct gpio_chip gpio; 4262306a36Sopenharmony_ci struct sm501_gpio *ourgpio; /* to get back to parent. */ 4362306a36Sopenharmony_ci void __iomem *regbase; 4462306a36Sopenharmony_ci void __iomem *control; /* address of control reg. */ 4562306a36Sopenharmony_ci}; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistruct sm501_gpio { 4862306a36Sopenharmony_ci struct sm501_gpio_chip low; 4962306a36Sopenharmony_ci struct sm501_gpio_chip high; 5062306a36Sopenharmony_ci spinlock_t lock; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci unsigned int registered : 1; 5362306a36Sopenharmony_ci void __iomem *regs; 5462306a36Sopenharmony_ci struct resource *regs_res; 5562306a36Sopenharmony_ci}; 5662306a36Sopenharmony_ci#else 5762306a36Sopenharmony_cistruct sm501_gpio { 5862306a36Sopenharmony_ci /* no gpio support, empty definition for sm501_devdata. */ 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci#endif 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistruct sm501_devdata { 6362306a36Sopenharmony_ci spinlock_t reg_lock; 6462306a36Sopenharmony_ci struct mutex clock_lock; 6562306a36Sopenharmony_ci struct list_head devices; 6662306a36Sopenharmony_ci struct sm501_gpio gpio; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci struct device *dev; 6962306a36Sopenharmony_ci struct resource *io_res; 7062306a36Sopenharmony_ci struct resource *mem_res; 7162306a36Sopenharmony_ci struct resource *regs_claim; 7262306a36Sopenharmony_ci struct sm501_platdata *platdata; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci unsigned int in_suspend; 7662306a36Sopenharmony_ci unsigned long pm_misc; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci int unit_power[20]; 7962306a36Sopenharmony_ci unsigned int pdev_id; 8062306a36Sopenharmony_ci unsigned int irq; 8162306a36Sopenharmony_ci void __iomem *regs; 8262306a36Sopenharmony_ci unsigned int rev; 8362306a36Sopenharmony_ci}; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci#define MHZ (1000 * 1000) 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci#ifdef DEBUG 8962306a36Sopenharmony_cistatic const unsigned int div_tab[] = { 9062306a36Sopenharmony_ci [0] = 1, 9162306a36Sopenharmony_ci [1] = 2, 9262306a36Sopenharmony_ci [2] = 4, 9362306a36Sopenharmony_ci [3] = 8, 9462306a36Sopenharmony_ci [4] = 16, 9562306a36Sopenharmony_ci [5] = 32, 9662306a36Sopenharmony_ci [6] = 64, 9762306a36Sopenharmony_ci [7] = 128, 9862306a36Sopenharmony_ci [8] = 3, 9962306a36Sopenharmony_ci [9] = 6, 10062306a36Sopenharmony_ci [10] = 12, 10162306a36Sopenharmony_ci [11] = 24, 10262306a36Sopenharmony_ci [12] = 48, 10362306a36Sopenharmony_ci [13] = 96, 10462306a36Sopenharmony_ci [14] = 192, 10562306a36Sopenharmony_ci [15] = 384, 10662306a36Sopenharmony_ci [16] = 5, 10762306a36Sopenharmony_ci [17] = 10, 10862306a36Sopenharmony_ci [18] = 20, 10962306a36Sopenharmony_ci [19] = 40, 11062306a36Sopenharmony_ci [20] = 80, 11162306a36Sopenharmony_ci [21] = 160, 11262306a36Sopenharmony_ci [22] = 320, 11362306a36Sopenharmony_ci [23] = 604, 11462306a36Sopenharmony_ci}; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic unsigned long decode_div(unsigned long pll2, unsigned long val, 11762306a36Sopenharmony_ci unsigned int lshft, unsigned int selbit, 11862306a36Sopenharmony_ci unsigned long mask) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci if (val & selbit) 12162306a36Sopenharmony_ci pll2 = 288 * MHZ; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci return pll2 / div_tab[(val >> lshft) & mask]; 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci#define fmt_freq(x) ((x) / MHZ), ((x) % MHZ), (x) 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci/* sm501_dump_clk 12962306a36Sopenharmony_ci * 13062306a36Sopenharmony_ci * Print out the current clock configuration for the device 13162306a36Sopenharmony_ci*/ 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic void sm501_dump_clk(struct sm501_devdata *sm) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci unsigned long misct = smc501_readl(sm->regs + SM501_MISC_TIMING); 13662306a36Sopenharmony_ci unsigned long pm0 = smc501_readl(sm->regs + SM501_POWER_MODE_0_CLOCK); 13762306a36Sopenharmony_ci unsigned long pm1 = smc501_readl(sm->regs + SM501_POWER_MODE_1_CLOCK); 13862306a36Sopenharmony_ci unsigned long pmc = smc501_readl(sm->regs + SM501_POWER_MODE_CONTROL); 13962306a36Sopenharmony_ci unsigned long sdclk0, sdclk1; 14062306a36Sopenharmony_ci unsigned long pll2 = 0; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci switch (misct & 0x30) { 14362306a36Sopenharmony_ci case 0x00: 14462306a36Sopenharmony_ci pll2 = 336 * MHZ; 14562306a36Sopenharmony_ci break; 14662306a36Sopenharmony_ci case 0x10: 14762306a36Sopenharmony_ci pll2 = 288 * MHZ; 14862306a36Sopenharmony_ci break; 14962306a36Sopenharmony_ci case 0x20: 15062306a36Sopenharmony_ci pll2 = 240 * MHZ; 15162306a36Sopenharmony_ci break; 15262306a36Sopenharmony_ci case 0x30: 15362306a36Sopenharmony_ci pll2 = 192 * MHZ; 15462306a36Sopenharmony_ci break; 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci sdclk0 = (misct & (1<<12)) ? pll2 : 288 * MHZ; 15862306a36Sopenharmony_ci sdclk0 /= div_tab[((misct >> 8) & 0xf)]; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci sdclk1 = (misct & (1<<20)) ? pll2 : 288 * MHZ; 16162306a36Sopenharmony_ci sdclk1 /= div_tab[((misct >> 16) & 0xf)]; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci dev_dbg(sm->dev, "MISCT=%08lx, PM0=%08lx, PM1=%08lx\n", 16462306a36Sopenharmony_ci misct, pm0, pm1); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci dev_dbg(sm->dev, "PLL2 = %ld.%ld MHz (%ld), SDCLK0=%08lx, SDCLK1=%08lx\n", 16762306a36Sopenharmony_ci fmt_freq(pll2), sdclk0, sdclk1); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci dev_dbg(sm->dev, "SDRAM: PM0=%ld, PM1=%ld\n", sdclk0, sdclk1); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci dev_dbg(sm->dev, "PM0[%c]: " 17262306a36Sopenharmony_ci "P2 %ld.%ld MHz (%ld), V2 %ld.%ld (%ld), " 17362306a36Sopenharmony_ci "M %ld.%ld (%ld), MX1 %ld.%ld (%ld)\n", 17462306a36Sopenharmony_ci (pmc & 3 ) == 0 ? '*' : '-', 17562306a36Sopenharmony_ci fmt_freq(decode_div(pll2, pm0, 24, 1<<29, 31)), 17662306a36Sopenharmony_ci fmt_freq(decode_div(pll2, pm0, 16, 1<<20, 15)), 17762306a36Sopenharmony_ci fmt_freq(decode_div(pll2, pm0, 8, 1<<12, 15)), 17862306a36Sopenharmony_ci fmt_freq(decode_div(pll2, pm0, 0, 1<<4, 15))); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci dev_dbg(sm->dev, "PM1[%c]: " 18162306a36Sopenharmony_ci "P2 %ld.%ld MHz (%ld), V2 %ld.%ld (%ld), " 18262306a36Sopenharmony_ci "M %ld.%ld (%ld), MX1 %ld.%ld (%ld)\n", 18362306a36Sopenharmony_ci (pmc & 3 ) == 1 ? '*' : '-', 18462306a36Sopenharmony_ci fmt_freq(decode_div(pll2, pm1, 24, 1<<29, 31)), 18562306a36Sopenharmony_ci fmt_freq(decode_div(pll2, pm1, 16, 1<<20, 15)), 18662306a36Sopenharmony_ci fmt_freq(decode_div(pll2, pm1, 8, 1<<12, 15)), 18762306a36Sopenharmony_ci fmt_freq(decode_div(pll2, pm1, 0, 1<<4, 15))); 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic void sm501_dump_regs(struct sm501_devdata *sm) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci void __iomem *regs = sm->regs; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci dev_info(sm->dev, "System Control %08x\n", 19562306a36Sopenharmony_ci smc501_readl(regs + SM501_SYSTEM_CONTROL)); 19662306a36Sopenharmony_ci dev_info(sm->dev, "Misc Control %08x\n", 19762306a36Sopenharmony_ci smc501_readl(regs + SM501_MISC_CONTROL)); 19862306a36Sopenharmony_ci dev_info(sm->dev, "GPIO Control Low %08x\n", 19962306a36Sopenharmony_ci smc501_readl(regs + SM501_GPIO31_0_CONTROL)); 20062306a36Sopenharmony_ci dev_info(sm->dev, "GPIO Control Hi %08x\n", 20162306a36Sopenharmony_ci smc501_readl(regs + SM501_GPIO63_32_CONTROL)); 20262306a36Sopenharmony_ci dev_info(sm->dev, "DRAM Control %08x\n", 20362306a36Sopenharmony_ci smc501_readl(regs + SM501_DRAM_CONTROL)); 20462306a36Sopenharmony_ci dev_info(sm->dev, "Arbitration Ctrl %08x\n", 20562306a36Sopenharmony_ci smc501_readl(regs + SM501_ARBTRTN_CONTROL)); 20662306a36Sopenharmony_ci dev_info(sm->dev, "Misc Timing %08x\n", 20762306a36Sopenharmony_ci smc501_readl(regs + SM501_MISC_TIMING)); 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic void sm501_dump_gate(struct sm501_devdata *sm) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci dev_info(sm->dev, "CurrentGate %08x\n", 21362306a36Sopenharmony_ci smc501_readl(sm->regs + SM501_CURRENT_GATE)); 21462306a36Sopenharmony_ci dev_info(sm->dev, "CurrentClock %08x\n", 21562306a36Sopenharmony_ci smc501_readl(sm->regs + SM501_CURRENT_CLOCK)); 21662306a36Sopenharmony_ci dev_info(sm->dev, "PowerModeControl %08x\n", 21762306a36Sopenharmony_ci smc501_readl(sm->regs + SM501_POWER_MODE_CONTROL)); 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci#else 22162306a36Sopenharmony_cistatic inline void sm501_dump_gate(struct sm501_devdata *sm) { } 22262306a36Sopenharmony_cistatic inline void sm501_dump_regs(struct sm501_devdata *sm) { } 22362306a36Sopenharmony_cistatic inline void sm501_dump_clk(struct sm501_devdata *sm) { } 22462306a36Sopenharmony_ci#endif 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci/* sm501_sync_regs 22762306a36Sopenharmony_ci * 22862306a36Sopenharmony_ci * ensure the 22962306a36Sopenharmony_ci*/ 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic void sm501_sync_regs(struct sm501_devdata *sm) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci smc501_readl(sm->regs); 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic inline void sm501_mdelay(struct sm501_devdata *sm, unsigned int delay) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci /* during suspend/resume, we are currently not allowed to sleep, 23962306a36Sopenharmony_ci * so change to using mdelay() instead of msleep() if we 24062306a36Sopenharmony_ci * are in one of these paths */ 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (sm->in_suspend) 24362306a36Sopenharmony_ci mdelay(delay); 24462306a36Sopenharmony_ci else 24562306a36Sopenharmony_ci msleep(delay); 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci/* sm501_misc_control 24962306a36Sopenharmony_ci * 25062306a36Sopenharmony_ci * alters the miscellaneous control parameters 25162306a36Sopenharmony_ci*/ 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ciint sm501_misc_control(struct device *dev, 25462306a36Sopenharmony_ci unsigned long set, unsigned long clear) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci struct sm501_devdata *sm = dev_get_drvdata(dev); 25762306a36Sopenharmony_ci unsigned long misc; 25862306a36Sopenharmony_ci unsigned long save; 25962306a36Sopenharmony_ci unsigned long to; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci spin_lock_irqsave(&sm->reg_lock, save); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci misc = smc501_readl(sm->regs + SM501_MISC_CONTROL); 26462306a36Sopenharmony_ci to = (misc & ~clear) | set; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if (to != misc) { 26762306a36Sopenharmony_ci smc501_writel(to, sm->regs + SM501_MISC_CONTROL); 26862306a36Sopenharmony_ci sm501_sync_regs(sm); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci dev_dbg(sm->dev, "MISC_CONTROL %08lx\n", misc); 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci spin_unlock_irqrestore(&sm->reg_lock, save); 27462306a36Sopenharmony_ci return to; 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sm501_misc_control); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci/* sm501_modify_reg 28062306a36Sopenharmony_ci * 28162306a36Sopenharmony_ci * Modify a register in the SM501 which may be shared with other 28262306a36Sopenharmony_ci * drivers. 28362306a36Sopenharmony_ci*/ 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ciunsigned long sm501_modify_reg(struct device *dev, 28662306a36Sopenharmony_ci unsigned long reg, 28762306a36Sopenharmony_ci unsigned long set, 28862306a36Sopenharmony_ci unsigned long clear) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci struct sm501_devdata *sm = dev_get_drvdata(dev); 29162306a36Sopenharmony_ci unsigned long data; 29262306a36Sopenharmony_ci unsigned long save; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci spin_lock_irqsave(&sm->reg_lock, save); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci data = smc501_readl(sm->regs + reg); 29762306a36Sopenharmony_ci data |= set; 29862306a36Sopenharmony_ci data &= ~clear; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci smc501_writel(data, sm->regs + reg); 30162306a36Sopenharmony_ci sm501_sync_regs(sm); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci spin_unlock_irqrestore(&sm->reg_lock, save); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci return data; 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sm501_modify_reg); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci/* sm501_unit_power 31162306a36Sopenharmony_ci * 31262306a36Sopenharmony_ci * alters the power active gate to set specific units on or off 31362306a36Sopenharmony_ci */ 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ciint sm501_unit_power(struct device *dev, unsigned int unit, unsigned int to) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci struct sm501_devdata *sm = dev_get_drvdata(dev); 31862306a36Sopenharmony_ci unsigned long mode; 31962306a36Sopenharmony_ci unsigned long gate; 32062306a36Sopenharmony_ci unsigned long clock; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci mutex_lock(&sm->clock_lock); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci mode = smc501_readl(sm->regs + SM501_POWER_MODE_CONTROL); 32562306a36Sopenharmony_ci gate = smc501_readl(sm->regs + SM501_CURRENT_GATE); 32662306a36Sopenharmony_ci clock = smc501_readl(sm->regs + SM501_CURRENT_CLOCK); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci mode &= 3; /* get current power mode */ 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (unit >= ARRAY_SIZE(sm->unit_power)) { 33162306a36Sopenharmony_ci dev_err(dev, "%s: bad unit %d\n", __func__, unit); 33262306a36Sopenharmony_ci goto already; 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci dev_dbg(sm->dev, "%s: unit %d, cur %d, to %d\n", __func__, unit, 33662306a36Sopenharmony_ci sm->unit_power[unit], to); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci if (to == 0 && sm->unit_power[unit] == 0) { 33962306a36Sopenharmony_ci dev_err(sm->dev, "unit %d is already shutdown\n", unit); 34062306a36Sopenharmony_ci goto already; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci sm->unit_power[unit] += to ? 1 : -1; 34462306a36Sopenharmony_ci to = sm->unit_power[unit] ? 1 : 0; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci if (to) { 34762306a36Sopenharmony_ci if (gate & (1 << unit)) 34862306a36Sopenharmony_ci goto already; 34962306a36Sopenharmony_ci gate |= (1 << unit); 35062306a36Sopenharmony_ci } else { 35162306a36Sopenharmony_ci if (!(gate & (1 << unit))) 35262306a36Sopenharmony_ci goto already; 35362306a36Sopenharmony_ci gate &= ~(1 << unit); 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci switch (mode) { 35762306a36Sopenharmony_ci case 1: 35862306a36Sopenharmony_ci smc501_writel(gate, sm->regs + SM501_POWER_MODE_0_GATE); 35962306a36Sopenharmony_ci smc501_writel(clock, sm->regs + SM501_POWER_MODE_0_CLOCK); 36062306a36Sopenharmony_ci mode = 0; 36162306a36Sopenharmony_ci break; 36262306a36Sopenharmony_ci case 2: 36362306a36Sopenharmony_ci case 0: 36462306a36Sopenharmony_ci smc501_writel(gate, sm->regs + SM501_POWER_MODE_1_GATE); 36562306a36Sopenharmony_ci smc501_writel(clock, sm->regs + SM501_POWER_MODE_1_CLOCK); 36662306a36Sopenharmony_ci mode = 1; 36762306a36Sopenharmony_ci break; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci default: 37062306a36Sopenharmony_ci gate = -1; 37162306a36Sopenharmony_ci goto already; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci smc501_writel(mode, sm->regs + SM501_POWER_MODE_CONTROL); 37562306a36Sopenharmony_ci sm501_sync_regs(sm); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci dev_dbg(sm->dev, "gate %08lx, clock %08lx, mode %08lx\n", 37862306a36Sopenharmony_ci gate, clock, mode); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci sm501_mdelay(sm, 16); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci already: 38362306a36Sopenharmony_ci mutex_unlock(&sm->clock_lock); 38462306a36Sopenharmony_ci return gate; 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sm501_unit_power); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci/* clock value structure. */ 39062306a36Sopenharmony_cistruct sm501_clock { 39162306a36Sopenharmony_ci unsigned long mclk; 39262306a36Sopenharmony_ci int divider; 39362306a36Sopenharmony_ci int shift; 39462306a36Sopenharmony_ci unsigned int m, n, k; 39562306a36Sopenharmony_ci}; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci/* sm501_calc_clock 39862306a36Sopenharmony_ci * 39962306a36Sopenharmony_ci * Calculates the nearest discrete clock frequency that 40062306a36Sopenharmony_ci * can be achieved with the specified input clock. 40162306a36Sopenharmony_ci * the maximum divisor is 3 or 5 40262306a36Sopenharmony_ci */ 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_cistatic int sm501_calc_clock(unsigned long freq, 40562306a36Sopenharmony_ci struct sm501_clock *clock, 40662306a36Sopenharmony_ci int max_div, 40762306a36Sopenharmony_ci unsigned long mclk, 40862306a36Sopenharmony_ci long *best_diff) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci int ret = 0; 41162306a36Sopenharmony_ci int divider; 41262306a36Sopenharmony_ci int shift; 41362306a36Sopenharmony_ci long diff; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci /* try dividers 1 and 3 for CRT and for panel, 41662306a36Sopenharmony_ci try divider 5 for panel only.*/ 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci for (divider = 1; divider <= max_div; divider += 2) { 41962306a36Sopenharmony_ci /* try all 8 shift values.*/ 42062306a36Sopenharmony_ci for (shift = 0; shift < 8; shift++) { 42162306a36Sopenharmony_ci /* Calculate difference to requested clock */ 42262306a36Sopenharmony_ci diff = DIV_ROUND_CLOSEST(mclk, divider << shift) - freq; 42362306a36Sopenharmony_ci if (diff < 0) 42462306a36Sopenharmony_ci diff = -diff; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci /* If it is less than the current, use it */ 42762306a36Sopenharmony_ci if (diff < *best_diff) { 42862306a36Sopenharmony_ci *best_diff = diff; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci clock->mclk = mclk; 43162306a36Sopenharmony_ci clock->divider = divider; 43262306a36Sopenharmony_ci clock->shift = shift; 43362306a36Sopenharmony_ci ret = 1; 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci return ret; 43962306a36Sopenharmony_ci} 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci/* sm501_calc_pll 44262306a36Sopenharmony_ci * 44362306a36Sopenharmony_ci * Calculates the nearest discrete clock frequency that can be 44462306a36Sopenharmony_ci * achieved using the programmable PLL. 44562306a36Sopenharmony_ci * the maximum divisor is 3 or 5 44662306a36Sopenharmony_ci */ 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cistatic unsigned long sm501_calc_pll(unsigned long freq, 44962306a36Sopenharmony_ci struct sm501_clock *clock, 45062306a36Sopenharmony_ci int max_div) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci unsigned long mclk; 45362306a36Sopenharmony_ci unsigned int m, n, k; 45462306a36Sopenharmony_ci long best_diff = 999999999; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci /* 45762306a36Sopenharmony_ci * The SM502 datasheet doesn't specify the min/max values for M and N. 45862306a36Sopenharmony_ci * N = 1 at least doesn't work in practice. 45962306a36Sopenharmony_ci */ 46062306a36Sopenharmony_ci for (m = 2; m <= 255; m++) { 46162306a36Sopenharmony_ci for (n = 2; n <= 127; n++) { 46262306a36Sopenharmony_ci for (k = 0; k <= 1; k++) { 46362306a36Sopenharmony_ci mclk = (24000000UL * m / n) >> k; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci if (sm501_calc_clock(freq, clock, max_div, 46662306a36Sopenharmony_ci mclk, &best_diff)) { 46762306a36Sopenharmony_ci clock->m = m; 46862306a36Sopenharmony_ci clock->n = n; 46962306a36Sopenharmony_ci clock->k = k; 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci /* Return best clock. */ 47662306a36Sopenharmony_ci return clock->mclk / (clock->divider << clock->shift); 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci/* sm501_select_clock 48062306a36Sopenharmony_ci * 48162306a36Sopenharmony_ci * Calculates the nearest discrete clock frequency that can be 48262306a36Sopenharmony_ci * achieved using the 288MHz and 336MHz PLLs. 48362306a36Sopenharmony_ci * the maximum divisor is 3 or 5 48462306a36Sopenharmony_ci */ 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_cistatic unsigned long sm501_select_clock(unsigned long freq, 48762306a36Sopenharmony_ci struct sm501_clock *clock, 48862306a36Sopenharmony_ci int max_div) 48962306a36Sopenharmony_ci{ 49062306a36Sopenharmony_ci unsigned long mclk; 49162306a36Sopenharmony_ci long best_diff = 999999999; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci /* Try 288MHz and 336MHz clocks. */ 49462306a36Sopenharmony_ci for (mclk = 288000000; mclk <= 336000000; mclk += 48000000) { 49562306a36Sopenharmony_ci sm501_calc_clock(freq, clock, max_div, mclk, &best_diff); 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci /* Return best clock. */ 49962306a36Sopenharmony_ci return clock->mclk / (clock->divider << clock->shift); 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci/* sm501_set_clock 50362306a36Sopenharmony_ci * 50462306a36Sopenharmony_ci * set one of the four clock sources to the closest available frequency to 50562306a36Sopenharmony_ci * the one specified 50662306a36Sopenharmony_ci*/ 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ciunsigned long sm501_set_clock(struct device *dev, 50962306a36Sopenharmony_ci int clksrc, 51062306a36Sopenharmony_ci unsigned long req_freq) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci struct sm501_devdata *sm = dev_get_drvdata(dev); 51362306a36Sopenharmony_ci unsigned long mode = smc501_readl(sm->regs + SM501_POWER_MODE_CONTROL); 51462306a36Sopenharmony_ci unsigned long gate = smc501_readl(sm->regs + SM501_CURRENT_GATE); 51562306a36Sopenharmony_ci unsigned long clock = smc501_readl(sm->regs + SM501_CURRENT_CLOCK); 51662306a36Sopenharmony_ci unsigned int pll_reg = 0; 51762306a36Sopenharmony_ci unsigned long sm501_freq; /* the actual frequency achieved */ 51862306a36Sopenharmony_ci u64 reg; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci struct sm501_clock to; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci /* find achivable discrete frequency and setup register value 52362306a36Sopenharmony_ci * accordingly, V2XCLK, MCLK and M1XCLK are the same P2XCLK 52462306a36Sopenharmony_ci * has an extra bit for the divider */ 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci switch (clksrc) { 52762306a36Sopenharmony_ci case SM501_CLOCK_P2XCLK: 52862306a36Sopenharmony_ci /* This clock is divided in half so to achieve the 52962306a36Sopenharmony_ci * requested frequency the value must be multiplied by 53062306a36Sopenharmony_ci * 2. This clock also has an additional pre divisor */ 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci if (sm->rev >= 0xC0) { 53362306a36Sopenharmony_ci /* SM502 -> use the programmable PLL */ 53462306a36Sopenharmony_ci sm501_freq = (sm501_calc_pll(2 * req_freq, 53562306a36Sopenharmony_ci &to, 5) / 2); 53662306a36Sopenharmony_ci reg = to.shift & 0x07;/* bottom 3 bits are shift */ 53762306a36Sopenharmony_ci if (to.divider == 3) 53862306a36Sopenharmony_ci reg |= 0x08; /* /3 divider required */ 53962306a36Sopenharmony_ci else if (to.divider == 5) 54062306a36Sopenharmony_ci reg |= 0x10; /* /5 divider required */ 54162306a36Sopenharmony_ci reg |= 0x40; /* select the programmable PLL */ 54262306a36Sopenharmony_ci pll_reg = 0x20000 | (to.k << 15) | (to.n << 8) | to.m; 54362306a36Sopenharmony_ci } else { 54462306a36Sopenharmony_ci sm501_freq = (sm501_select_clock(2 * req_freq, 54562306a36Sopenharmony_ci &to, 5) / 2); 54662306a36Sopenharmony_ci reg = to.shift & 0x07;/* bottom 3 bits are shift */ 54762306a36Sopenharmony_ci if (to.divider == 3) 54862306a36Sopenharmony_ci reg |= 0x08; /* /3 divider required */ 54962306a36Sopenharmony_ci else if (to.divider == 5) 55062306a36Sopenharmony_ci reg |= 0x10; /* /5 divider required */ 55162306a36Sopenharmony_ci if (to.mclk != 288000000) 55262306a36Sopenharmony_ci reg |= 0x20; /* which mclk pll is source */ 55362306a36Sopenharmony_ci } 55462306a36Sopenharmony_ci break; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci case SM501_CLOCK_V2XCLK: 55762306a36Sopenharmony_ci /* This clock is divided in half so to achieve the 55862306a36Sopenharmony_ci * requested frequency the value must be multiplied by 2. */ 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci sm501_freq = (sm501_select_clock(2 * req_freq, &to, 3) / 2); 56162306a36Sopenharmony_ci reg=to.shift & 0x07; /* bottom 3 bits are shift */ 56262306a36Sopenharmony_ci if (to.divider == 3) 56362306a36Sopenharmony_ci reg |= 0x08; /* /3 divider required */ 56462306a36Sopenharmony_ci if (to.mclk != 288000000) 56562306a36Sopenharmony_ci reg |= 0x10; /* which mclk pll is source */ 56662306a36Sopenharmony_ci break; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci case SM501_CLOCK_MCLK: 56962306a36Sopenharmony_ci case SM501_CLOCK_M1XCLK: 57062306a36Sopenharmony_ci /* These clocks are the same and not further divided */ 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci sm501_freq = sm501_select_clock( req_freq, &to, 3); 57362306a36Sopenharmony_ci reg=to.shift & 0x07; /* bottom 3 bits are shift */ 57462306a36Sopenharmony_ci if (to.divider == 3) 57562306a36Sopenharmony_ci reg |= 0x08; /* /3 divider required */ 57662306a36Sopenharmony_ci if (to.mclk != 288000000) 57762306a36Sopenharmony_ci reg |= 0x10; /* which mclk pll is source */ 57862306a36Sopenharmony_ci break; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci default: 58162306a36Sopenharmony_ci return 0; /* this is bad */ 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci mutex_lock(&sm->clock_lock); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci mode = smc501_readl(sm->regs + SM501_POWER_MODE_CONTROL); 58762306a36Sopenharmony_ci gate = smc501_readl(sm->regs + SM501_CURRENT_GATE); 58862306a36Sopenharmony_ci clock = smc501_readl(sm->regs + SM501_CURRENT_CLOCK); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci clock = clock & ~(0xFF << clksrc); 59162306a36Sopenharmony_ci clock |= reg<<clksrc; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci mode &= 3; /* find current mode */ 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci switch (mode) { 59662306a36Sopenharmony_ci case 1: 59762306a36Sopenharmony_ci smc501_writel(gate, sm->regs + SM501_POWER_MODE_0_GATE); 59862306a36Sopenharmony_ci smc501_writel(clock, sm->regs + SM501_POWER_MODE_0_CLOCK); 59962306a36Sopenharmony_ci mode = 0; 60062306a36Sopenharmony_ci break; 60162306a36Sopenharmony_ci case 2: 60262306a36Sopenharmony_ci case 0: 60362306a36Sopenharmony_ci smc501_writel(gate, sm->regs + SM501_POWER_MODE_1_GATE); 60462306a36Sopenharmony_ci smc501_writel(clock, sm->regs + SM501_POWER_MODE_1_CLOCK); 60562306a36Sopenharmony_ci mode = 1; 60662306a36Sopenharmony_ci break; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci default: 60962306a36Sopenharmony_ci mutex_unlock(&sm->clock_lock); 61062306a36Sopenharmony_ci return -1; 61162306a36Sopenharmony_ci } 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci smc501_writel(mode, sm->regs + SM501_POWER_MODE_CONTROL); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci if (pll_reg) 61662306a36Sopenharmony_ci smc501_writel(pll_reg, 61762306a36Sopenharmony_ci sm->regs + SM501_PROGRAMMABLE_PLL_CONTROL); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci sm501_sync_regs(sm); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci dev_dbg(sm->dev, "gate %08lx, clock %08lx, mode %08lx\n", 62262306a36Sopenharmony_ci gate, clock, mode); 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci sm501_mdelay(sm, 16); 62562306a36Sopenharmony_ci mutex_unlock(&sm->clock_lock); 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci sm501_dump_clk(sm); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci return sm501_freq; 63062306a36Sopenharmony_ci} 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sm501_set_clock); 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci/* sm501_find_clock 63562306a36Sopenharmony_ci * 63662306a36Sopenharmony_ci * finds the closest available frequency for a given clock 63762306a36Sopenharmony_ci*/ 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ciunsigned long sm501_find_clock(struct device *dev, 64062306a36Sopenharmony_ci int clksrc, 64162306a36Sopenharmony_ci unsigned long req_freq) 64262306a36Sopenharmony_ci{ 64362306a36Sopenharmony_ci struct sm501_devdata *sm = dev_get_drvdata(dev); 64462306a36Sopenharmony_ci unsigned long sm501_freq; /* the frequency achieveable by the 501 */ 64562306a36Sopenharmony_ci struct sm501_clock to; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci switch (clksrc) { 64862306a36Sopenharmony_ci case SM501_CLOCK_P2XCLK: 64962306a36Sopenharmony_ci if (sm->rev >= 0xC0) { 65062306a36Sopenharmony_ci /* SM502 -> use the programmable PLL */ 65162306a36Sopenharmony_ci sm501_freq = (sm501_calc_pll(2 * req_freq, 65262306a36Sopenharmony_ci &to, 5) / 2); 65362306a36Sopenharmony_ci } else { 65462306a36Sopenharmony_ci sm501_freq = (sm501_select_clock(2 * req_freq, 65562306a36Sopenharmony_ci &to, 5) / 2); 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci break; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci case SM501_CLOCK_V2XCLK: 66062306a36Sopenharmony_ci sm501_freq = (sm501_select_clock(2 * req_freq, &to, 3) / 2); 66162306a36Sopenharmony_ci break; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci case SM501_CLOCK_MCLK: 66462306a36Sopenharmony_ci case SM501_CLOCK_M1XCLK: 66562306a36Sopenharmony_ci sm501_freq = sm501_select_clock(req_freq, &to, 3); 66662306a36Sopenharmony_ci break; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci default: 66962306a36Sopenharmony_ci sm501_freq = 0; /* error */ 67062306a36Sopenharmony_ci } 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci return sm501_freq; 67362306a36Sopenharmony_ci} 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sm501_find_clock); 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_cistatic struct sm501_device *to_sm_device(struct platform_device *pdev) 67862306a36Sopenharmony_ci{ 67962306a36Sopenharmony_ci return container_of(pdev, struct sm501_device, pdev); 68062306a36Sopenharmony_ci} 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci/* sm501_device_release 68362306a36Sopenharmony_ci * 68462306a36Sopenharmony_ci * A release function for the platform devices we create to allow us to 68562306a36Sopenharmony_ci * free any items we allocated 68662306a36Sopenharmony_ci*/ 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_cistatic void sm501_device_release(struct device *dev) 68962306a36Sopenharmony_ci{ 69062306a36Sopenharmony_ci kfree(to_sm_device(to_platform_device(dev))); 69162306a36Sopenharmony_ci} 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci/* sm501_create_subdev 69462306a36Sopenharmony_ci * 69562306a36Sopenharmony_ci * Create a skeleton platform device with resources for passing to a 69662306a36Sopenharmony_ci * sub-driver 69762306a36Sopenharmony_ci*/ 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_cistatic struct platform_device * 70062306a36Sopenharmony_cism501_create_subdev(struct sm501_devdata *sm, char *name, 70162306a36Sopenharmony_ci unsigned int res_count, unsigned int platform_data_size) 70262306a36Sopenharmony_ci{ 70362306a36Sopenharmony_ci struct sm501_device *smdev; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci smdev = kzalloc(sizeof(struct sm501_device) + 70662306a36Sopenharmony_ci (sizeof(struct resource) * res_count) + 70762306a36Sopenharmony_ci platform_data_size, GFP_KERNEL); 70862306a36Sopenharmony_ci if (!smdev) 70962306a36Sopenharmony_ci return NULL; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci smdev->pdev.dev.release = sm501_device_release; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci smdev->pdev.name = name; 71462306a36Sopenharmony_ci smdev->pdev.id = sm->pdev_id; 71562306a36Sopenharmony_ci smdev->pdev.dev.parent = sm->dev; 71662306a36Sopenharmony_ci smdev->pdev.dev.coherent_dma_mask = 0xffffffff; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci if (res_count) { 71962306a36Sopenharmony_ci smdev->pdev.resource = (struct resource *)(smdev+1); 72062306a36Sopenharmony_ci smdev->pdev.num_resources = res_count; 72162306a36Sopenharmony_ci } 72262306a36Sopenharmony_ci if (platform_data_size) 72362306a36Sopenharmony_ci smdev->pdev.dev.platform_data = (void *)(smdev+1); 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci return &smdev->pdev; 72662306a36Sopenharmony_ci} 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci/* sm501_register_device 72962306a36Sopenharmony_ci * 73062306a36Sopenharmony_ci * Register a platform device created with sm501_create_subdev() 73162306a36Sopenharmony_ci*/ 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_cistatic int sm501_register_device(struct sm501_devdata *sm, 73462306a36Sopenharmony_ci struct platform_device *pdev) 73562306a36Sopenharmony_ci{ 73662306a36Sopenharmony_ci struct sm501_device *smdev = to_sm_device(pdev); 73762306a36Sopenharmony_ci int ptr; 73862306a36Sopenharmony_ci int ret; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci for (ptr = 0; ptr < pdev->num_resources; ptr++) { 74162306a36Sopenharmony_ci printk(KERN_DEBUG "%s[%d] %pR\n", 74262306a36Sopenharmony_ci pdev->name, ptr, &pdev->resource[ptr]); 74362306a36Sopenharmony_ci } 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci ret = platform_device_register(pdev); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci if (ret >= 0) { 74862306a36Sopenharmony_ci dev_dbg(sm->dev, "registered %s\n", pdev->name); 74962306a36Sopenharmony_ci list_add_tail(&smdev->list, &sm->devices); 75062306a36Sopenharmony_ci } else 75162306a36Sopenharmony_ci dev_err(sm->dev, "error registering %s (%d)\n", 75262306a36Sopenharmony_ci pdev->name, ret); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci return ret; 75562306a36Sopenharmony_ci} 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci/* sm501_create_subio 75862306a36Sopenharmony_ci * 75962306a36Sopenharmony_ci * Fill in an IO resource for a sub device 76062306a36Sopenharmony_ci*/ 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_cistatic void sm501_create_subio(struct sm501_devdata *sm, 76362306a36Sopenharmony_ci struct resource *res, 76462306a36Sopenharmony_ci resource_size_t offs, 76562306a36Sopenharmony_ci resource_size_t size) 76662306a36Sopenharmony_ci{ 76762306a36Sopenharmony_ci res->flags = IORESOURCE_MEM; 76862306a36Sopenharmony_ci res->parent = sm->io_res; 76962306a36Sopenharmony_ci res->start = sm->io_res->start + offs; 77062306a36Sopenharmony_ci res->end = res->start + size - 1; 77162306a36Sopenharmony_ci} 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci/* sm501_create_mem 77462306a36Sopenharmony_ci * 77562306a36Sopenharmony_ci * Fill in an MEM resource for a sub device 77662306a36Sopenharmony_ci*/ 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_cistatic void sm501_create_mem(struct sm501_devdata *sm, 77962306a36Sopenharmony_ci struct resource *res, 78062306a36Sopenharmony_ci resource_size_t *offs, 78162306a36Sopenharmony_ci resource_size_t size) 78262306a36Sopenharmony_ci{ 78362306a36Sopenharmony_ci *offs -= size; /* adjust memory size */ 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci res->flags = IORESOURCE_MEM; 78662306a36Sopenharmony_ci res->parent = sm->mem_res; 78762306a36Sopenharmony_ci res->start = sm->mem_res->start + *offs; 78862306a36Sopenharmony_ci res->end = res->start + size - 1; 78962306a36Sopenharmony_ci} 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci/* sm501_create_irq 79262306a36Sopenharmony_ci * 79362306a36Sopenharmony_ci * Fill in an IRQ resource for a sub device 79462306a36Sopenharmony_ci*/ 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_cistatic void sm501_create_irq(struct sm501_devdata *sm, 79762306a36Sopenharmony_ci struct resource *res) 79862306a36Sopenharmony_ci{ 79962306a36Sopenharmony_ci res->flags = IORESOURCE_IRQ; 80062306a36Sopenharmony_ci res->parent = NULL; 80162306a36Sopenharmony_ci res->start = res->end = sm->irq; 80262306a36Sopenharmony_ci} 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_cistatic int sm501_register_usbhost(struct sm501_devdata *sm, 80562306a36Sopenharmony_ci resource_size_t *mem_avail) 80662306a36Sopenharmony_ci{ 80762306a36Sopenharmony_ci struct platform_device *pdev; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci pdev = sm501_create_subdev(sm, "sm501-usb", 3, 0); 81062306a36Sopenharmony_ci if (!pdev) 81162306a36Sopenharmony_ci return -ENOMEM; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci sm501_create_subio(sm, &pdev->resource[0], 0x40000, 0x20000); 81462306a36Sopenharmony_ci sm501_create_mem(sm, &pdev->resource[1], mem_avail, 256*1024); 81562306a36Sopenharmony_ci sm501_create_irq(sm, &pdev->resource[2]); 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci return sm501_register_device(sm, pdev); 81862306a36Sopenharmony_ci} 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_cistatic void sm501_setup_uart_data(struct sm501_devdata *sm, 82162306a36Sopenharmony_ci struct plat_serial8250_port *uart_data, 82262306a36Sopenharmony_ci unsigned int offset) 82362306a36Sopenharmony_ci{ 82462306a36Sopenharmony_ci uart_data->membase = sm->regs + offset; 82562306a36Sopenharmony_ci uart_data->mapbase = sm->io_res->start + offset; 82662306a36Sopenharmony_ci uart_data->iotype = UPIO_MEM; 82762306a36Sopenharmony_ci uart_data->irq = sm->irq; 82862306a36Sopenharmony_ci uart_data->flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_SHARE_IRQ; 82962306a36Sopenharmony_ci uart_data->regshift = 2; 83062306a36Sopenharmony_ci uart_data->uartclk = (9600 * 16); 83162306a36Sopenharmony_ci} 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_cistatic int sm501_register_uart(struct sm501_devdata *sm, int devices) 83462306a36Sopenharmony_ci{ 83562306a36Sopenharmony_ci struct platform_device *pdev; 83662306a36Sopenharmony_ci struct plat_serial8250_port *uart_data; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci pdev = sm501_create_subdev(sm, "serial8250", 0, 83962306a36Sopenharmony_ci sizeof(struct plat_serial8250_port) * 3); 84062306a36Sopenharmony_ci if (!pdev) 84162306a36Sopenharmony_ci return -ENOMEM; 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci uart_data = dev_get_platdata(&pdev->dev); 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci if (devices & SM501_USE_UART0) { 84662306a36Sopenharmony_ci sm501_setup_uart_data(sm, uart_data++, 0x30000); 84762306a36Sopenharmony_ci sm501_unit_power(sm->dev, SM501_GATE_UART0, 1); 84862306a36Sopenharmony_ci sm501_modify_reg(sm->dev, SM501_IRQ_MASK, 1 << 12, 0); 84962306a36Sopenharmony_ci sm501_modify_reg(sm->dev, SM501_GPIO63_32_CONTROL, 0x01e0, 0); 85062306a36Sopenharmony_ci } 85162306a36Sopenharmony_ci if (devices & SM501_USE_UART1) { 85262306a36Sopenharmony_ci sm501_setup_uart_data(sm, uart_data++, 0x30020); 85362306a36Sopenharmony_ci sm501_unit_power(sm->dev, SM501_GATE_UART1, 1); 85462306a36Sopenharmony_ci sm501_modify_reg(sm->dev, SM501_IRQ_MASK, 1 << 13, 0); 85562306a36Sopenharmony_ci sm501_modify_reg(sm->dev, SM501_GPIO63_32_CONTROL, 0x1e00, 0); 85662306a36Sopenharmony_ci } 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci pdev->id = PLAT8250_DEV_SM501; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci return sm501_register_device(sm, pdev); 86162306a36Sopenharmony_ci} 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_cistatic int sm501_register_display(struct sm501_devdata *sm, 86462306a36Sopenharmony_ci resource_size_t *mem_avail) 86562306a36Sopenharmony_ci{ 86662306a36Sopenharmony_ci struct platform_device *pdev; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci pdev = sm501_create_subdev(sm, "sm501-fb", 4, 0); 86962306a36Sopenharmony_ci if (!pdev) 87062306a36Sopenharmony_ci return -ENOMEM; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci sm501_create_subio(sm, &pdev->resource[0], 0x80000, 0x10000); 87362306a36Sopenharmony_ci sm501_create_subio(sm, &pdev->resource[1], 0x100000, 0x50000); 87462306a36Sopenharmony_ci sm501_create_mem(sm, &pdev->resource[2], mem_avail, *mem_avail); 87562306a36Sopenharmony_ci sm501_create_irq(sm, &pdev->resource[3]); 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci return sm501_register_device(sm, pdev); 87862306a36Sopenharmony_ci} 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci#ifdef CONFIG_MFD_SM501_GPIO 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_cistatic inline struct sm501_devdata *sm501_gpio_to_dev(struct sm501_gpio *gpio) 88362306a36Sopenharmony_ci{ 88462306a36Sopenharmony_ci return container_of(gpio, struct sm501_devdata, gpio); 88562306a36Sopenharmony_ci} 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_cistatic int sm501_gpio_get(struct gpio_chip *chip, unsigned offset) 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci{ 89062306a36Sopenharmony_ci struct sm501_gpio_chip *smgpio = gpiochip_get_data(chip); 89162306a36Sopenharmony_ci unsigned long result; 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci result = smc501_readl(smgpio->regbase + SM501_GPIO_DATA_LOW); 89462306a36Sopenharmony_ci result >>= offset; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci return result & 1UL; 89762306a36Sopenharmony_ci} 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_cistatic void sm501_gpio_ensure_gpio(struct sm501_gpio_chip *smchip, 90062306a36Sopenharmony_ci unsigned long bit) 90162306a36Sopenharmony_ci{ 90262306a36Sopenharmony_ci unsigned long ctrl; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci /* check and modify if this pin is not set as gpio. */ 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci if (smc501_readl(smchip->control) & bit) { 90762306a36Sopenharmony_ci dev_info(sm501_gpio_to_dev(smchip->ourgpio)->dev, 90862306a36Sopenharmony_ci "changing mode of gpio, bit %08lx\n", bit); 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci ctrl = smc501_readl(smchip->control); 91162306a36Sopenharmony_ci ctrl &= ~bit; 91262306a36Sopenharmony_ci smc501_writel(ctrl, smchip->control); 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci sm501_sync_regs(sm501_gpio_to_dev(smchip->ourgpio)); 91562306a36Sopenharmony_ci } 91662306a36Sopenharmony_ci} 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_cistatic void sm501_gpio_set(struct gpio_chip *chip, unsigned offset, int value) 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci{ 92162306a36Sopenharmony_ci struct sm501_gpio_chip *smchip = gpiochip_get_data(chip); 92262306a36Sopenharmony_ci struct sm501_gpio *smgpio = smchip->ourgpio; 92362306a36Sopenharmony_ci unsigned long bit = 1 << offset; 92462306a36Sopenharmony_ci void __iomem *regs = smchip->regbase; 92562306a36Sopenharmony_ci unsigned long save; 92662306a36Sopenharmony_ci unsigned long val; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci dev_dbg(sm501_gpio_to_dev(smgpio)->dev, "%s(%p,%d)\n", 92962306a36Sopenharmony_ci __func__, chip, offset); 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci spin_lock_irqsave(&smgpio->lock, save); 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci val = smc501_readl(regs + SM501_GPIO_DATA_LOW) & ~bit; 93462306a36Sopenharmony_ci if (value) 93562306a36Sopenharmony_ci val |= bit; 93662306a36Sopenharmony_ci smc501_writel(val, regs); 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci sm501_sync_regs(sm501_gpio_to_dev(smgpio)); 93962306a36Sopenharmony_ci sm501_gpio_ensure_gpio(smchip, bit); 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci spin_unlock_irqrestore(&smgpio->lock, save); 94262306a36Sopenharmony_ci} 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_cistatic int sm501_gpio_input(struct gpio_chip *chip, unsigned offset) 94562306a36Sopenharmony_ci{ 94662306a36Sopenharmony_ci struct sm501_gpio_chip *smchip = gpiochip_get_data(chip); 94762306a36Sopenharmony_ci struct sm501_gpio *smgpio = smchip->ourgpio; 94862306a36Sopenharmony_ci void __iomem *regs = smchip->regbase; 94962306a36Sopenharmony_ci unsigned long bit = 1 << offset; 95062306a36Sopenharmony_ci unsigned long save; 95162306a36Sopenharmony_ci unsigned long ddr; 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci dev_dbg(sm501_gpio_to_dev(smgpio)->dev, "%s(%p,%d)\n", 95462306a36Sopenharmony_ci __func__, chip, offset); 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci spin_lock_irqsave(&smgpio->lock, save); 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci ddr = smc501_readl(regs + SM501_GPIO_DDR_LOW); 95962306a36Sopenharmony_ci smc501_writel(ddr & ~bit, regs + SM501_GPIO_DDR_LOW); 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci sm501_sync_regs(sm501_gpio_to_dev(smgpio)); 96262306a36Sopenharmony_ci sm501_gpio_ensure_gpio(smchip, bit); 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci spin_unlock_irqrestore(&smgpio->lock, save); 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci return 0; 96762306a36Sopenharmony_ci} 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_cistatic int sm501_gpio_output(struct gpio_chip *chip, 97062306a36Sopenharmony_ci unsigned offset, int value) 97162306a36Sopenharmony_ci{ 97262306a36Sopenharmony_ci struct sm501_gpio_chip *smchip = gpiochip_get_data(chip); 97362306a36Sopenharmony_ci struct sm501_gpio *smgpio = smchip->ourgpio; 97462306a36Sopenharmony_ci unsigned long bit = 1 << offset; 97562306a36Sopenharmony_ci void __iomem *regs = smchip->regbase; 97662306a36Sopenharmony_ci unsigned long save; 97762306a36Sopenharmony_ci unsigned long val; 97862306a36Sopenharmony_ci unsigned long ddr; 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci dev_dbg(sm501_gpio_to_dev(smgpio)->dev, "%s(%p,%d,%d)\n", 98162306a36Sopenharmony_ci __func__, chip, offset, value); 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci spin_lock_irqsave(&smgpio->lock, save); 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci val = smc501_readl(regs + SM501_GPIO_DATA_LOW); 98662306a36Sopenharmony_ci if (value) 98762306a36Sopenharmony_ci val |= bit; 98862306a36Sopenharmony_ci else 98962306a36Sopenharmony_ci val &= ~bit; 99062306a36Sopenharmony_ci smc501_writel(val, regs); 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci ddr = smc501_readl(regs + SM501_GPIO_DDR_LOW); 99362306a36Sopenharmony_ci smc501_writel(ddr | bit, regs + SM501_GPIO_DDR_LOW); 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci sm501_sync_regs(sm501_gpio_to_dev(smgpio)); 99662306a36Sopenharmony_ci smc501_writel(val, regs + SM501_GPIO_DATA_LOW); 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci sm501_sync_regs(sm501_gpio_to_dev(smgpio)); 99962306a36Sopenharmony_ci spin_unlock_irqrestore(&smgpio->lock, save); 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci return 0; 100262306a36Sopenharmony_ci} 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_cistatic const struct gpio_chip gpio_chip_template = { 100562306a36Sopenharmony_ci .ngpio = 32, 100662306a36Sopenharmony_ci .direction_input = sm501_gpio_input, 100762306a36Sopenharmony_ci .direction_output = sm501_gpio_output, 100862306a36Sopenharmony_ci .set = sm501_gpio_set, 100962306a36Sopenharmony_ci .get = sm501_gpio_get, 101062306a36Sopenharmony_ci}; 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_cistatic int sm501_gpio_register_chip(struct sm501_devdata *sm, 101362306a36Sopenharmony_ci struct sm501_gpio *gpio, 101462306a36Sopenharmony_ci struct sm501_gpio_chip *chip) 101562306a36Sopenharmony_ci{ 101662306a36Sopenharmony_ci struct sm501_platdata *pdata = sm->platdata; 101762306a36Sopenharmony_ci struct gpio_chip *gchip = &chip->gpio; 101862306a36Sopenharmony_ci int base = pdata->gpio_base; 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci chip->gpio = gpio_chip_template; 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci if (chip == &gpio->high) { 102362306a36Sopenharmony_ci if (base > 0) 102462306a36Sopenharmony_ci base += 32; 102562306a36Sopenharmony_ci chip->regbase = gpio->regs + SM501_GPIO_DATA_HIGH; 102662306a36Sopenharmony_ci chip->control = sm->regs + SM501_GPIO63_32_CONTROL; 102762306a36Sopenharmony_ci gchip->label = "SM501-HIGH"; 102862306a36Sopenharmony_ci } else { 102962306a36Sopenharmony_ci chip->regbase = gpio->regs + SM501_GPIO_DATA_LOW; 103062306a36Sopenharmony_ci chip->control = sm->regs + SM501_GPIO31_0_CONTROL; 103162306a36Sopenharmony_ci gchip->label = "SM501-LOW"; 103262306a36Sopenharmony_ci } 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci gchip->base = base; 103562306a36Sopenharmony_ci chip->ourgpio = gpio; 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci return gpiochip_add_data(gchip, chip); 103862306a36Sopenharmony_ci} 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_cistatic int sm501_register_gpio(struct sm501_devdata *sm) 104162306a36Sopenharmony_ci{ 104262306a36Sopenharmony_ci struct sm501_gpio *gpio = &sm->gpio; 104362306a36Sopenharmony_ci resource_size_t iobase = sm->io_res->start + SM501_GPIO; 104462306a36Sopenharmony_ci int ret; 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci dev_dbg(sm->dev, "registering gpio block %08llx\n", 104762306a36Sopenharmony_ci (unsigned long long)iobase); 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci spin_lock_init(&gpio->lock); 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci gpio->regs_res = request_mem_region(iobase, 0x20, "sm501-gpio"); 105262306a36Sopenharmony_ci if (!gpio->regs_res) { 105362306a36Sopenharmony_ci dev_err(sm->dev, "gpio: failed to request region\n"); 105462306a36Sopenharmony_ci return -ENXIO; 105562306a36Sopenharmony_ci } 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci gpio->regs = ioremap(iobase, 0x20); 105862306a36Sopenharmony_ci if (!gpio->regs) { 105962306a36Sopenharmony_ci dev_err(sm->dev, "gpio: failed to remap registers\n"); 106062306a36Sopenharmony_ci ret = -ENXIO; 106162306a36Sopenharmony_ci goto err_claimed; 106262306a36Sopenharmony_ci } 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci /* Register both our chips. */ 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci ret = sm501_gpio_register_chip(sm, gpio, &gpio->low); 106762306a36Sopenharmony_ci if (ret) { 106862306a36Sopenharmony_ci dev_err(sm->dev, "failed to add low chip\n"); 106962306a36Sopenharmony_ci goto err_mapped; 107062306a36Sopenharmony_ci } 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci ret = sm501_gpio_register_chip(sm, gpio, &gpio->high); 107362306a36Sopenharmony_ci if (ret) { 107462306a36Sopenharmony_ci dev_err(sm->dev, "failed to add high chip\n"); 107562306a36Sopenharmony_ci goto err_low_chip; 107662306a36Sopenharmony_ci } 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci gpio->registered = 1; 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci return 0; 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci err_low_chip: 108362306a36Sopenharmony_ci gpiochip_remove(&gpio->low.gpio); 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci err_mapped: 108662306a36Sopenharmony_ci iounmap(gpio->regs); 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci err_claimed: 108962306a36Sopenharmony_ci release_mem_region(iobase, 0x20); 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci return ret; 109262306a36Sopenharmony_ci} 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_cistatic void sm501_gpio_remove(struct sm501_devdata *sm) 109562306a36Sopenharmony_ci{ 109662306a36Sopenharmony_ci struct sm501_gpio *gpio = &sm->gpio; 109762306a36Sopenharmony_ci resource_size_t iobase = sm->io_res->start + SM501_GPIO; 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci if (!sm->gpio.registered) 110062306a36Sopenharmony_ci return; 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci gpiochip_remove(&gpio->low.gpio); 110362306a36Sopenharmony_ci gpiochip_remove(&gpio->high.gpio); 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci iounmap(gpio->regs); 110662306a36Sopenharmony_ci release_mem_region(iobase, 0x20); 110762306a36Sopenharmony_ci} 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_cistatic inline int sm501_gpio_isregistered(struct sm501_devdata *sm) 111062306a36Sopenharmony_ci{ 111162306a36Sopenharmony_ci return sm->gpio.registered; 111262306a36Sopenharmony_ci} 111362306a36Sopenharmony_ci#else 111462306a36Sopenharmony_cistatic inline int sm501_register_gpio(struct sm501_devdata *sm) 111562306a36Sopenharmony_ci{ 111662306a36Sopenharmony_ci return 0; 111762306a36Sopenharmony_ci} 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_cistatic inline void sm501_gpio_remove(struct sm501_devdata *sm) 112062306a36Sopenharmony_ci{ 112162306a36Sopenharmony_ci} 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_cistatic inline int sm501_gpio_isregistered(struct sm501_devdata *sm) 112462306a36Sopenharmony_ci{ 112562306a36Sopenharmony_ci return 0; 112662306a36Sopenharmony_ci} 112762306a36Sopenharmony_ci#endif 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_cistatic int sm501_register_gpio_i2c_instance(struct sm501_devdata *sm, 113062306a36Sopenharmony_ci struct sm501_platdata_gpio_i2c *iic) 113162306a36Sopenharmony_ci{ 113262306a36Sopenharmony_ci struct i2c_gpio_platform_data *icd; 113362306a36Sopenharmony_ci struct platform_device *pdev; 113462306a36Sopenharmony_ci struct gpiod_lookup_table *lookup; 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci pdev = sm501_create_subdev(sm, "i2c-gpio", 0, 113762306a36Sopenharmony_ci sizeof(struct i2c_gpio_platform_data)); 113862306a36Sopenharmony_ci if (!pdev) 113962306a36Sopenharmony_ci return -ENOMEM; 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci /* Create a gpiod lookup using gpiochip-local offsets */ 114262306a36Sopenharmony_ci lookup = devm_kzalloc(&pdev->dev, struct_size(lookup, table, 3), 114362306a36Sopenharmony_ci GFP_KERNEL); 114462306a36Sopenharmony_ci if (!lookup) 114562306a36Sopenharmony_ci return -ENOMEM; 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci lookup->dev_id = "i2c-gpio"; 114862306a36Sopenharmony_ci lookup->table[0] = (struct gpiod_lookup) 114962306a36Sopenharmony_ci GPIO_LOOKUP_IDX(iic->pin_sda < 32 ? "SM501-LOW" : "SM501-HIGH", 115062306a36Sopenharmony_ci iic->pin_sda % 32, NULL, 0, 115162306a36Sopenharmony_ci GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN); 115262306a36Sopenharmony_ci lookup->table[1] = (struct gpiod_lookup) 115362306a36Sopenharmony_ci GPIO_LOOKUP_IDX(iic->pin_scl < 32 ? "SM501-LOW" : "SM501-HIGH", 115462306a36Sopenharmony_ci iic->pin_scl % 32, NULL, 1, 115562306a36Sopenharmony_ci GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN); 115662306a36Sopenharmony_ci gpiod_add_lookup_table(lookup); 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci icd = dev_get_platdata(&pdev->dev); 115962306a36Sopenharmony_ci icd->timeout = iic->timeout; 116062306a36Sopenharmony_ci icd->udelay = iic->udelay; 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci /* note, we can't use either of the pin numbers, as the i2c-gpio 116362306a36Sopenharmony_ci * driver uses the platform.id field to generate the bus number 116462306a36Sopenharmony_ci * to register with the i2c core; The i2c core doesn't have enough 116562306a36Sopenharmony_ci * entries to deal with anything we currently use. 116662306a36Sopenharmony_ci */ 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci pdev->id = iic->bus_num; 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci dev_info(sm->dev, "registering i2c-%d: sda=%d, scl=%d\n", 117162306a36Sopenharmony_ci iic->bus_num, 117262306a36Sopenharmony_ci iic->pin_sda, iic->pin_scl); 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci return sm501_register_device(sm, pdev); 117562306a36Sopenharmony_ci} 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_cistatic int sm501_register_gpio_i2c(struct sm501_devdata *sm, 117862306a36Sopenharmony_ci struct sm501_platdata *pdata) 117962306a36Sopenharmony_ci{ 118062306a36Sopenharmony_ci struct sm501_platdata_gpio_i2c *iic = pdata->gpio_i2c; 118162306a36Sopenharmony_ci int index; 118262306a36Sopenharmony_ci int ret; 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci for (index = 0; index < pdata->gpio_i2c_nr; index++, iic++) { 118562306a36Sopenharmony_ci ret = sm501_register_gpio_i2c_instance(sm, iic); 118662306a36Sopenharmony_ci if (ret < 0) 118762306a36Sopenharmony_ci return ret; 118862306a36Sopenharmony_ci } 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci return 0; 119162306a36Sopenharmony_ci} 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci/* dbg_regs_show 119462306a36Sopenharmony_ci * 119562306a36Sopenharmony_ci * Debug attribute to attach to parent device to show core registers 119662306a36Sopenharmony_ci*/ 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_cistatic ssize_t dbg_regs_show(struct device *dev, 119962306a36Sopenharmony_ci struct device_attribute *attr, char *buff) 120062306a36Sopenharmony_ci{ 120162306a36Sopenharmony_ci struct sm501_devdata *sm = dev_get_drvdata(dev) ; 120262306a36Sopenharmony_ci unsigned int reg; 120362306a36Sopenharmony_ci char *ptr = buff; 120462306a36Sopenharmony_ci int ret; 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci for (reg = 0x00; reg < 0x70; reg += 4) { 120762306a36Sopenharmony_ci ret = sprintf(ptr, "%08x = %08x\n", 120862306a36Sopenharmony_ci reg, smc501_readl(sm->regs + reg)); 120962306a36Sopenharmony_ci ptr += ret; 121062306a36Sopenharmony_ci } 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci return ptr - buff; 121362306a36Sopenharmony_ci} 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_cistatic DEVICE_ATTR_RO(dbg_regs); 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci/* sm501_init_reg 121962306a36Sopenharmony_ci * 122062306a36Sopenharmony_ci * Helper function for the init code to setup a register 122162306a36Sopenharmony_ci * 122262306a36Sopenharmony_ci * clear the bits which are set in r->mask, and then set 122362306a36Sopenharmony_ci * the bits set in r->set. 122462306a36Sopenharmony_ci*/ 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_cistatic inline void sm501_init_reg(struct sm501_devdata *sm, 122762306a36Sopenharmony_ci unsigned long reg, 122862306a36Sopenharmony_ci struct sm501_reg_init *r) 122962306a36Sopenharmony_ci{ 123062306a36Sopenharmony_ci unsigned long tmp; 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci tmp = smc501_readl(sm->regs + reg); 123362306a36Sopenharmony_ci tmp &= ~r->mask; 123462306a36Sopenharmony_ci tmp |= r->set; 123562306a36Sopenharmony_ci smc501_writel(tmp, sm->regs + reg); 123662306a36Sopenharmony_ci} 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci/* sm501_init_regs 123962306a36Sopenharmony_ci * 124062306a36Sopenharmony_ci * Setup core register values 124162306a36Sopenharmony_ci*/ 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_cistatic void sm501_init_regs(struct sm501_devdata *sm, 124462306a36Sopenharmony_ci struct sm501_initdata *init) 124562306a36Sopenharmony_ci{ 124662306a36Sopenharmony_ci sm501_misc_control(sm->dev, 124762306a36Sopenharmony_ci init->misc_control.set, 124862306a36Sopenharmony_ci init->misc_control.mask); 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci sm501_init_reg(sm, SM501_MISC_TIMING, &init->misc_timing); 125162306a36Sopenharmony_ci sm501_init_reg(sm, SM501_GPIO31_0_CONTROL, &init->gpio_low); 125262306a36Sopenharmony_ci sm501_init_reg(sm, SM501_GPIO63_32_CONTROL, &init->gpio_high); 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci if (init->m1xclk) { 125562306a36Sopenharmony_ci dev_info(sm->dev, "setting M1XCLK to %ld\n", init->m1xclk); 125662306a36Sopenharmony_ci sm501_set_clock(sm->dev, SM501_CLOCK_M1XCLK, init->m1xclk); 125762306a36Sopenharmony_ci } 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci if (init->mclk) { 126062306a36Sopenharmony_ci dev_info(sm->dev, "setting MCLK to %ld\n", init->mclk); 126162306a36Sopenharmony_ci sm501_set_clock(sm->dev, SM501_CLOCK_MCLK, init->mclk); 126262306a36Sopenharmony_ci } 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci} 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci/* Check the PLL sources for the M1CLK and M1XCLK 126762306a36Sopenharmony_ci * 126862306a36Sopenharmony_ci * If the M1CLK and M1XCLKs are not sourced from the same PLL, then 126962306a36Sopenharmony_ci * there is a risk (see errata AB-5) that the SM501 will cease proper 127062306a36Sopenharmony_ci * function. If this happens, then it is likely the SM501 will 127162306a36Sopenharmony_ci * hang the system. 127262306a36Sopenharmony_ci*/ 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_cistatic int sm501_check_clocks(struct sm501_devdata *sm) 127562306a36Sopenharmony_ci{ 127662306a36Sopenharmony_ci unsigned long pwrmode = smc501_readl(sm->regs + SM501_CURRENT_CLOCK); 127762306a36Sopenharmony_ci unsigned long msrc = (pwrmode & SM501_POWERMODE_M_SRC); 127862306a36Sopenharmony_ci unsigned long m1src = (pwrmode & SM501_POWERMODE_M1_SRC); 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci return ((msrc == 0 && m1src != 0) || (msrc != 0 && m1src == 0)); 128162306a36Sopenharmony_ci} 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_cistatic unsigned int sm501_mem_local[] = { 128462306a36Sopenharmony_ci [0] = 4*1024*1024, 128562306a36Sopenharmony_ci [1] = 8*1024*1024, 128662306a36Sopenharmony_ci [2] = 16*1024*1024, 128762306a36Sopenharmony_ci [3] = 32*1024*1024, 128862306a36Sopenharmony_ci [4] = 64*1024*1024, 128962306a36Sopenharmony_ci [5] = 2*1024*1024, 129062306a36Sopenharmony_ci}; 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci/* sm501_init_dev 129362306a36Sopenharmony_ci * 129462306a36Sopenharmony_ci * Common init code for an SM501 129562306a36Sopenharmony_ci*/ 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_cistatic int sm501_init_dev(struct sm501_devdata *sm) 129862306a36Sopenharmony_ci{ 129962306a36Sopenharmony_ci struct sm501_initdata *idata; 130062306a36Sopenharmony_ci struct sm501_platdata *pdata; 130162306a36Sopenharmony_ci resource_size_t mem_avail; 130262306a36Sopenharmony_ci unsigned long dramctrl; 130362306a36Sopenharmony_ci unsigned long devid; 130462306a36Sopenharmony_ci int ret; 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci mutex_init(&sm->clock_lock); 130762306a36Sopenharmony_ci spin_lock_init(&sm->reg_lock); 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci INIT_LIST_HEAD(&sm->devices); 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci devid = smc501_readl(sm->regs + SM501_DEVICEID); 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci if ((devid & SM501_DEVICEID_IDMASK) != SM501_DEVICEID_SM501) { 131462306a36Sopenharmony_ci dev_err(sm->dev, "incorrect device id %08lx\n", devid); 131562306a36Sopenharmony_ci return -EINVAL; 131662306a36Sopenharmony_ci } 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_ci /* disable irqs */ 131962306a36Sopenharmony_ci smc501_writel(0, sm->regs + SM501_IRQ_MASK); 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci dramctrl = smc501_readl(sm->regs + SM501_DRAM_CONTROL); 132262306a36Sopenharmony_ci mem_avail = sm501_mem_local[(dramctrl >> 13) & 0x7]; 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci dev_info(sm->dev, "SM501 At %p: Version %08lx, %ld Mb, IRQ %d\n", 132562306a36Sopenharmony_ci sm->regs, devid, (unsigned long)mem_avail >> 20, sm->irq); 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ci sm->rev = devid & SM501_DEVICEID_REVMASK; 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci sm501_dump_gate(sm); 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci ret = device_create_file(sm->dev, &dev_attr_dbg_regs); 133262306a36Sopenharmony_ci if (ret) 133362306a36Sopenharmony_ci dev_err(sm->dev, "failed to create debug regs file\n"); 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci sm501_dump_clk(sm); 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci /* check to see if we have some device initialisation */ 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci pdata = sm->platdata; 134062306a36Sopenharmony_ci idata = pdata ? pdata->init : NULL; 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci if (idata) { 134362306a36Sopenharmony_ci sm501_init_regs(sm, idata); 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_ci if (idata->devices & SM501_USE_USB_HOST) 134662306a36Sopenharmony_ci sm501_register_usbhost(sm, &mem_avail); 134762306a36Sopenharmony_ci if (idata->devices & (SM501_USE_UART0 | SM501_USE_UART1)) 134862306a36Sopenharmony_ci sm501_register_uart(sm, idata->devices); 134962306a36Sopenharmony_ci if (idata->devices & SM501_USE_GPIO) 135062306a36Sopenharmony_ci sm501_register_gpio(sm); 135162306a36Sopenharmony_ci } 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci if (pdata && pdata->gpio_i2c && pdata->gpio_i2c_nr > 0) { 135462306a36Sopenharmony_ci if (!sm501_gpio_isregistered(sm)) 135562306a36Sopenharmony_ci dev_err(sm->dev, "no gpio available for i2c gpio.\n"); 135662306a36Sopenharmony_ci else 135762306a36Sopenharmony_ci sm501_register_gpio_i2c(sm, pdata); 135862306a36Sopenharmony_ci } 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci ret = sm501_check_clocks(sm); 136162306a36Sopenharmony_ci if (ret) { 136262306a36Sopenharmony_ci dev_err(sm->dev, "M1X and M clocks sourced from different " 136362306a36Sopenharmony_ci "PLLs\n"); 136462306a36Sopenharmony_ci return -EINVAL; 136562306a36Sopenharmony_ci } 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci /* always create a framebuffer */ 136862306a36Sopenharmony_ci sm501_register_display(sm, &mem_avail); 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci return 0; 137162306a36Sopenharmony_ci} 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_cistatic int sm501_plat_probe(struct platform_device *dev) 137462306a36Sopenharmony_ci{ 137562306a36Sopenharmony_ci struct sm501_devdata *sm; 137662306a36Sopenharmony_ci int ret; 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci sm = kzalloc(sizeof(*sm), GFP_KERNEL); 137962306a36Sopenharmony_ci if (!sm) { 138062306a36Sopenharmony_ci ret = -ENOMEM; 138162306a36Sopenharmony_ci goto err1; 138262306a36Sopenharmony_ci } 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_ci sm->dev = &dev->dev; 138562306a36Sopenharmony_ci sm->pdev_id = dev->id; 138662306a36Sopenharmony_ci sm->platdata = dev_get_platdata(&dev->dev); 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci ret = platform_get_irq(dev, 0); 138962306a36Sopenharmony_ci if (ret < 0) 139062306a36Sopenharmony_ci goto err_res; 139162306a36Sopenharmony_ci sm->irq = ret; 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci sm->io_res = platform_get_resource(dev, IORESOURCE_MEM, 1); 139462306a36Sopenharmony_ci sm->mem_res = platform_get_resource(dev, IORESOURCE_MEM, 0); 139562306a36Sopenharmony_ci if (!sm->io_res || !sm->mem_res) { 139662306a36Sopenharmony_ci dev_err(&dev->dev, "failed to get IO resource\n"); 139762306a36Sopenharmony_ci ret = -ENOENT; 139862306a36Sopenharmony_ci goto err_res; 139962306a36Sopenharmony_ci } 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci sm->regs_claim = request_mem_region(sm->io_res->start, 140262306a36Sopenharmony_ci 0x100, "sm501"); 140362306a36Sopenharmony_ci if (!sm->regs_claim) { 140462306a36Sopenharmony_ci dev_err(&dev->dev, "cannot claim registers\n"); 140562306a36Sopenharmony_ci ret = -EBUSY; 140662306a36Sopenharmony_ci goto err_res; 140762306a36Sopenharmony_ci } 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ci platform_set_drvdata(dev, sm); 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_ci sm->regs = ioremap(sm->io_res->start, resource_size(sm->io_res)); 141262306a36Sopenharmony_ci if (!sm->regs) { 141362306a36Sopenharmony_ci dev_err(&dev->dev, "cannot remap registers\n"); 141462306a36Sopenharmony_ci ret = -EIO; 141562306a36Sopenharmony_ci goto err_claim; 141662306a36Sopenharmony_ci } 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_ci ret = sm501_init_dev(sm); 141962306a36Sopenharmony_ci if (ret) 142062306a36Sopenharmony_ci goto err_unmap; 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci return 0; 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_ci err_unmap: 142562306a36Sopenharmony_ci iounmap(sm->regs); 142662306a36Sopenharmony_ci err_claim: 142762306a36Sopenharmony_ci release_mem_region(sm->io_res->start, 0x100); 142862306a36Sopenharmony_ci err_res: 142962306a36Sopenharmony_ci kfree(sm); 143062306a36Sopenharmony_ci err1: 143162306a36Sopenharmony_ci return ret; 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_ci} 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci/* power management support */ 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_cistatic void sm501_set_power(struct sm501_devdata *sm, int on) 143862306a36Sopenharmony_ci{ 143962306a36Sopenharmony_ci struct sm501_platdata *pd = sm->platdata; 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_ci if (!pd) 144262306a36Sopenharmony_ci return; 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_ci if (pd->get_power) { 144562306a36Sopenharmony_ci if (pd->get_power(sm->dev) == on) { 144662306a36Sopenharmony_ci dev_dbg(sm->dev, "is already %d\n", on); 144762306a36Sopenharmony_ci return; 144862306a36Sopenharmony_ci } 144962306a36Sopenharmony_ci } 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci if (pd->set_power) { 145262306a36Sopenharmony_ci dev_dbg(sm->dev, "setting power to %d\n", on); 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_ci pd->set_power(sm->dev, on); 145562306a36Sopenharmony_ci sm501_mdelay(sm, 10); 145662306a36Sopenharmony_ci } 145762306a36Sopenharmony_ci} 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_cistatic int sm501_plat_suspend(struct platform_device *pdev, pm_message_t state) 146062306a36Sopenharmony_ci{ 146162306a36Sopenharmony_ci struct sm501_devdata *sm = platform_get_drvdata(pdev); 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_ci sm->in_suspend = 1; 146462306a36Sopenharmony_ci sm->pm_misc = smc501_readl(sm->regs + SM501_MISC_CONTROL); 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci sm501_dump_regs(sm); 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci if (sm->platdata) { 146962306a36Sopenharmony_ci if (sm->platdata->flags & SM501_FLAG_SUSPEND_OFF) 147062306a36Sopenharmony_ci sm501_set_power(sm, 0); 147162306a36Sopenharmony_ci } 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_ci return 0; 147462306a36Sopenharmony_ci} 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_cistatic int sm501_plat_resume(struct platform_device *pdev) 147762306a36Sopenharmony_ci{ 147862306a36Sopenharmony_ci struct sm501_devdata *sm = platform_get_drvdata(pdev); 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci sm501_set_power(sm, 1); 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_ci sm501_dump_regs(sm); 148362306a36Sopenharmony_ci sm501_dump_gate(sm); 148462306a36Sopenharmony_ci sm501_dump_clk(sm); 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_ci /* check to see if we are in the same state as when suspended */ 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci if (smc501_readl(sm->regs + SM501_MISC_CONTROL) != sm->pm_misc) { 148962306a36Sopenharmony_ci dev_info(sm->dev, "SM501_MISC_CONTROL changed over sleep\n"); 149062306a36Sopenharmony_ci smc501_writel(sm->pm_misc, sm->regs + SM501_MISC_CONTROL); 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci /* our suspend causes the controller state to change, 149362306a36Sopenharmony_ci * either by something attempting setup, power loss, 149462306a36Sopenharmony_ci * or an external reset event on power change */ 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci if (sm->platdata && sm->platdata->init) { 149762306a36Sopenharmony_ci sm501_init_regs(sm, sm->platdata->init); 149862306a36Sopenharmony_ci } 149962306a36Sopenharmony_ci } 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci /* dump our state from resume */ 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_ci sm501_dump_regs(sm); 150462306a36Sopenharmony_ci sm501_dump_clk(sm); 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci sm->in_suspend = 0; 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ci return 0; 150962306a36Sopenharmony_ci} 151062306a36Sopenharmony_ci 151162306a36Sopenharmony_ci/* Initialisation data for PCI devices */ 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_cistatic struct sm501_initdata sm501_pci_initdata = { 151462306a36Sopenharmony_ci .gpio_high = { 151562306a36Sopenharmony_ci .set = 0x3F000000, /* 24bit panel */ 151662306a36Sopenharmony_ci .mask = 0x0, 151762306a36Sopenharmony_ci }, 151862306a36Sopenharmony_ci .misc_timing = { 151962306a36Sopenharmony_ci .set = 0x010100, /* SDRAM timing */ 152062306a36Sopenharmony_ci .mask = 0x1F1F00, 152162306a36Sopenharmony_ci }, 152262306a36Sopenharmony_ci .misc_control = { 152362306a36Sopenharmony_ci .set = SM501_MISC_PNL_24BIT, 152462306a36Sopenharmony_ci .mask = 0, 152562306a36Sopenharmony_ci }, 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_ci .devices = SM501_USE_ALL, 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci /* Errata AB-3 says that 72MHz is the fastest available 153062306a36Sopenharmony_ci * for 33MHZ PCI with proper bus-mastering operation */ 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ci .mclk = 72 * MHZ, 153362306a36Sopenharmony_ci .m1xclk = 144 * MHZ, 153462306a36Sopenharmony_ci}; 153562306a36Sopenharmony_ci 153662306a36Sopenharmony_cistatic struct sm501_platdata_fbsub sm501_pdata_fbsub = { 153762306a36Sopenharmony_ci .flags = (SM501FB_FLAG_USE_INIT_MODE | 153862306a36Sopenharmony_ci SM501FB_FLAG_USE_HWCURSOR | 153962306a36Sopenharmony_ci SM501FB_FLAG_USE_HWACCEL | 154062306a36Sopenharmony_ci SM501FB_FLAG_DISABLE_AT_EXIT), 154162306a36Sopenharmony_ci}; 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_cistatic struct sm501_platdata_fb sm501_fb_pdata = { 154462306a36Sopenharmony_ci .fb_route = SM501_FB_OWN, 154562306a36Sopenharmony_ci .fb_crt = &sm501_pdata_fbsub, 154662306a36Sopenharmony_ci .fb_pnl = &sm501_pdata_fbsub, 154762306a36Sopenharmony_ci}; 154862306a36Sopenharmony_ci 154962306a36Sopenharmony_cistatic struct sm501_platdata sm501_pci_platdata = { 155062306a36Sopenharmony_ci .init = &sm501_pci_initdata, 155162306a36Sopenharmony_ci .fb = &sm501_fb_pdata, 155262306a36Sopenharmony_ci .gpio_base = -1, 155362306a36Sopenharmony_ci}; 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_cistatic int sm501_pci_probe(struct pci_dev *dev, 155662306a36Sopenharmony_ci const struct pci_device_id *id) 155762306a36Sopenharmony_ci{ 155862306a36Sopenharmony_ci struct sm501_devdata *sm; 155962306a36Sopenharmony_ci int err; 156062306a36Sopenharmony_ci 156162306a36Sopenharmony_ci sm = kzalloc(sizeof(*sm), GFP_KERNEL); 156262306a36Sopenharmony_ci if (!sm) { 156362306a36Sopenharmony_ci err = -ENOMEM; 156462306a36Sopenharmony_ci goto err1; 156562306a36Sopenharmony_ci } 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_ci /* set a default set of platform data */ 156862306a36Sopenharmony_ci dev->dev.platform_data = sm->platdata = &sm501_pci_platdata; 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_ci /* set a hopefully unique id for our child platform devices */ 157162306a36Sopenharmony_ci sm->pdev_id = 32 + dev->devfn; 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci pci_set_drvdata(dev, sm); 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_ci err = pci_enable_device(dev); 157662306a36Sopenharmony_ci if (err) { 157762306a36Sopenharmony_ci dev_err(&dev->dev, "cannot enable device\n"); 157862306a36Sopenharmony_ci goto err2; 157962306a36Sopenharmony_ci } 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_ci sm->dev = &dev->dev; 158262306a36Sopenharmony_ci sm->irq = dev->irq; 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci#ifdef __BIG_ENDIAN 158562306a36Sopenharmony_ci /* if the system is big-endian, we most probably have a 158662306a36Sopenharmony_ci * translation in the IO layer making the PCI bus little endian 158762306a36Sopenharmony_ci * so make the framebuffer swapped pixels */ 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci sm501_fb_pdata.flags |= SM501_FBPD_SWAP_FB_ENDIAN; 159062306a36Sopenharmony_ci#endif 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_ci /* check our resources */ 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci if (!(pci_resource_flags(dev, 0) & IORESOURCE_MEM)) { 159562306a36Sopenharmony_ci dev_err(&dev->dev, "region #0 is not memory?\n"); 159662306a36Sopenharmony_ci err = -EINVAL; 159762306a36Sopenharmony_ci goto err3; 159862306a36Sopenharmony_ci } 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_ci if (!(pci_resource_flags(dev, 1) & IORESOURCE_MEM)) { 160162306a36Sopenharmony_ci dev_err(&dev->dev, "region #1 is not memory?\n"); 160262306a36Sopenharmony_ci err = -EINVAL; 160362306a36Sopenharmony_ci goto err3; 160462306a36Sopenharmony_ci } 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_ci /* make our resources ready for sharing */ 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci sm->io_res = &dev->resource[1]; 160962306a36Sopenharmony_ci sm->mem_res = &dev->resource[0]; 161062306a36Sopenharmony_ci 161162306a36Sopenharmony_ci sm->regs_claim = request_mem_region(sm->io_res->start, 161262306a36Sopenharmony_ci 0x100, "sm501"); 161362306a36Sopenharmony_ci if (!sm->regs_claim) { 161462306a36Sopenharmony_ci dev_err(&dev->dev, "cannot claim registers\n"); 161562306a36Sopenharmony_ci err= -EBUSY; 161662306a36Sopenharmony_ci goto err3; 161762306a36Sopenharmony_ci } 161862306a36Sopenharmony_ci 161962306a36Sopenharmony_ci sm->regs = pci_ioremap_bar(dev, 1); 162062306a36Sopenharmony_ci if (!sm->regs) { 162162306a36Sopenharmony_ci dev_err(&dev->dev, "cannot remap registers\n"); 162262306a36Sopenharmony_ci err = -EIO; 162362306a36Sopenharmony_ci goto err4; 162462306a36Sopenharmony_ci } 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_ci sm501_init_dev(sm); 162762306a36Sopenharmony_ci return 0; 162862306a36Sopenharmony_ci 162962306a36Sopenharmony_ci err4: 163062306a36Sopenharmony_ci release_mem_region(sm->io_res->start, 0x100); 163162306a36Sopenharmony_ci err3: 163262306a36Sopenharmony_ci pci_disable_device(dev); 163362306a36Sopenharmony_ci err2: 163462306a36Sopenharmony_ci kfree(sm); 163562306a36Sopenharmony_ci err1: 163662306a36Sopenharmony_ci return err; 163762306a36Sopenharmony_ci} 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_cistatic void sm501_remove_sub(struct sm501_devdata *sm, 164062306a36Sopenharmony_ci struct sm501_device *smdev) 164162306a36Sopenharmony_ci{ 164262306a36Sopenharmony_ci list_del(&smdev->list); 164362306a36Sopenharmony_ci platform_device_unregister(&smdev->pdev); 164462306a36Sopenharmony_ci} 164562306a36Sopenharmony_ci 164662306a36Sopenharmony_cistatic void sm501_dev_remove(struct sm501_devdata *sm) 164762306a36Sopenharmony_ci{ 164862306a36Sopenharmony_ci struct sm501_device *smdev, *tmp; 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_ci list_for_each_entry_safe(smdev, tmp, &sm->devices, list) 165162306a36Sopenharmony_ci sm501_remove_sub(sm, smdev); 165262306a36Sopenharmony_ci 165362306a36Sopenharmony_ci device_remove_file(sm->dev, &dev_attr_dbg_regs); 165462306a36Sopenharmony_ci 165562306a36Sopenharmony_ci sm501_gpio_remove(sm); 165662306a36Sopenharmony_ci} 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_cistatic void sm501_pci_remove(struct pci_dev *dev) 165962306a36Sopenharmony_ci{ 166062306a36Sopenharmony_ci struct sm501_devdata *sm = pci_get_drvdata(dev); 166162306a36Sopenharmony_ci 166262306a36Sopenharmony_ci sm501_dev_remove(sm); 166362306a36Sopenharmony_ci iounmap(sm->regs); 166462306a36Sopenharmony_ci 166562306a36Sopenharmony_ci release_mem_region(sm->io_res->start, 0x100); 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_ci pci_disable_device(dev); 166862306a36Sopenharmony_ci} 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_cistatic int sm501_plat_remove(struct platform_device *dev) 167162306a36Sopenharmony_ci{ 167262306a36Sopenharmony_ci struct sm501_devdata *sm = platform_get_drvdata(dev); 167362306a36Sopenharmony_ci 167462306a36Sopenharmony_ci sm501_dev_remove(sm); 167562306a36Sopenharmony_ci iounmap(sm->regs); 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_ci release_mem_region(sm->io_res->start, 0x100); 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_ci return 0; 168062306a36Sopenharmony_ci} 168162306a36Sopenharmony_ci 168262306a36Sopenharmony_cistatic const struct pci_device_id sm501_pci_tbl[] = { 168362306a36Sopenharmony_ci { 0x126f, 0x0501, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, 168462306a36Sopenharmony_ci { 0, }, 168562306a36Sopenharmony_ci}; 168662306a36Sopenharmony_ci 168762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, sm501_pci_tbl); 168862306a36Sopenharmony_ci 168962306a36Sopenharmony_cistatic struct pci_driver sm501_pci_driver = { 169062306a36Sopenharmony_ci .name = "sm501", 169162306a36Sopenharmony_ci .id_table = sm501_pci_tbl, 169262306a36Sopenharmony_ci .probe = sm501_pci_probe, 169362306a36Sopenharmony_ci .remove = sm501_pci_remove, 169462306a36Sopenharmony_ci}; 169562306a36Sopenharmony_ci 169662306a36Sopenharmony_ciMODULE_ALIAS("platform:sm501"); 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_cistatic const struct of_device_id of_sm501_match_tbl[] = { 169962306a36Sopenharmony_ci { .compatible = "smi,sm501", }, 170062306a36Sopenharmony_ci { /* end */ } 170162306a36Sopenharmony_ci}; 170262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, of_sm501_match_tbl); 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_cistatic struct platform_driver sm501_plat_driver = { 170562306a36Sopenharmony_ci .driver = { 170662306a36Sopenharmony_ci .name = "sm501", 170762306a36Sopenharmony_ci .of_match_table = of_sm501_match_tbl, 170862306a36Sopenharmony_ci }, 170962306a36Sopenharmony_ci .probe = sm501_plat_probe, 171062306a36Sopenharmony_ci .remove = sm501_plat_remove, 171162306a36Sopenharmony_ci .suspend = pm_sleep_ptr(sm501_plat_suspend), 171262306a36Sopenharmony_ci .resume = pm_sleep_ptr(sm501_plat_resume), 171362306a36Sopenharmony_ci}; 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_cistatic int __init sm501_base_init(void) 171662306a36Sopenharmony_ci{ 171762306a36Sopenharmony_ci int ret; 171862306a36Sopenharmony_ci 171962306a36Sopenharmony_ci ret = platform_driver_register(&sm501_plat_driver); 172062306a36Sopenharmony_ci if (ret < 0) 172162306a36Sopenharmony_ci return ret; 172262306a36Sopenharmony_ci 172362306a36Sopenharmony_ci return pci_register_driver(&sm501_pci_driver); 172462306a36Sopenharmony_ci} 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_cistatic void __exit sm501_base_exit(void) 172762306a36Sopenharmony_ci{ 172862306a36Sopenharmony_ci platform_driver_unregister(&sm501_plat_driver); 172962306a36Sopenharmony_ci pci_unregister_driver(&sm501_pci_driver); 173062306a36Sopenharmony_ci} 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_cimodule_init(sm501_base_init); 173362306a36Sopenharmony_cimodule_exit(sm501_base_exit); 173462306a36Sopenharmony_ci 173562306a36Sopenharmony_ciMODULE_DESCRIPTION("SM501 Core Driver"); 173662306a36Sopenharmony_ciMODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>, Vincent Sanders"); 173762306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1738