18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* linux/drivers/mfd/sm501.c 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2006 Simtec Electronics 58c2ecf20Sopenharmony_ci * Ben Dooks <ben@simtec.co.uk> 68c2ecf20Sopenharmony_ci * Vincent Sanders <vince@simtec.co.uk> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * SM501 MFD driver 98c2ecf20Sopenharmony_ci*/ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/delay.h> 148c2ecf20Sopenharmony_ci#include <linux/init.h> 158c2ecf20Sopenharmony_ci#include <linux/list.h> 168c2ecf20Sopenharmony_ci#include <linux/device.h> 178c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 188c2ecf20Sopenharmony_ci#include <linux/pci.h> 198c2ecf20Sopenharmony_ci#include <linux/platform_data/i2c-gpio.h> 208c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h> 218c2ecf20Sopenharmony_ci#include <linux/gpio/machine.h> 228c2ecf20Sopenharmony_ci#include <linux/slab.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include <linux/sm501.h> 258c2ecf20Sopenharmony_ci#include <linux/sm501-regs.h> 268c2ecf20Sopenharmony_ci#include <linux/serial_8250.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include <linux/io.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistruct sm501_device { 318c2ecf20Sopenharmony_ci struct list_head list; 328c2ecf20Sopenharmony_ci struct platform_device pdev; 338c2ecf20Sopenharmony_ci}; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistruct sm501_gpio; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#ifdef CONFIG_MFD_SM501_GPIO 388c2ecf20Sopenharmony_ci#include <linux/gpio.h> 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistruct sm501_gpio_chip { 418c2ecf20Sopenharmony_ci struct gpio_chip gpio; 428c2ecf20Sopenharmony_ci struct sm501_gpio *ourgpio; /* to get back to parent. */ 438c2ecf20Sopenharmony_ci void __iomem *regbase; 448c2ecf20Sopenharmony_ci void __iomem *control; /* address of control reg. */ 458c2ecf20Sopenharmony_ci}; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistruct sm501_gpio { 488c2ecf20Sopenharmony_ci struct sm501_gpio_chip low; 498c2ecf20Sopenharmony_ci struct sm501_gpio_chip high; 508c2ecf20Sopenharmony_ci spinlock_t lock; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci unsigned int registered : 1; 538c2ecf20Sopenharmony_ci void __iomem *regs; 548c2ecf20Sopenharmony_ci struct resource *regs_res; 558c2ecf20Sopenharmony_ci}; 568c2ecf20Sopenharmony_ci#else 578c2ecf20Sopenharmony_cistruct sm501_gpio { 588c2ecf20Sopenharmony_ci /* no gpio support, empty definition for sm501_devdata. */ 598c2ecf20Sopenharmony_ci}; 608c2ecf20Sopenharmony_ci#endif 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistruct sm501_devdata { 638c2ecf20Sopenharmony_ci spinlock_t reg_lock; 648c2ecf20Sopenharmony_ci struct mutex clock_lock; 658c2ecf20Sopenharmony_ci struct list_head devices; 668c2ecf20Sopenharmony_ci struct sm501_gpio gpio; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci struct device *dev; 698c2ecf20Sopenharmony_ci struct resource *io_res; 708c2ecf20Sopenharmony_ci struct resource *mem_res; 718c2ecf20Sopenharmony_ci struct resource *regs_claim; 728c2ecf20Sopenharmony_ci struct sm501_platdata *platdata; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci unsigned int in_suspend; 768c2ecf20Sopenharmony_ci unsigned long pm_misc; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci int unit_power[20]; 798c2ecf20Sopenharmony_ci unsigned int pdev_id; 808c2ecf20Sopenharmony_ci unsigned int irq; 818c2ecf20Sopenharmony_ci void __iomem *regs; 828c2ecf20Sopenharmony_ci unsigned int rev; 838c2ecf20Sopenharmony_ci}; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci#define MHZ (1000 * 1000) 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci#ifdef DEBUG 898c2ecf20Sopenharmony_cistatic const unsigned int div_tab[] = { 908c2ecf20Sopenharmony_ci [0] = 1, 918c2ecf20Sopenharmony_ci [1] = 2, 928c2ecf20Sopenharmony_ci [2] = 4, 938c2ecf20Sopenharmony_ci [3] = 8, 948c2ecf20Sopenharmony_ci [4] = 16, 958c2ecf20Sopenharmony_ci [5] = 32, 968c2ecf20Sopenharmony_ci [6] = 64, 978c2ecf20Sopenharmony_ci [7] = 128, 988c2ecf20Sopenharmony_ci [8] = 3, 998c2ecf20Sopenharmony_ci [9] = 6, 1008c2ecf20Sopenharmony_ci [10] = 12, 1018c2ecf20Sopenharmony_ci [11] = 24, 1028c2ecf20Sopenharmony_ci [12] = 48, 1038c2ecf20Sopenharmony_ci [13] = 96, 1048c2ecf20Sopenharmony_ci [14] = 192, 1058c2ecf20Sopenharmony_ci [15] = 384, 1068c2ecf20Sopenharmony_ci [16] = 5, 1078c2ecf20Sopenharmony_ci [17] = 10, 1088c2ecf20Sopenharmony_ci [18] = 20, 1098c2ecf20Sopenharmony_ci [19] = 40, 1108c2ecf20Sopenharmony_ci [20] = 80, 1118c2ecf20Sopenharmony_ci [21] = 160, 1128c2ecf20Sopenharmony_ci [22] = 320, 1138c2ecf20Sopenharmony_ci [23] = 604, 1148c2ecf20Sopenharmony_ci}; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic unsigned long decode_div(unsigned long pll2, unsigned long val, 1178c2ecf20Sopenharmony_ci unsigned int lshft, unsigned int selbit, 1188c2ecf20Sopenharmony_ci unsigned long mask) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci if (val & selbit) 1218c2ecf20Sopenharmony_ci pll2 = 288 * MHZ; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci return pll2 / div_tab[(val >> lshft) & mask]; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci#define fmt_freq(x) ((x) / MHZ), ((x) % MHZ), (x) 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci/* sm501_dump_clk 1298c2ecf20Sopenharmony_ci * 1308c2ecf20Sopenharmony_ci * Print out the current clock configuration for the device 1318c2ecf20Sopenharmony_ci*/ 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic void sm501_dump_clk(struct sm501_devdata *sm) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci unsigned long misct = smc501_readl(sm->regs + SM501_MISC_TIMING); 1368c2ecf20Sopenharmony_ci unsigned long pm0 = smc501_readl(sm->regs + SM501_POWER_MODE_0_CLOCK); 1378c2ecf20Sopenharmony_ci unsigned long pm1 = smc501_readl(sm->regs + SM501_POWER_MODE_1_CLOCK); 1388c2ecf20Sopenharmony_ci unsigned long pmc = smc501_readl(sm->regs + SM501_POWER_MODE_CONTROL); 1398c2ecf20Sopenharmony_ci unsigned long sdclk0, sdclk1; 1408c2ecf20Sopenharmony_ci unsigned long pll2 = 0; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci switch (misct & 0x30) { 1438c2ecf20Sopenharmony_ci case 0x00: 1448c2ecf20Sopenharmony_ci pll2 = 336 * MHZ; 1458c2ecf20Sopenharmony_ci break; 1468c2ecf20Sopenharmony_ci case 0x10: 1478c2ecf20Sopenharmony_ci pll2 = 288 * MHZ; 1488c2ecf20Sopenharmony_ci break; 1498c2ecf20Sopenharmony_ci case 0x20: 1508c2ecf20Sopenharmony_ci pll2 = 240 * MHZ; 1518c2ecf20Sopenharmony_ci break; 1528c2ecf20Sopenharmony_ci case 0x30: 1538c2ecf20Sopenharmony_ci pll2 = 192 * MHZ; 1548c2ecf20Sopenharmony_ci break; 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci sdclk0 = (misct & (1<<12)) ? pll2 : 288 * MHZ; 1588c2ecf20Sopenharmony_ci sdclk0 /= div_tab[((misct >> 8) & 0xf)]; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci sdclk1 = (misct & (1<<20)) ? pll2 : 288 * MHZ; 1618c2ecf20Sopenharmony_ci sdclk1 /= div_tab[((misct >> 16) & 0xf)]; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci dev_dbg(sm->dev, "MISCT=%08lx, PM0=%08lx, PM1=%08lx\n", 1648c2ecf20Sopenharmony_ci misct, pm0, pm1); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci dev_dbg(sm->dev, "PLL2 = %ld.%ld MHz (%ld), SDCLK0=%08lx, SDCLK1=%08lx\n", 1678c2ecf20Sopenharmony_ci fmt_freq(pll2), sdclk0, sdclk1); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci dev_dbg(sm->dev, "SDRAM: PM0=%ld, PM1=%ld\n", sdclk0, sdclk1); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci dev_dbg(sm->dev, "PM0[%c]: " 1728c2ecf20Sopenharmony_ci "P2 %ld.%ld MHz (%ld), V2 %ld.%ld (%ld), " 1738c2ecf20Sopenharmony_ci "M %ld.%ld (%ld), MX1 %ld.%ld (%ld)\n", 1748c2ecf20Sopenharmony_ci (pmc & 3 ) == 0 ? '*' : '-', 1758c2ecf20Sopenharmony_ci fmt_freq(decode_div(pll2, pm0, 24, 1<<29, 31)), 1768c2ecf20Sopenharmony_ci fmt_freq(decode_div(pll2, pm0, 16, 1<<20, 15)), 1778c2ecf20Sopenharmony_ci fmt_freq(decode_div(pll2, pm0, 8, 1<<12, 15)), 1788c2ecf20Sopenharmony_ci fmt_freq(decode_div(pll2, pm0, 0, 1<<4, 15))); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci dev_dbg(sm->dev, "PM1[%c]: " 1818c2ecf20Sopenharmony_ci "P2 %ld.%ld MHz (%ld), V2 %ld.%ld (%ld), " 1828c2ecf20Sopenharmony_ci "M %ld.%ld (%ld), MX1 %ld.%ld (%ld)\n", 1838c2ecf20Sopenharmony_ci (pmc & 3 ) == 1 ? '*' : '-', 1848c2ecf20Sopenharmony_ci fmt_freq(decode_div(pll2, pm1, 24, 1<<29, 31)), 1858c2ecf20Sopenharmony_ci fmt_freq(decode_div(pll2, pm1, 16, 1<<20, 15)), 1868c2ecf20Sopenharmony_ci fmt_freq(decode_div(pll2, pm1, 8, 1<<12, 15)), 1878c2ecf20Sopenharmony_ci fmt_freq(decode_div(pll2, pm1, 0, 1<<4, 15))); 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic void sm501_dump_regs(struct sm501_devdata *sm) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci void __iomem *regs = sm->regs; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci dev_info(sm->dev, "System Control %08x\n", 1958c2ecf20Sopenharmony_ci smc501_readl(regs + SM501_SYSTEM_CONTROL)); 1968c2ecf20Sopenharmony_ci dev_info(sm->dev, "Misc Control %08x\n", 1978c2ecf20Sopenharmony_ci smc501_readl(regs + SM501_MISC_CONTROL)); 1988c2ecf20Sopenharmony_ci dev_info(sm->dev, "GPIO Control Low %08x\n", 1998c2ecf20Sopenharmony_ci smc501_readl(regs + SM501_GPIO31_0_CONTROL)); 2008c2ecf20Sopenharmony_ci dev_info(sm->dev, "GPIO Control Hi %08x\n", 2018c2ecf20Sopenharmony_ci smc501_readl(regs + SM501_GPIO63_32_CONTROL)); 2028c2ecf20Sopenharmony_ci dev_info(sm->dev, "DRAM Control %08x\n", 2038c2ecf20Sopenharmony_ci smc501_readl(regs + SM501_DRAM_CONTROL)); 2048c2ecf20Sopenharmony_ci dev_info(sm->dev, "Arbitration Ctrl %08x\n", 2058c2ecf20Sopenharmony_ci smc501_readl(regs + SM501_ARBTRTN_CONTROL)); 2068c2ecf20Sopenharmony_ci dev_info(sm->dev, "Misc Timing %08x\n", 2078c2ecf20Sopenharmony_ci smc501_readl(regs + SM501_MISC_TIMING)); 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic void sm501_dump_gate(struct sm501_devdata *sm) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci dev_info(sm->dev, "CurrentGate %08x\n", 2138c2ecf20Sopenharmony_ci smc501_readl(sm->regs + SM501_CURRENT_GATE)); 2148c2ecf20Sopenharmony_ci dev_info(sm->dev, "CurrentClock %08x\n", 2158c2ecf20Sopenharmony_ci smc501_readl(sm->regs + SM501_CURRENT_CLOCK)); 2168c2ecf20Sopenharmony_ci dev_info(sm->dev, "PowerModeControl %08x\n", 2178c2ecf20Sopenharmony_ci smc501_readl(sm->regs + SM501_POWER_MODE_CONTROL)); 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci#else 2218c2ecf20Sopenharmony_cistatic inline void sm501_dump_gate(struct sm501_devdata *sm) { } 2228c2ecf20Sopenharmony_cistatic inline void sm501_dump_regs(struct sm501_devdata *sm) { } 2238c2ecf20Sopenharmony_cistatic inline void sm501_dump_clk(struct sm501_devdata *sm) { } 2248c2ecf20Sopenharmony_ci#endif 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci/* sm501_sync_regs 2278c2ecf20Sopenharmony_ci * 2288c2ecf20Sopenharmony_ci * ensure the 2298c2ecf20Sopenharmony_ci*/ 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistatic void sm501_sync_regs(struct sm501_devdata *sm) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci smc501_readl(sm->regs); 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic inline void sm501_mdelay(struct sm501_devdata *sm, unsigned int delay) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci /* during suspend/resume, we are currently not allowed to sleep, 2398c2ecf20Sopenharmony_ci * so change to using mdelay() instead of msleep() if we 2408c2ecf20Sopenharmony_ci * are in one of these paths */ 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (sm->in_suspend) 2438c2ecf20Sopenharmony_ci mdelay(delay); 2448c2ecf20Sopenharmony_ci else 2458c2ecf20Sopenharmony_ci msleep(delay); 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci/* sm501_misc_control 2498c2ecf20Sopenharmony_ci * 2508c2ecf20Sopenharmony_ci * alters the miscellaneous control parameters 2518c2ecf20Sopenharmony_ci*/ 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ciint sm501_misc_control(struct device *dev, 2548c2ecf20Sopenharmony_ci unsigned long set, unsigned long clear) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci struct sm501_devdata *sm = dev_get_drvdata(dev); 2578c2ecf20Sopenharmony_ci unsigned long misc; 2588c2ecf20Sopenharmony_ci unsigned long save; 2598c2ecf20Sopenharmony_ci unsigned long to; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci spin_lock_irqsave(&sm->reg_lock, save); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci misc = smc501_readl(sm->regs + SM501_MISC_CONTROL); 2648c2ecf20Sopenharmony_ci to = (misc & ~clear) | set; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci if (to != misc) { 2678c2ecf20Sopenharmony_ci smc501_writel(to, sm->regs + SM501_MISC_CONTROL); 2688c2ecf20Sopenharmony_ci sm501_sync_regs(sm); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci dev_dbg(sm->dev, "MISC_CONTROL %08lx\n", misc); 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sm->reg_lock, save); 2748c2ecf20Sopenharmony_ci return to; 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sm501_misc_control); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci/* sm501_modify_reg 2808c2ecf20Sopenharmony_ci * 2818c2ecf20Sopenharmony_ci * Modify a register in the SM501 which may be shared with other 2828c2ecf20Sopenharmony_ci * drivers. 2838c2ecf20Sopenharmony_ci*/ 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ciunsigned long sm501_modify_reg(struct device *dev, 2868c2ecf20Sopenharmony_ci unsigned long reg, 2878c2ecf20Sopenharmony_ci unsigned long set, 2888c2ecf20Sopenharmony_ci unsigned long clear) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci struct sm501_devdata *sm = dev_get_drvdata(dev); 2918c2ecf20Sopenharmony_ci unsigned long data; 2928c2ecf20Sopenharmony_ci unsigned long save; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci spin_lock_irqsave(&sm->reg_lock, save); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci data = smc501_readl(sm->regs + reg); 2978c2ecf20Sopenharmony_ci data |= set; 2988c2ecf20Sopenharmony_ci data &= ~clear; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci smc501_writel(data, sm->regs + reg); 3018c2ecf20Sopenharmony_ci sm501_sync_regs(sm); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sm->reg_lock, save); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci return data; 3068c2ecf20Sopenharmony_ci} 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sm501_modify_reg); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci/* sm501_unit_power 3118c2ecf20Sopenharmony_ci * 3128c2ecf20Sopenharmony_ci * alters the power active gate to set specific units on or off 3138c2ecf20Sopenharmony_ci */ 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ciint sm501_unit_power(struct device *dev, unsigned int unit, unsigned int to) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci struct sm501_devdata *sm = dev_get_drvdata(dev); 3188c2ecf20Sopenharmony_ci unsigned long mode; 3198c2ecf20Sopenharmony_ci unsigned long gate; 3208c2ecf20Sopenharmony_ci unsigned long clock; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci mutex_lock(&sm->clock_lock); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci mode = smc501_readl(sm->regs + SM501_POWER_MODE_CONTROL); 3258c2ecf20Sopenharmony_ci gate = smc501_readl(sm->regs + SM501_CURRENT_GATE); 3268c2ecf20Sopenharmony_ci clock = smc501_readl(sm->regs + SM501_CURRENT_CLOCK); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci mode &= 3; /* get current power mode */ 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci if (unit >= ARRAY_SIZE(sm->unit_power)) { 3318c2ecf20Sopenharmony_ci dev_err(dev, "%s: bad unit %d\n", __func__, unit); 3328c2ecf20Sopenharmony_ci goto already; 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci dev_dbg(sm->dev, "%s: unit %d, cur %d, to %d\n", __func__, unit, 3368c2ecf20Sopenharmony_ci sm->unit_power[unit], to); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci if (to == 0 && sm->unit_power[unit] == 0) { 3398c2ecf20Sopenharmony_ci dev_err(sm->dev, "unit %d is already shutdown\n", unit); 3408c2ecf20Sopenharmony_ci goto already; 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci sm->unit_power[unit] += to ? 1 : -1; 3448c2ecf20Sopenharmony_ci to = sm->unit_power[unit] ? 1 : 0; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci if (to) { 3478c2ecf20Sopenharmony_ci if (gate & (1 << unit)) 3488c2ecf20Sopenharmony_ci goto already; 3498c2ecf20Sopenharmony_ci gate |= (1 << unit); 3508c2ecf20Sopenharmony_ci } else { 3518c2ecf20Sopenharmony_ci if (!(gate & (1 << unit))) 3528c2ecf20Sopenharmony_ci goto already; 3538c2ecf20Sopenharmony_ci gate &= ~(1 << unit); 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci switch (mode) { 3578c2ecf20Sopenharmony_ci case 1: 3588c2ecf20Sopenharmony_ci smc501_writel(gate, sm->regs + SM501_POWER_MODE_0_GATE); 3598c2ecf20Sopenharmony_ci smc501_writel(clock, sm->regs + SM501_POWER_MODE_0_CLOCK); 3608c2ecf20Sopenharmony_ci mode = 0; 3618c2ecf20Sopenharmony_ci break; 3628c2ecf20Sopenharmony_ci case 2: 3638c2ecf20Sopenharmony_ci case 0: 3648c2ecf20Sopenharmony_ci smc501_writel(gate, sm->regs + SM501_POWER_MODE_1_GATE); 3658c2ecf20Sopenharmony_ci smc501_writel(clock, sm->regs + SM501_POWER_MODE_1_CLOCK); 3668c2ecf20Sopenharmony_ci mode = 1; 3678c2ecf20Sopenharmony_ci break; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci default: 3708c2ecf20Sopenharmony_ci gate = -1; 3718c2ecf20Sopenharmony_ci goto already; 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci smc501_writel(mode, sm->regs + SM501_POWER_MODE_CONTROL); 3758c2ecf20Sopenharmony_ci sm501_sync_regs(sm); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci dev_dbg(sm->dev, "gate %08lx, clock %08lx, mode %08lx\n", 3788c2ecf20Sopenharmony_ci gate, clock, mode); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci sm501_mdelay(sm, 16); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci already: 3838c2ecf20Sopenharmony_ci mutex_unlock(&sm->clock_lock); 3848c2ecf20Sopenharmony_ci return gate; 3858c2ecf20Sopenharmony_ci} 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sm501_unit_power); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci/* clock value structure. */ 3908c2ecf20Sopenharmony_cistruct sm501_clock { 3918c2ecf20Sopenharmony_ci unsigned long mclk; 3928c2ecf20Sopenharmony_ci int divider; 3938c2ecf20Sopenharmony_ci int shift; 3948c2ecf20Sopenharmony_ci unsigned int m, n, k; 3958c2ecf20Sopenharmony_ci}; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci/* sm501_calc_clock 3988c2ecf20Sopenharmony_ci * 3998c2ecf20Sopenharmony_ci * Calculates the nearest discrete clock frequency that 4008c2ecf20Sopenharmony_ci * can be achieved with the specified input clock. 4018c2ecf20Sopenharmony_ci * the maximum divisor is 3 or 5 4028c2ecf20Sopenharmony_ci */ 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_cistatic int sm501_calc_clock(unsigned long freq, 4058c2ecf20Sopenharmony_ci struct sm501_clock *clock, 4068c2ecf20Sopenharmony_ci int max_div, 4078c2ecf20Sopenharmony_ci unsigned long mclk, 4088c2ecf20Sopenharmony_ci long *best_diff) 4098c2ecf20Sopenharmony_ci{ 4108c2ecf20Sopenharmony_ci int ret = 0; 4118c2ecf20Sopenharmony_ci int divider; 4128c2ecf20Sopenharmony_ci int shift; 4138c2ecf20Sopenharmony_ci long diff; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci /* try dividers 1 and 3 for CRT and for panel, 4168c2ecf20Sopenharmony_ci try divider 5 for panel only.*/ 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci for (divider = 1; divider <= max_div; divider += 2) { 4198c2ecf20Sopenharmony_ci /* try all 8 shift values.*/ 4208c2ecf20Sopenharmony_ci for (shift = 0; shift < 8; shift++) { 4218c2ecf20Sopenharmony_ci /* Calculate difference to requested clock */ 4228c2ecf20Sopenharmony_ci diff = DIV_ROUND_CLOSEST(mclk, divider << shift) - freq; 4238c2ecf20Sopenharmony_ci if (diff < 0) 4248c2ecf20Sopenharmony_ci diff = -diff; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci /* If it is less than the current, use it */ 4278c2ecf20Sopenharmony_ci if (diff < *best_diff) { 4288c2ecf20Sopenharmony_ci *best_diff = diff; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci clock->mclk = mclk; 4318c2ecf20Sopenharmony_ci clock->divider = divider; 4328c2ecf20Sopenharmony_ci clock->shift = shift; 4338c2ecf20Sopenharmony_ci ret = 1; 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci return ret; 4398c2ecf20Sopenharmony_ci} 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci/* sm501_calc_pll 4428c2ecf20Sopenharmony_ci * 4438c2ecf20Sopenharmony_ci * Calculates the nearest discrete clock frequency that can be 4448c2ecf20Sopenharmony_ci * achieved using the programmable PLL. 4458c2ecf20Sopenharmony_ci * the maximum divisor is 3 or 5 4468c2ecf20Sopenharmony_ci */ 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_cistatic unsigned long sm501_calc_pll(unsigned long freq, 4498c2ecf20Sopenharmony_ci struct sm501_clock *clock, 4508c2ecf20Sopenharmony_ci int max_div) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci unsigned long mclk; 4538c2ecf20Sopenharmony_ci unsigned int m, n, k; 4548c2ecf20Sopenharmony_ci long best_diff = 999999999; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci /* 4578c2ecf20Sopenharmony_ci * The SM502 datasheet doesn't specify the min/max values for M and N. 4588c2ecf20Sopenharmony_ci * N = 1 at least doesn't work in practice. 4598c2ecf20Sopenharmony_ci */ 4608c2ecf20Sopenharmony_ci for (m = 2; m <= 255; m++) { 4618c2ecf20Sopenharmony_ci for (n = 2; n <= 127; n++) { 4628c2ecf20Sopenharmony_ci for (k = 0; k <= 1; k++) { 4638c2ecf20Sopenharmony_ci mclk = (24000000UL * m / n) >> k; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci if (sm501_calc_clock(freq, clock, max_div, 4668c2ecf20Sopenharmony_ci mclk, &best_diff)) { 4678c2ecf20Sopenharmony_ci clock->m = m; 4688c2ecf20Sopenharmony_ci clock->n = n; 4698c2ecf20Sopenharmony_ci clock->k = k; 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci } 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci /* Return best clock. */ 4768c2ecf20Sopenharmony_ci return clock->mclk / (clock->divider << clock->shift); 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci/* sm501_select_clock 4808c2ecf20Sopenharmony_ci * 4818c2ecf20Sopenharmony_ci * Calculates the nearest discrete clock frequency that can be 4828c2ecf20Sopenharmony_ci * achieved using the 288MHz and 336MHz PLLs. 4838c2ecf20Sopenharmony_ci * the maximum divisor is 3 or 5 4848c2ecf20Sopenharmony_ci */ 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_cistatic unsigned long sm501_select_clock(unsigned long freq, 4878c2ecf20Sopenharmony_ci struct sm501_clock *clock, 4888c2ecf20Sopenharmony_ci int max_div) 4898c2ecf20Sopenharmony_ci{ 4908c2ecf20Sopenharmony_ci unsigned long mclk; 4918c2ecf20Sopenharmony_ci long best_diff = 999999999; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci /* Try 288MHz and 336MHz clocks. */ 4948c2ecf20Sopenharmony_ci for (mclk = 288000000; mclk <= 336000000; mclk += 48000000) { 4958c2ecf20Sopenharmony_ci sm501_calc_clock(freq, clock, max_div, mclk, &best_diff); 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci /* Return best clock. */ 4998c2ecf20Sopenharmony_ci return clock->mclk / (clock->divider << clock->shift); 5008c2ecf20Sopenharmony_ci} 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci/* sm501_set_clock 5038c2ecf20Sopenharmony_ci * 5048c2ecf20Sopenharmony_ci * set one of the four clock sources to the closest available frequency to 5058c2ecf20Sopenharmony_ci * the one specified 5068c2ecf20Sopenharmony_ci*/ 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ciunsigned long sm501_set_clock(struct device *dev, 5098c2ecf20Sopenharmony_ci int clksrc, 5108c2ecf20Sopenharmony_ci unsigned long req_freq) 5118c2ecf20Sopenharmony_ci{ 5128c2ecf20Sopenharmony_ci struct sm501_devdata *sm = dev_get_drvdata(dev); 5138c2ecf20Sopenharmony_ci unsigned long mode = smc501_readl(sm->regs + SM501_POWER_MODE_CONTROL); 5148c2ecf20Sopenharmony_ci unsigned long gate = smc501_readl(sm->regs + SM501_CURRENT_GATE); 5158c2ecf20Sopenharmony_ci unsigned long clock = smc501_readl(sm->regs + SM501_CURRENT_CLOCK); 5168c2ecf20Sopenharmony_ci unsigned int pll_reg = 0; 5178c2ecf20Sopenharmony_ci unsigned long sm501_freq; /* the actual frequency achieved */ 5188c2ecf20Sopenharmony_ci u64 reg; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci struct sm501_clock to; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci /* find achivable discrete frequency and setup register value 5238c2ecf20Sopenharmony_ci * accordingly, V2XCLK, MCLK and M1XCLK are the same P2XCLK 5248c2ecf20Sopenharmony_ci * has an extra bit for the divider */ 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci switch (clksrc) { 5278c2ecf20Sopenharmony_ci case SM501_CLOCK_P2XCLK: 5288c2ecf20Sopenharmony_ci /* This clock is divided in half so to achieve the 5298c2ecf20Sopenharmony_ci * requested frequency the value must be multiplied by 5308c2ecf20Sopenharmony_ci * 2. This clock also has an additional pre divisor */ 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci if (sm->rev >= 0xC0) { 5338c2ecf20Sopenharmony_ci /* SM502 -> use the programmable PLL */ 5348c2ecf20Sopenharmony_ci sm501_freq = (sm501_calc_pll(2 * req_freq, 5358c2ecf20Sopenharmony_ci &to, 5) / 2); 5368c2ecf20Sopenharmony_ci reg = to.shift & 0x07;/* bottom 3 bits are shift */ 5378c2ecf20Sopenharmony_ci if (to.divider == 3) 5388c2ecf20Sopenharmony_ci reg |= 0x08; /* /3 divider required */ 5398c2ecf20Sopenharmony_ci else if (to.divider == 5) 5408c2ecf20Sopenharmony_ci reg |= 0x10; /* /5 divider required */ 5418c2ecf20Sopenharmony_ci reg |= 0x40; /* select the programmable PLL */ 5428c2ecf20Sopenharmony_ci pll_reg = 0x20000 | (to.k << 15) | (to.n << 8) | to.m; 5438c2ecf20Sopenharmony_ci } else { 5448c2ecf20Sopenharmony_ci sm501_freq = (sm501_select_clock(2 * req_freq, 5458c2ecf20Sopenharmony_ci &to, 5) / 2); 5468c2ecf20Sopenharmony_ci reg = to.shift & 0x07;/* bottom 3 bits are shift */ 5478c2ecf20Sopenharmony_ci if (to.divider == 3) 5488c2ecf20Sopenharmony_ci reg |= 0x08; /* /3 divider required */ 5498c2ecf20Sopenharmony_ci else if (to.divider == 5) 5508c2ecf20Sopenharmony_ci reg |= 0x10; /* /5 divider required */ 5518c2ecf20Sopenharmony_ci if (to.mclk != 288000000) 5528c2ecf20Sopenharmony_ci reg |= 0x20; /* which mclk pll is source */ 5538c2ecf20Sopenharmony_ci } 5548c2ecf20Sopenharmony_ci break; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci case SM501_CLOCK_V2XCLK: 5578c2ecf20Sopenharmony_ci /* This clock is divided in half so to achieve the 5588c2ecf20Sopenharmony_ci * requested frequency the value must be multiplied by 2. */ 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci sm501_freq = (sm501_select_clock(2 * req_freq, &to, 3) / 2); 5618c2ecf20Sopenharmony_ci reg=to.shift & 0x07; /* bottom 3 bits are shift */ 5628c2ecf20Sopenharmony_ci if (to.divider == 3) 5638c2ecf20Sopenharmony_ci reg |= 0x08; /* /3 divider required */ 5648c2ecf20Sopenharmony_ci if (to.mclk != 288000000) 5658c2ecf20Sopenharmony_ci reg |= 0x10; /* which mclk pll is source */ 5668c2ecf20Sopenharmony_ci break; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci case SM501_CLOCK_MCLK: 5698c2ecf20Sopenharmony_ci case SM501_CLOCK_M1XCLK: 5708c2ecf20Sopenharmony_ci /* These clocks are the same and not further divided */ 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci sm501_freq = sm501_select_clock( req_freq, &to, 3); 5738c2ecf20Sopenharmony_ci reg=to.shift & 0x07; /* bottom 3 bits are shift */ 5748c2ecf20Sopenharmony_ci if (to.divider == 3) 5758c2ecf20Sopenharmony_ci reg |= 0x08; /* /3 divider required */ 5768c2ecf20Sopenharmony_ci if (to.mclk != 288000000) 5778c2ecf20Sopenharmony_ci reg |= 0x10; /* which mclk pll is source */ 5788c2ecf20Sopenharmony_ci break; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci default: 5818c2ecf20Sopenharmony_ci return 0; /* this is bad */ 5828c2ecf20Sopenharmony_ci } 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci mutex_lock(&sm->clock_lock); 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci mode = smc501_readl(sm->regs + SM501_POWER_MODE_CONTROL); 5878c2ecf20Sopenharmony_ci gate = smc501_readl(sm->regs + SM501_CURRENT_GATE); 5888c2ecf20Sopenharmony_ci clock = smc501_readl(sm->regs + SM501_CURRENT_CLOCK); 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci clock = clock & ~(0xFF << clksrc); 5918c2ecf20Sopenharmony_ci clock |= reg<<clksrc; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci mode &= 3; /* find current mode */ 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci switch (mode) { 5968c2ecf20Sopenharmony_ci case 1: 5978c2ecf20Sopenharmony_ci smc501_writel(gate, sm->regs + SM501_POWER_MODE_0_GATE); 5988c2ecf20Sopenharmony_ci smc501_writel(clock, sm->regs + SM501_POWER_MODE_0_CLOCK); 5998c2ecf20Sopenharmony_ci mode = 0; 6008c2ecf20Sopenharmony_ci break; 6018c2ecf20Sopenharmony_ci case 2: 6028c2ecf20Sopenharmony_ci case 0: 6038c2ecf20Sopenharmony_ci smc501_writel(gate, sm->regs + SM501_POWER_MODE_1_GATE); 6048c2ecf20Sopenharmony_ci smc501_writel(clock, sm->regs + SM501_POWER_MODE_1_CLOCK); 6058c2ecf20Sopenharmony_ci mode = 1; 6068c2ecf20Sopenharmony_ci break; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci default: 6098c2ecf20Sopenharmony_ci mutex_unlock(&sm->clock_lock); 6108c2ecf20Sopenharmony_ci return -1; 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci smc501_writel(mode, sm->regs + SM501_POWER_MODE_CONTROL); 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci if (pll_reg) 6168c2ecf20Sopenharmony_ci smc501_writel(pll_reg, 6178c2ecf20Sopenharmony_ci sm->regs + SM501_PROGRAMMABLE_PLL_CONTROL); 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci sm501_sync_regs(sm); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci dev_dbg(sm->dev, "gate %08lx, clock %08lx, mode %08lx\n", 6228c2ecf20Sopenharmony_ci gate, clock, mode); 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci sm501_mdelay(sm, 16); 6258c2ecf20Sopenharmony_ci mutex_unlock(&sm->clock_lock); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci sm501_dump_clk(sm); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci return sm501_freq; 6308c2ecf20Sopenharmony_ci} 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sm501_set_clock); 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci/* sm501_find_clock 6358c2ecf20Sopenharmony_ci * 6368c2ecf20Sopenharmony_ci * finds the closest available frequency for a given clock 6378c2ecf20Sopenharmony_ci*/ 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ciunsigned long sm501_find_clock(struct device *dev, 6408c2ecf20Sopenharmony_ci int clksrc, 6418c2ecf20Sopenharmony_ci unsigned long req_freq) 6428c2ecf20Sopenharmony_ci{ 6438c2ecf20Sopenharmony_ci struct sm501_devdata *sm = dev_get_drvdata(dev); 6448c2ecf20Sopenharmony_ci unsigned long sm501_freq; /* the frequency achieveable by the 501 */ 6458c2ecf20Sopenharmony_ci struct sm501_clock to; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci switch (clksrc) { 6488c2ecf20Sopenharmony_ci case SM501_CLOCK_P2XCLK: 6498c2ecf20Sopenharmony_ci if (sm->rev >= 0xC0) { 6508c2ecf20Sopenharmony_ci /* SM502 -> use the programmable PLL */ 6518c2ecf20Sopenharmony_ci sm501_freq = (sm501_calc_pll(2 * req_freq, 6528c2ecf20Sopenharmony_ci &to, 5) / 2); 6538c2ecf20Sopenharmony_ci } else { 6548c2ecf20Sopenharmony_ci sm501_freq = (sm501_select_clock(2 * req_freq, 6558c2ecf20Sopenharmony_ci &to, 5) / 2); 6568c2ecf20Sopenharmony_ci } 6578c2ecf20Sopenharmony_ci break; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci case SM501_CLOCK_V2XCLK: 6608c2ecf20Sopenharmony_ci sm501_freq = (sm501_select_clock(2 * req_freq, &to, 3) / 2); 6618c2ecf20Sopenharmony_ci break; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci case SM501_CLOCK_MCLK: 6648c2ecf20Sopenharmony_ci case SM501_CLOCK_M1XCLK: 6658c2ecf20Sopenharmony_ci sm501_freq = sm501_select_clock(req_freq, &to, 3); 6668c2ecf20Sopenharmony_ci break; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci default: 6698c2ecf20Sopenharmony_ci sm501_freq = 0; /* error */ 6708c2ecf20Sopenharmony_ci } 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci return sm501_freq; 6738c2ecf20Sopenharmony_ci} 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sm501_find_clock); 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_cistatic struct sm501_device *to_sm_device(struct platform_device *pdev) 6788c2ecf20Sopenharmony_ci{ 6798c2ecf20Sopenharmony_ci return container_of(pdev, struct sm501_device, pdev); 6808c2ecf20Sopenharmony_ci} 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci/* sm501_device_release 6838c2ecf20Sopenharmony_ci * 6848c2ecf20Sopenharmony_ci * A release function for the platform devices we create to allow us to 6858c2ecf20Sopenharmony_ci * free any items we allocated 6868c2ecf20Sopenharmony_ci*/ 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_cistatic void sm501_device_release(struct device *dev) 6898c2ecf20Sopenharmony_ci{ 6908c2ecf20Sopenharmony_ci kfree(to_sm_device(to_platform_device(dev))); 6918c2ecf20Sopenharmony_ci} 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci/* sm501_create_subdev 6948c2ecf20Sopenharmony_ci * 6958c2ecf20Sopenharmony_ci * Create a skeleton platform device with resources for passing to a 6968c2ecf20Sopenharmony_ci * sub-driver 6978c2ecf20Sopenharmony_ci*/ 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_cistatic struct platform_device * 7008c2ecf20Sopenharmony_cism501_create_subdev(struct sm501_devdata *sm, char *name, 7018c2ecf20Sopenharmony_ci unsigned int res_count, unsigned int platform_data_size) 7028c2ecf20Sopenharmony_ci{ 7038c2ecf20Sopenharmony_ci struct sm501_device *smdev; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci smdev = kzalloc(sizeof(struct sm501_device) + 7068c2ecf20Sopenharmony_ci (sizeof(struct resource) * res_count) + 7078c2ecf20Sopenharmony_ci platform_data_size, GFP_KERNEL); 7088c2ecf20Sopenharmony_ci if (!smdev) 7098c2ecf20Sopenharmony_ci return NULL; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci smdev->pdev.dev.release = sm501_device_release; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci smdev->pdev.name = name; 7148c2ecf20Sopenharmony_ci smdev->pdev.id = sm->pdev_id; 7158c2ecf20Sopenharmony_ci smdev->pdev.dev.parent = sm->dev; 7168c2ecf20Sopenharmony_ci smdev->pdev.dev.coherent_dma_mask = 0xffffffff; 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci if (res_count) { 7198c2ecf20Sopenharmony_ci smdev->pdev.resource = (struct resource *)(smdev+1); 7208c2ecf20Sopenharmony_ci smdev->pdev.num_resources = res_count; 7218c2ecf20Sopenharmony_ci } 7228c2ecf20Sopenharmony_ci if (platform_data_size) 7238c2ecf20Sopenharmony_ci smdev->pdev.dev.platform_data = (void *)(smdev+1); 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci return &smdev->pdev; 7268c2ecf20Sopenharmony_ci} 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci/* sm501_register_device 7298c2ecf20Sopenharmony_ci * 7308c2ecf20Sopenharmony_ci * Register a platform device created with sm501_create_subdev() 7318c2ecf20Sopenharmony_ci*/ 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_cistatic int sm501_register_device(struct sm501_devdata *sm, 7348c2ecf20Sopenharmony_ci struct platform_device *pdev) 7358c2ecf20Sopenharmony_ci{ 7368c2ecf20Sopenharmony_ci struct sm501_device *smdev = to_sm_device(pdev); 7378c2ecf20Sopenharmony_ci int ptr; 7388c2ecf20Sopenharmony_ci int ret; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci for (ptr = 0; ptr < pdev->num_resources; ptr++) { 7418c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s[%d] %pR\n", 7428c2ecf20Sopenharmony_ci pdev->name, ptr, &pdev->resource[ptr]); 7438c2ecf20Sopenharmony_ci } 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci ret = platform_device_register(pdev); 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci if (ret >= 0) { 7488c2ecf20Sopenharmony_ci dev_dbg(sm->dev, "registered %s\n", pdev->name); 7498c2ecf20Sopenharmony_ci list_add_tail(&smdev->list, &sm->devices); 7508c2ecf20Sopenharmony_ci } else 7518c2ecf20Sopenharmony_ci dev_err(sm->dev, "error registering %s (%d)\n", 7528c2ecf20Sopenharmony_ci pdev->name, ret); 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci return ret; 7558c2ecf20Sopenharmony_ci} 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci/* sm501_create_subio 7588c2ecf20Sopenharmony_ci * 7598c2ecf20Sopenharmony_ci * Fill in an IO resource for a sub device 7608c2ecf20Sopenharmony_ci*/ 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_cistatic void sm501_create_subio(struct sm501_devdata *sm, 7638c2ecf20Sopenharmony_ci struct resource *res, 7648c2ecf20Sopenharmony_ci resource_size_t offs, 7658c2ecf20Sopenharmony_ci resource_size_t size) 7668c2ecf20Sopenharmony_ci{ 7678c2ecf20Sopenharmony_ci res->flags = IORESOURCE_MEM; 7688c2ecf20Sopenharmony_ci res->parent = sm->io_res; 7698c2ecf20Sopenharmony_ci res->start = sm->io_res->start + offs; 7708c2ecf20Sopenharmony_ci res->end = res->start + size - 1; 7718c2ecf20Sopenharmony_ci} 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci/* sm501_create_mem 7748c2ecf20Sopenharmony_ci * 7758c2ecf20Sopenharmony_ci * Fill in an MEM resource for a sub device 7768c2ecf20Sopenharmony_ci*/ 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_cistatic void sm501_create_mem(struct sm501_devdata *sm, 7798c2ecf20Sopenharmony_ci struct resource *res, 7808c2ecf20Sopenharmony_ci resource_size_t *offs, 7818c2ecf20Sopenharmony_ci resource_size_t size) 7828c2ecf20Sopenharmony_ci{ 7838c2ecf20Sopenharmony_ci *offs -= size; /* adjust memory size */ 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci res->flags = IORESOURCE_MEM; 7868c2ecf20Sopenharmony_ci res->parent = sm->mem_res; 7878c2ecf20Sopenharmony_ci res->start = sm->mem_res->start + *offs; 7888c2ecf20Sopenharmony_ci res->end = res->start + size - 1; 7898c2ecf20Sopenharmony_ci} 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci/* sm501_create_irq 7928c2ecf20Sopenharmony_ci * 7938c2ecf20Sopenharmony_ci * Fill in an IRQ resource for a sub device 7948c2ecf20Sopenharmony_ci*/ 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_cistatic void sm501_create_irq(struct sm501_devdata *sm, 7978c2ecf20Sopenharmony_ci struct resource *res) 7988c2ecf20Sopenharmony_ci{ 7998c2ecf20Sopenharmony_ci res->flags = IORESOURCE_IRQ; 8008c2ecf20Sopenharmony_ci res->parent = NULL; 8018c2ecf20Sopenharmony_ci res->start = res->end = sm->irq; 8028c2ecf20Sopenharmony_ci} 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_cistatic int sm501_register_usbhost(struct sm501_devdata *sm, 8058c2ecf20Sopenharmony_ci resource_size_t *mem_avail) 8068c2ecf20Sopenharmony_ci{ 8078c2ecf20Sopenharmony_ci struct platform_device *pdev; 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci pdev = sm501_create_subdev(sm, "sm501-usb", 3, 0); 8108c2ecf20Sopenharmony_ci if (!pdev) 8118c2ecf20Sopenharmony_ci return -ENOMEM; 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci sm501_create_subio(sm, &pdev->resource[0], 0x40000, 0x20000); 8148c2ecf20Sopenharmony_ci sm501_create_mem(sm, &pdev->resource[1], mem_avail, 256*1024); 8158c2ecf20Sopenharmony_ci sm501_create_irq(sm, &pdev->resource[2]); 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci return sm501_register_device(sm, pdev); 8188c2ecf20Sopenharmony_ci} 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_cistatic void sm501_setup_uart_data(struct sm501_devdata *sm, 8218c2ecf20Sopenharmony_ci struct plat_serial8250_port *uart_data, 8228c2ecf20Sopenharmony_ci unsigned int offset) 8238c2ecf20Sopenharmony_ci{ 8248c2ecf20Sopenharmony_ci uart_data->membase = sm->regs + offset; 8258c2ecf20Sopenharmony_ci uart_data->mapbase = sm->io_res->start + offset; 8268c2ecf20Sopenharmony_ci uart_data->iotype = UPIO_MEM; 8278c2ecf20Sopenharmony_ci uart_data->irq = sm->irq; 8288c2ecf20Sopenharmony_ci uart_data->flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_SHARE_IRQ; 8298c2ecf20Sopenharmony_ci uart_data->regshift = 2; 8308c2ecf20Sopenharmony_ci uart_data->uartclk = (9600 * 16); 8318c2ecf20Sopenharmony_ci} 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_cistatic int sm501_register_uart(struct sm501_devdata *sm, int devices) 8348c2ecf20Sopenharmony_ci{ 8358c2ecf20Sopenharmony_ci struct platform_device *pdev; 8368c2ecf20Sopenharmony_ci struct plat_serial8250_port *uart_data; 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci pdev = sm501_create_subdev(sm, "serial8250", 0, 8398c2ecf20Sopenharmony_ci sizeof(struct plat_serial8250_port) * 3); 8408c2ecf20Sopenharmony_ci if (!pdev) 8418c2ecf20Sopenharmony_ci return -ENOMEM; 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci uart_data = dev_get_platdata(&pdev->dev); 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci if (devices & SM501_USE_UART0) { 8468c2ecf20Sopenharmony_ci sm501_setup_uart_data(sm, uart_data++, 0x30000); 8478c2ecf20Sopenharmony_ci sm501_unit_power(sm->dev, SM501_GATE_UART0, 1); 8488c2ecf20Sopenharmony_ci sm501_modify_reg(sm->dev, SM501_IRQ_MASK, 1 << 12, 0); 8498c2ecf20Sopenharmony_ci sm501_modify_reg(sm->dev, SM501_GPIO63_32_CONTROL, 0x01e0, 0); 8508c2ecf20Sopenharmony_ci } 8518c2ecf20Sopenharmony_ci if (devices & SM501_USE_UART1) { 8528c2ecf20Sopenharmony_ci sm501_setup_uart_data(sm, uart_data++, 0x30020); 8538c2ecf20Sopenharmony_ci sm501_unit_power(sm->dev, SM501_GATE_UART1, 1); 8548c2ecf20Sopenharmony_ci sm501_modify_reg(sm->dev, SM501_IRQ_MASK, 1 << 13, 0); 8558c2ecf20Sopenharmony_ci sm501_modify_reg(sm->dev, SM501_GPIO63_32_CONTROL, 0x1e00, 0); 8568c2ecf20Sopenharmony_ci } 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci pdev->id = PLAT8250_DEV_SM501; 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci return sm501_register_device(sm, pdev); 8618c2ecf20Sopenharmony_ci} 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_cistatic int sm501_register_display(struct sm501_devdata *sm, 8648c2ecf20Sopenharmony_ci resource_size_t *mem_avail) 8658c2ecf20Sopenharmony_ci{ 8668c2ecf20Sopenharmony_ci struct platform_device *pdev; 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci pdev = sm501_create_subdev(sm, "sm501-fb", 4, 0); 8698c2ecf20Sopenharmony_ci if (!pdev) 8708c2ecf20Sopenharmony_ci return -ENOMEM; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci sm501_create_subio(sm, &pdev->resource[0], 0x80000, 0x10000); 8738c2ecf20Sopenharmony_ci sm501_create_subio(sm, &pdev->resource[1], 0x100000, 0x50000); 8748c2ecf20Sopenharmony_ci sm501_create_mem(sm, &pdev->resource[2], mem_avail, *mem_avail); 8758c2ecf20Sopenharmony_ci sm501_create_irq(sm, &pdev->resource[3]); 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci return sm501_register_device(sm, pdev); 8788c2ecf20Sopenharmony_ci} 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci#ifdef CONFIG_MFD_SM501_GPIO 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_cistatic inline struct sm501_devdata *sm501_gpio_to_dev(struct sm501_gpio *gpio) 8838c2ecf20Sopenharmony_ci{ 8848c2ecf20Sopenharmony_ci return container_of(gpio, struct sm501_devdata, gpio); 8858c2ecf20Sopenharmony_ci} 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_cistatic int sm501_gpio_get(struct gpio_chip *chip, unsigned offset) 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci{ 8908c2ecf20Sopenharmony_ci struct sm501_gpio_chip *smgpio = gpiochip_get_data(chip); 8918c2ecf20Sopenharmony_ci unsigned long result; 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci result = smc501_readl(smgpio->regbase + SM501_GPIO_DATA_LOW); 8948c2ecf20Sopenharmony_ci result >>= offset; 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci return result & 1UL; 8978c2ecf20Sopenharmony_ci} 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_cistatic void sm501_gpio_ensure_gpio(struct sm501_gpio_chip *smchip, 9008c2ecf20Sopenharmony_ci unsigned long bit) 9018c2ecf20Sopenharmony_ci{ 9028c2ecf20Sopenharmony_ci unsigned long ctrl; 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci /* check and modify if this pin is not set as gpio. */ 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci if (smc501_readl(smchip->control) & bit) { 9078c2ecf20Sopenharmony_ci dev_info(sm501_gpio_to_dev(smchip->ourgpio)->dev, 9088c2ecf20Sopenharmony_ci "changing mode of gpio, bit %08lx\n", bit); 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci ctrl = smc501_readl(smchip->control); 9118c2ecf20Sopenharmony_ci ctrl &= ~bit; 9128c2ecf20Sopenharmony_ci smc501_writel(ctrl, smchip->control); 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci sm501_sync_regs(sm501_gpio_to_dev(smchip->ourgpio)); 9158c2ecf20Sopenharmony_ci } 9168c2ecf20Sopenharmony_ci} 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_cistatic void sm501_gpio_set(struct gpio_chip *chip, unsigned offset, int value) 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci{ 9218c2ecf20Sopenharmony_ci struct sm501_gpio_chip *smchip = gpiochip_get_data(chip); 9228c2ecf20Sopenharmony_ci struct sm501_gpio *smgpio = smchip->ourgpio; 9238c2ecf20Sopenharmony_ci unsigned long bit = 1 << offset; 9248c2ecf20Sopenharmony_ci void __iomem *regs = smchip->regbase; 9258c2ecf20Sopenharmony_ci unsigned long save; 9268c2ecf20Sopenharmony_ci unsigned long val; 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci dev_dbg(sm501_gpio_to_dev(smgpio)->dev, "%s(%p,%d)\n", 9298c2ecf20Sopenharmony_ci __func__, chip, offset); 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci spin_lock_irqsave(&smgpio->lock, save); 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci val = smc501_readl(regs + SM501_GPIO_DATA_LOW) & ~bit; 9348c2ecf20Sopenharmony_ci if (value) 9358c2ecf20Sopenharmony_ci val |= bit; 9368c2ecf20Sopenharmony_ci smc501_writel(val, regs); 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci sm501_sync_regs(sm501_gpio_to_dev(smgpio)); 9398c2ecf20Sopenharmony_ci sm501_gpio_ensure_gpio(smchip, bit); 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&smgpio->lock, save); 9428c2ecf20Sopenharmony_ci} 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_cistatic int sm501_gpio_input(struct gpio_chip *chip, unsigned offset) 9458c2ecf20Sopenharmony_ci{ 9468c2ecf20Sopenharmony_ci struct sm501_gpio_chip *smchip = gpiochip_get_data(chip); 9478c2ecf20Sopenharmony_ci struct sm501_gpio *smgpio = smchip->ourgpio; 9488c2ecf20Sopenharmony_ci void __iomem *regs = smchip->regbase; 9498c2ecf20Sopenharmony_ci unsigned long bit = 1 << offset; 9508c2ecf20Sopenharmony_ci unsigned long save; 9518c2ecf20Sopenharmony_ci unsigned long ddr; 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci dev_dbg(sm501_gpio_to_dev(smgpio)->dev, "%s(%p,%d)\n", 9548c2ecf20Sopenharmony_ci __func__, chip, offset); 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci spin_lock_irqsave(&smgpio->lock, save); 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci ddr = smc501_readl(regs + SM501_GPIO_DDR_LOW); 9598c2ecf20Sopenharmony_ci smc501_writel(ddr & ~bit, regs + SM501_GPIO_DDR_LOW); 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci sm501_sync_regs(sm501_gpio_to_dev(smgpio)); 9628c2ecf20Sopenharmony_ci sm501_gpio_ensure_gpio(smchip, bit); 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&smgpio->lock, save); 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci return 0; 9678c2ecf20Sopenharmony_ci} 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_cistatic int sm501_gpio_output(struct gpio_chip *chip, 9708c2ecf20Sopenharmony_ci unsigned offset, int value) 9718c2ecf20Sopenharmony_ci{ 9728c2ecf20Sopenharmony_ci struct sm501_gpio_chip *smchip = gpiochip_get_data(chip); 9738c2ecf20Sopenharmony_ci struct sm501_gpio *smgpio = smchip->ourgpio; 9748c2ecf20Sopenharmony_ci unsigned long bit = 1 << offset; 9758c2ecf20Sopenharmony_ci void __iomem *regs = smchip->regbase; 9768c2ecf20Sopenharmony_ci unsigned long save; 9778c2ecf20Sopenharmony_ci unsigned long val; 9788c2ecf20Sopenharmony_ci unsigned long ddr; 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci dev_dbg(sm501_gpio_to_dev(smgpio)->dev, "%s(%p,%d,%d)\n", 9818c2ecf20Sopenharmony_ci __func__, chip, offset, value); 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci spin_lock_irqsave(&smgpio->lock, save); 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci val = smc501_readl(regs + SM501_GPIO_DATA_LOW); 9868c2ecf20Sopenharmony_ci if (value) 9878c2ecf20Sopenharmony_ci val |= bit; 9888c2ecf20Sopenharmony_ci else 9898c2ecf20Sopenharmony_ci val &= ~bit; 9908c2ecf20Sopenharmony_ci smc501_writel(val, regs); 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci ddr = smc501_readl(regs + SM501_GPIO_DDR_LOW); 9938c2ecf20Sopenharmony_ci smc501_writel(ddr | bit, regs + SM501_GPIO_DDR_LOW); 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci sm501_sync_regs(sm501_gpio_to_dev(smgpio)); 9968c2ecf20Sopenharmony_ci smc501_writel(val, regs + SM501_GPIO_DATA_LOW); 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci sm501_sync_regs(sm501_gpio_to_dev(smgpio)); 9998c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&smgpio->lock, save); 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci return 0; 10028c2ecf20Sopenharmony_ci} 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_cistatic const struct gpio_chip gpio_chip_template = { 10058c2ecf20Sopenharmony_ci .ngpio = 32, 10068c2ecf20Sopenharmony_ci .direction_input = sm501_gpio_input, 10078c2ecf20Sopenharmony_ci .direction_output = sm501_gpio_output, 10088c2ecf20Sopenharmony_ci .set = sm501_gpio_set, 10098c2ecf20Sopenharmony_ci .get = sm501_gpio_get, 10108c2ecf20Sopenharmony_ci}; 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_cistatic int sm501_gpio_register_chip(struct sm501_devdata *sm, 10138c2ecf20Sopenharmony_ci struct sm501_gpio *gpio, 10148c2ecf20Sopenharmony_ci struct sm501_gpio_chip *chip) 10158c2ecf20Sopenharmony_ci{ 10168c2ecf20Sopenharmony_ci struct sm501_platdata *pdata = sm->platdata; 10178c2ecf20Sopenharmony_ci struct gpio_chip *gchip = &chip->gpio; 10188c2ecf20Sopenharmony_ci int base = pdata->gpio_base; 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci chip->gpio = gpio_chip_template; 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci if (chip == &gpio->high) { 10238c2ecf20Sopenharmony_ci if (base > 0) 10248c2ecf20Sopenharmony_ci base += 32; 10258c2ecf20Sopenharmony_ci chip->regbase = gpio->regs + SM501_GPIO_DATA_HIGH; 10268c2ecf20Sopenharmony_ci chip->control = sm->regs + SM501_GPIO63_32_CONTROL; 10278c2ecf20Sopenharmony_ci gchip->label = "SM501-HIGH"; 10288c2ecf20Sopenharmony_ci } else { 10298c2ecf20Sopenharmony_ci chip->regbase = gpio->regs + SM501_GPIO_DATA_LOW; 10308c2ecf20Sopenharmony_ci chip->control = sm->regs + SM501_GPIO31_0_CONTROL; 10318c2ecf20Sopenharmony_ci gchip->label = "SM501-LOW"; 10328c2ecf20Sopenharmony_ci } 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci gchip->base = base; 10358c2ecf20Sopenharmony_ci chip->ourgpio = gpio; 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci return gpiochip_add_data(gchip, chip); 10388c2ecf20Sopenharmony_ci} 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_cistatic int sm501_register_gpio(struct sm501_devdata *sm) 10418c2ecf20Sopenharmony_ci{ 10428c2ecf20Sopenharmony_ci struct sm501_gpio *gpio = &sm->gpio; 10438c2ecf20Sopenharmony_ci resource_size_t iobase = sm->io_res->start + SM501_GPIO; 10448c2ecf20Sopenharmony_ci int ret; 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci dev_dbg(sm->dev, "registering gpio block %08llx\n", 10478c2ecf20Sopenharmony_ci (unsigned long long)iobase); 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci spin_lock_init(&gpio->lock); 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci gpio->regs_res = request_mem_region(iobase, 0x20, "sm501-gpio"); 10528c2ecf20Sopenharmony_ci if (!gpio->regs_res) { 10538c2ecf20Sopenharmony_ci dev_err(sm->dev, "gpio: failed to request region\n"); 10548c2ecf20Sopenharmony_ci return -ENXIO; 10558c2ecf20Sopenharmony_ci } 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci gpio->regs = ioremap(iobase, 0x20); 10588c2ecf20Sopenharmony_ci if (!gpio->regs) { 10598c2ecf20Sopenharmony_ci dev_err(sm->dev, "gpio: failed to remap registers\n"); 10608c2ecf20Sopenharmony_ci ret = -ENXIO; 10618c2ecf20Sopenharmony_ci goto err_claimed; 10628c2ecf20Sopenharmony_ci } 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci /* Register both our chips. */ 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci ret = sm501_gpio_register_chip(sm, gpio, &gpio->low); 10678c2ecf20Sopenharmony_ci if (ret) { 10688c2ecf20Sopenharmony_ci dev_err(sm->dev, "failed to add low chip\n"); 10698c2ecf20Sopenharmony_ci goto err_mapped; 10708c2ecf20Sopenharmony_ci } 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci ret = sm501_gpio_register_chip(sm, gpio, &gpio->high); 10738c2ecf20Sopenharmony_ci if (ret) { 10748c2ecf20Sopenharmony_ci dev_err(sm->dev, "failed to add high chip\n"); 10758c2ecf20Sopenharmony_ci goto err_low_chip; 10768c2ecf20Sopenharmony_ci } 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci gpio->registered = 1; 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci return 0; 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci err_low_chip: 10838c2ecf20Sopenharmony_ci gpiochip_remove(&gpio->low.gpio); 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci err_mapped: 10868c2ecf20Sopenharmony_ci iounmap(gpio->regs); 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci err_claimed: 10898c2ecf20Sopenharmony_ci release_mem_region(iobase, 0x20); 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci return ret; 10928c2ecf20Sopenharmony_ci} 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_cistatic void sm501_gpio_remove(struct sm501_devdata *sm) 10958c2ecf20Sopenharmony_ci{ 10968c2ecf20Sopenharmony_ci struct sm501_gpio *gpio = &sm->gpio; 10978c2ecf20Sopenharmony_ci resource_size_t iobase = sm->io_res->start + SM501_GPIO; 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci if (!sm->gpio.registered) 11008c2ecf20Sopenharmony_ci return; 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci gpiochip_remove(&gpio->low.gpio); 11038c2ecf20Sopenharmony_ci gpiochip_remove(&gpio->high.gpio); 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci iounmap(gpio->regs); 11068c2ecf20Sopenharmony_ci release_mem_region(iobase, 0x20); 11078c2ecf20Sopenharmony_ci} 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_cistatic inline int sm501_gpio_isregistered(struct sm501_devdata *sm) 11108c2ecf20Sopenharmony_ci{ 11118c2ecf20Sopenharmony_ci return sm->gpio.registered; 11128c2ecf20Sopenharmony_ci} 11138c2ecf20Sopenharmony_ci#else 11148c2ecf20Sopenharmony_cistatic inline int sm501_register_gpio(struct sm501_devdata *sm) 11158c2ecf20Sopenharmony_ci{ 11168c2ecf20Sopenharmony_ci return 0; 11178c2ecf20Sopenharmony_ci} 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_cistatic inline void sm501_gpio_remove(struct sm501_devdata *sm) 11208c2ecf20Sopenharmony_ci{ 11218c2ecf20Sopenharmony_ci} 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_cistatic inline int sm501_gpio_isregistered(struct sm501_devdata *sm) 11248c2ecf20Sopenharmony_ci{ 11258c2ecf20Sopenharmony_ci return 0; 11268c2ecf20Sopenharmony_ci} 11278c2ecf20Sopenharmony_ci#endif 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_cistatic int sm501_register_gpio_i2c_instance(struct sm501_devdata *sm, 11308c2ecf20Sopenharmony_ci struct sm501_platdata_gpio_i2c *iic) 11318c2ecf20Sopenharmony_ci{ 11328c2ecf20Sopenharmony_ci struct i2c_gpio_platform_data *icd; 11338c2ecf20Sopenharmony_ci struct platform_device *pdev; 11348c2ecf20Sopenharmony_ci struct gpiod_lookup_table *lookup; 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci pdev = sm501_create_subdev(sm, "i2c-gpio", 0, 11378c2ecf20Sopenharmony_ci sizeof(struct i2c_gpio_platform_data)); 11388c2ecf20Sopenharmony_ci if (!pdev) 11398c2ecf20Sopenharmony_ci return -ENOMEM; 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci /* Create a gpiod lookup using gpiochip-local offsets */ 11428c2ecf20Sopenharmony_ci lookup = devm_kzalloc(&pdev->dev, struct_size(lookup, table, 3), 11438c2ecf20Sopenharmony_ci GFP_KERNEL); 11448c2ecf20Sopenharmony_ci if (!lookup) 11458c2ecf20Sopenharmony_ci return -ENOMEM; 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci lookup->dev_id = "i2c-gpio"; 11488c2ecf20Sopenharmony_ci lookup->table[0] = (struct gpiod_lookup) 11498c2ecf20Sopenharmony_ci GPIO_LOOKUP_IDX(iic->pin_sda < 32 ? "SM501-LOW" : "SM501-HIGH", 11508c2ecf20Sopenharmony_ci iic->pin_sda % 32, NULL, 0, 11518c2ecf20Sopenharmony_ci GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN); 11528c2ecf20Sopenharmony_ci lookup->table[1] = (struct gpiod_lookup) 11538c2ecf20Sopenharmony_ci GPIO_LOOKUP_IDX(iic->pin_scl < 32 ? "SM501-LOW" : "SM501-HIGH", 11548c2ecf20Sopenharmony_ci iic->pin_scl % 32, NULL, 1, 11558c2ecf20Sopenharmony_ci GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN); 11568c2ecf20Sopenharmony_ci gpiod_add_lookup_table(lookup); 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci icd = dev_get_platdata(&pdev->dev); 11598c2ecf20Sopenharmony_ci icd->timeout = iic->timeout; 11608c2ecf20Sopenharmony_ci icd->udelay = iic->udelay; 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci /* note, we can't use either of the pin numbers, as the i2c-gpio 11638c2ecf20Sopenharmony_ci * driver uses the platform.id field to generate the bus number 11648c2ecf20Sopenharmony_ci * to register with the i2c core; The i2c core doesn't have enough 11658c2ecf20Sopenharmony_ci * entries to deal with anything we currently use. 11668c2ecf20Sopenharmony_ci */ 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci pdev->id = iic->bus_num; 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci dev_info(sm->dev, "registering i2c-%d: sda=%d, scl=%d\n", 11718c2ecf20Sopenharmony_ci iic->bus_num, 11728c2ecf20Sopenharmony_ci iic->pin_sda, iic->pin_scl); 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci return sm501_register_device(sm, pdev); 11758c2ecf20Sopenharmony_ci} 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_cistatic int sm501_register_gpio_i2c(struct sm501_devdata *sm, 11788c2ecf20Sopenharmony_ci struct sm501_platdata *pdata) 11798c2ecf20Sopenharmony_ci{ 11808c2ecf20Sopenharmony_ci struct sm501_platdata_gpio_i2c *iic = pdata->gpio_i2c; 11818c2ecf20Sopenharmony_ci int index; 11828c2ecf20Sopenharmony_ci int ret; 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci for (index = 0; index < pdata->gpio_i2c_nr; index++, iic++) { 11858c2ecf20Sopenharmony_ci ret = sm501_register_gpio_i2c_instance(sm, iic); 11868c2ecf20Sopenharmony_ci if (ret < 0) 11878c2ecf20Sopenharmony_ci return ret; 11888c2ecf20Sopenharmony_ci } 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci return 0; 11918c2ecf20Sopenharmony_ci} 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci/* sm501_dbg_regs 11948c2ecf20Sopenharmony_ci * 11958c2ecf20Sopenharmony_ci * Debug attribute to attach to parent device to show core registers 11968c2ecf20Sopenharmony_ci*/ 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_cistatic ssize_t sm501_dbg_regs(struct device *dev, 11998c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buff) 12008c2ecf20Sopenharmony_ci{ 12018c2ecf20Sopenharmony_ci struct sm501_devdata *sm = dev_get_drvdata(dev) ; 12028c2ecf20Sopenharmony_ci unsigned int reg; 12038c2ecf20Sopenharmony_ci char *ptr = buff; 12048c2ecf20Sopenharmony_ci int ret; 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci for (reg = 0x00; reg < 0x70; reg += 4) { 12078c2ecf20Sopenharmony_ci ret = sprintf(ptr, "%08x = %08x\n", 12088c2ecf20Sopenharmony_ci reg, smc501_readl(sm->regs + reg)); 12098c2ecf20Sopenharmony_ci ptr += ret; 12108c2ecf20Sopenharmony_ci } 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci return ptr - buff; 12138c2ecf20Sopenharmony_ci} 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_cistatic DEVICE_ATTR(dbg_regs, 0444, sm501_dbg_regs, NULL); 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci/* sm501_init_reg 12198c2ecf20Sopenharmony_ci * 12208c2ecf20Sopenharmony_ci * Helper function for the init code to setup a register 12218c2ecf20Sopenharmony_ci * 12228c2ecf20Sopenharmony_ci * clear the bits which are set in r->mask, and then set 12238c2ecf20Sopenharmony_ci * the bits set in r->set. 12248c2ecf20Sopenharmony_ci*/ 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_cistatic inline void sm501_init_reg(struct sm501_devdata *sm, 12278c2ecf20Sopenharmony_ci unsigned long reg, 12288c2ecf20Sopenharmony_ci struct sm501_reg_init *r) 12298c2ecf20Sopenharmony_ci{ 12308c2ecf20Sopenharmony_ci unsigned long tmp; 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci tmp = smc501_readl(sm->regs + reg); 12338c2ecf20Sopenharmony_ci tmp &= ~r->mask; 12348c2ecf20Sopenharmony_ci tmp |= r->set; 12358c2ecf20Sopenharmony_ci smc501_writel(tmp, sm->regs + reg); 12368c2ecf20Sopenharmony_ci} 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci/* sm501_init_regs 12398c2ecf20Sopenharmony_ci * 12408c2ecf20Sopenharmony_ci * Setup core register values 12418c2ecf20Sopenharmony_ci*/ 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_cistatic void sm501_init_regs(struct sm501_devdata *sm, 12448c2ecf20Sopenharmony_ci struct sm501_initdata *init) 12458c2ecf20Sopenharmony_ci{ 12468c2ecf20Sopenharmony_ci sm501_misc_control(sm->dev, 12478c2ecf20Sopenharmony_ci init->misc_control.set, 12488c2ecf20Sopenharmony_ci init->misc_control.mask); 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci sm501_init_reg(sm, SM501_MISC_TIMING, &init->misc_timing); 12518c2ecf20Sopenharmony_ci sm501_init_reg(sm, SM501_GPIO31_0_CONTROL, &init->gpio_low); 12528c2ecf20Sopenharmony_ci sm501_init_reg(sm, SM501_GPIO63_32_CONTROL, &init->gpio_high); 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci if (init->m1xclk) { 12558c2ecf20Sopenharmony_ci dev_info(sm->dev, "setting M1XCLK to %ld\n", init->m1xclk); 12568c2ecf20Sopenharmony_ci sm501_set_clock(sm->dev, SM501_CLOCK_M1XCLK, init->m1xclk); 12578c2ecf20Sopenharmony_ci } 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci if (init->mclk) { 12608c2ecf20Sopenharmony_ci dev_info(sm->dev, "setting MCLK to %ld\n", init->mclk); 12618c2ecf20Sopenharmony_ci sm501_set_clock(sm->dev, SM501_CLOCK_MCLK, init->mclk); 12628c2ecf20Sopenharmony_ci } 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci} 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci/* Check the PLL sources for the M1CLK and M1XCLK 12678c2ecf20Sopenharmony_ci * 12688c2ecf20Sopenharmony_ci * If the M1CLK and M1XCLKs are not sourced from the same PLL, then 12698c2ecf20Sopenharmony_ci * there is a risk (see errata AB-5) that the SM501 will cease proper 12708c2ecf20Sopenharmony_ci * function. If this happens, then it is likely the SM501 will 12718c2ecf20Sopenharmony_ci * hang the system. 12728c2ecf20Sopenharmony_ci*/ 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_cistatic int sm501_check_clocks(struct sm501_devdata *sm) 12758c2ecf20Sopenharmony_ci{ 12768c2ecf20Sopenharmony_ci unsigned long pwrmode = smc501_readl(sm->regs + SM501_CURRENT_CLOCK); 12778c2ecf20Sopenharmony_ci unsigned long msrc = (pwrmode & SM501_POWERMODE_M_SRC); 12788c2ecf20Sopenharmony_ci unsigned long m1src = (pwrmode & SM501_POWERMODE_M1_SRC); 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci return ((msrc == 0 && m1src != 0) || (msrc != 0 && m1src == 0)); 12818c2ecf20Sopenharmony_ci} 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_cistatic unsigned int sm501_mem_local[] = { 12848c2ecf20Sopenharmony_ci [0] = 4*1024*1024, 12858c2ecf20Sopenharmony_ci [1] = 8*1024*1024, 12868c2ecf20Sopenharmony_ci [2] = 16*1024*1024, 12878c2ecf20Sopenharmony_ci [3] = 32*1024*1024, 12888c2ecf20Sopenharmony_ci [4] = 64*1024*1024, 12898c2ecf20Sopenharmony_ci [5] = 2*1024*1024, 12908c2ecf20Sopenharmony_ci}; 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci/* sm501_init_dev 12938c2ecf20Sopenharmony_ci * 12948c2ecf20Sopenharmony_ci * Common init code for an SM501 12958c2ecf20Sopenharmony_ci*/ 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_cistatic int sm501_init_dev(struct sm501_devdata *sm) 12988c2ecf20Sopenharmony_ci{ 12998c2ecf20Sopenharmony_ci struct sm501_initdata *idata; 13008c2ecf20Sopenharmony_ci struct sm501_platdata *pdata; 13018c2ecf20Sopenharmony_ci resource_size_t mem_avail; 13028c2ecf20Sopenharmony_ci unsigned long dramctrl; 13038c2ecf20Sopenharmony_ci unsigned long devid; 13048c2ecf20Sopenharmony_ci int ret; 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_ci mutex_init(&sm->clock_lock); 13078c2ecf20Sopenharmony_ci spin_lock_init(&sm->reg_lock); 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&sm->devices); 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci devid = smc501_readl(sm->regs + SM501_DEVICEID); 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ci if ((devid & SM501_DEVICEID_IDMASK) != SM501_DEVICEID_SM501) { 13148c2ecf20Sopenharmony_ci dev_err(sm->dev, "incorrect device id %08lx\n", devid); 13158c2ecf20Sopenharmony_ci return -EINVAL; 13168c2ecf20Sopenharmony_ci } 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_ci /* disable irqs */ 13198c2ecf20Sopenharmony_ci smc501_writel(0, sm->regs + SM501_IRQ_MASK); 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_ci dramctrl = smc501_readl(sm->regs + SM501_DRAM_CONTROL); 13228c2ecf20Sopenharmony_ci mem_avail = sm501_mem_local[(dramctrl >> 13) & 0x7]; 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci dev_info(sm->dev, "SM501 At %p: Version %08lx, %ld Mb, IRQ %d\n", 13258c2ecf20Sopenharmony_ci sm->regs, devid, (unsigned long)mem_avail >> 20, sm->irq); 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci sm->rev = devid & SM501_DEVICEID_REVMASK; 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci sm501_dump_gate(sm); 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_ci ret = device_create_file(sm->dev, &dev_attr_dbg_regs); 13328c2ecf20Sopenharmony_ci if (ret) 13338c2ecf20Sopenharmony_ci dev_err(sm->dev, "failed to create debug regs file\n"); 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_ci sm501_dump_clk(sm); 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_ci /* check to see if we have some device initialisation */ 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci pdata = sm->platdata; 13408c2ecf20Sopenharmony_ci idata = pdata ? pdata->init : NULL; 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_ci if (idata) { 13438c2ecf20Sopenharmony_ci sm501_init_regs(sm, idata); 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci if (idata->devices & SM501_USE_USB_HOST) 13468c2ecf20Sopenharmony_ci sm501_register_usbhost(sm, &mem_avail); 13478c2ecf20Sopenharmony_ci if (idata->devices & (SM501_USE_UART0 | SM501_USE_UART1)) 13488c2ecf20Sopenharmony_ci sm501_register_uart(sm, idata->devices); 13498c2ecf20Sopenharmony_ci if (idata->devices & SM501_USE_GPIO) 13508c2ecf20Sopenharmony_ci sm501_register_gpio(sm); 13518c2ecf20Sopenharmony_ci } 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci if (pdata && pdata->gpio_i2c && pdata->gpio_i2c_nr > 0) { 13548c2ecf20Sopenharmony_ci if (!sm501_gpio_isregistered(sm)) 13558c2ecf20Sopenharmony_ci dev_err(sm->dev, "no gpio available for i2c gpio.\n"); 13568c2ecf20Sopenharmony_ci else 13578c2ecf20Sopenharmony_ci sm501_register_gpio_i2c(sm, pdata); 13588c2ecf20Sopenharmony_ci } 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_ci ret = sm501_check_clocks(sm); 13618c2ecf20Sopenharmony_ci if (ret) { 13628c2ecf20Sopenharmony_ci dev_err(sm->dev, "M1X and M clocks sourced from different " 13638c2ecf20Sopenharmony_ci "PLLs\n"); 13648c2ecf20Sopenharmony_ci return -EINVAL; 13658c2ecf20Sopenharmony_ci } 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci /* always create a framebuffer */ 13688c2ecf20Sopenharmony_ci sm501_register_display(sm, &mem_avail); 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_ci return 0; 13718c2ecf20Sopenharmony_ci} 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_cistatic int sm501_plat_probe(struct platform_device *dev) 13748c2ecf20Sopenharmony_ci{ 13758c2ecf20Sopenharmony_ci struct sm501_devdata *sm; 13768c2ecf20Sopenharmony_ci int ret; 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci sm = kzalloc(sizeof(*sm), GFP_KERNEL); 13798c2ecf20Sopenharmony_ci if (!sm) { 13808c2ecf20Sopenharmony_ci ret = -ENOMEM; 13818c2ecf20Sopenharmony_ci goto err1; 13828c2ecf20Sopenharmony_ci } 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci sm->dev = &dev->dev; 13858c2ecf20Sopenharmony_ci sm->pdev_id = dev->id; 13868c2ecf20Sopenharmony_ci sm->platdata = dev_get_platdata(&dev->dev); 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci ret = platform_get_irq(dev, 0); 13898c2ecf20Sopenharmony_ci if (ret < 0) 13908c2ecf20Sopenharmony_ci goto err_res; 13918c2ecf20Sopenharmony_ci sm->irq = ret; 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci sm->io_res = platform_get_resource(dev, IORESOURCE_MEM, 1); 13948c2ecf20Sopenharmony_ci sm->mem_res = platform_get_resource(dev, IORESOURCE_MEM, 0); 13958c2ecf20Sopenharmony_ci if (!sm->io_res || !sm->mem_res) { 13968c2ecf20Sopenharmony_ci dev_err(&dev->dev, "failed to get IO resource\n"); 13978c2ecf20Sopenharmony_ci ret = -ENOENT; 13988c2ecf20Sopenharmony_ci goto err_res; 13998c2ecf20Sopenharmony_ci } 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ci sm->regs_claim = request_mem_region(sm->io_res->start, 14028c2ecf20Sopenharmony_ci 0x100, "sm501"); 14038c2ecf20Sopenharmony_ci if (!sm->regs_claim) { 14048c2ecf20Sopenharmony_ci dev_err(&dev->dev, "cannot claim registers\n"); 14058c2ecf20Sopenharmony_ci ret = -EBUSY; 14068c2ecf20Sopenharmony_ci goto err_res; 14078c2ecf20Sopenharmony_ci } 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci platform_set_drvdata(dev, sm); 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_ci sm->regs = ioremap(sm->io_res->start, resource_size(sm->io_res)); 14128c2ecf20Sopenharmony_ci if (!sm->regs) { 14138c2ecf20Sopenharmony_ci dev_err(&dev->dev, "cannot remap registers\n"); 14148c2ecf20Sopenharmony_ci ret = -EIO; 14158c2ecf20Sopenharmony_ci goto err_claim; 14168c2ecf20Sopenharmony_ci } 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci ret = sm501_init_dev(sm); 14198c2ecf20Sopenharmony_ci if (ret) 14208c2ecf20Sopenharmony_ci goto err_unmap; 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci return 0; 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci err_unmap: 14258c2ecf20Sopenharmony_ci iounmap(sm->regs); 14268c2ecf20Sopenharmony_ci err_claim: 14278c2ecf20Sopenharmony_ci release_mem_region(sm->io_res->start, 0x100); 14288c2ecf20Sopenharmony_ci err_res: 14298c2ecf20Sopenharmony_ci kfree(sm); 14308c2ecf20Sopenharmony_ci err1: 14318c2ecf20Sopenharmony_ci return ret; 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_ci} 14348c2ecf20Sopenharmony_ci 14358c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci/* power management support */ 14388c2ecf20Sopenharmony_ci 14398c2ecf20Sopenharmony_cistatic void sm501_set_power(struct sm501_devdata *sm, int on) 14408c2ecf20Sopenharmony_ci{ 14418c2ecf20Sopenharmony_ci struct sm501_platdata *pd = sm->platdata; 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_ci if (!pd) 14448c2ecf20Sopenharmony_ci return; 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_ci if (pd->get_power) { 14478c2ecf20Sopenharmony_ci if (pd->get_power(sm->dev) == on) { 14488c2ecf20Sopenharmony_ci dev_dbg(sm->dev, "is already %d\n", on); 14498c2ecf20Sopenharmony_ci return; 14508c2ecf20Sopenharmony_ci } 14518c2ecf20Sopenharmony_ci } 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci if (pd->set_power) { 14548c2ecf20Sopenharmony_ci dev_dbg(sm->dev, "setting power to %d\n", on); 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ci pd->set_power(sm->dev, on); 14578c2ecf20Sopenharmony_ci sm501_mdelay(sm, 10); 14588c2ecf20Sopenharmony_ci } 14598c2ecf20Sopenharmony_ci} 14608c2ecf20Sopenharmony_ci 14618c2ecf20Sopenharmony_cistatic int sm501_plat_suspend(struct platform_device *pdev, pm_message_t state) 14628c2ecf20Sopenharmony_ci{ 14638c2ecf20Sopenharmony_ci struct sm501_devdata *sm = platform_get_drvdata(pdev); 14648c2ecf20Sopenharmony_ci 14658c2ecf20Sopenharmony_ci sm->in_suspend = 1; 14668c2ecf20Sopenharmony_ci sm->pm_misc = smc501_readl(sm->regs + SM501_MISC_CONTROL); 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_ci sm501_dump_regs(sm); 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_ci if (sm->platdata) { 14718c2ecf20Sopenharmony_ci if (sm->platdata->flags & SM501_FLAG_SUSPEND_OFF) 14728c2ecf20Sopenharmony_ci sm501_set_power(sm, 0); 14738c2ecf20Sopenharmony_ci } 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci return 0; 14768c2ecf20Sopenharmony_ci} 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_cistatic int sm501_plat_resume(struct platform_device *pdev) 14798c2ecf20Sopenharmony_ci{ 14808c2ecf20Sopenharmony_ci struct sm501_devdata *sm = platform_get_drvdata(pdev); 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci sm501_set_power(sm, 1); 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ci sm501_dump_regs(sm); 14858c2ecf20Sopenharmony_ci sm501_dump_gate(sm); 14868c2ecf20Sopenharmony_ci sm501_dump_clk(sm); 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_ci /* check to see if we are in the same state as when suspended */ 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_ci if (smc501_readl(sm->regs + SM501_MISC_CONTROL) != sm->pm_misc) { 14918c2ecf20Sopenharmony_ci dev_info(sm->dev, "SM501_MISC_CONTROL changed over sleep\n"); 14928c2ecf20Sopenharmony_ci smc501_writel(sm->pm_misc, sm->regs + SM501_MISC_CONTROL); 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_ci /* our suspend causes the controller state to change, 14958c2ecf20Sopenharmony_ci * either by something attempting setup, power loss, 14968c2ecf20Sopenharmony_ci * or an external reset event on power change */ 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_ci if (sm->platdata && sm->platdata->init) { 14998c2ecf20Sopenharmony_ci sm501_init_regs(sm, sm->platdata->init); 15008c2ecf20Sopenharmony_ci } 15018c2ecf20Sopenharmony_ci } 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci /* dump our state from resume */ 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci sm501_dump_regs(sm); 15068c2ecf20Sopenharmony_ci sm501_dump_clk(sm); 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_ci sm->in_suspend = 0; 15098c2ecf20Sopenharmony_ci 15108c2ecf20Sopenharmony_ci return 0; 15118c2ecf20Sopenharmony_ci} 15128c2ecf20Sopenharmony_ci#else 15138c2ecf20Sopenharmony_ci#define sm501_plat_suspend NULL 15148c2ecf20Sopenharmony_ci#define sm501_plat_resume NULL 15158c2ecf20Sopenharmony_ci#endif 15168c2ecf20Sopenharmony_ci 15178c2ecf20Sopenharmony_ci/* Initialisation data for PCI devices */ 15188c2ecf20Sopenharmony_ci 15198c2ecf20Sopenharmony_cistatic struct sm501_initdata sm501_pci_initdata = { 15208c2ecf20Sopenharmony_ci .gpio_high = { 15218c2ecf20Sopenharmony_ci .set = 0x3F000000, /* 24bit panel */ 15228c2ecf20Sopenharmony_ci .mask = 0x0, 15238c2ecf20Sopenharmony_ci }, 15248c2ecf20Sopenharmony_ci .misc_timing = { 15258c2ecf20Sopenharmony_ci .set = 0x010100, /* SDRAM timing */ 15268c2ecf20Sopenharmony_ci .mask = 0x1F1F00, 15278c2ecf20Sopenharmony_ci }, 15288c2ecf20Sopenharmony_ci .misc_control = { 15298c2ecf20Sopenharmony_ci .set = SM501_MISC_PNL_24BIT, 15308c2ecf20Sopenharmony_ci .mask = 0, 15318c2ecf20Sopenharmony_ci }, 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_ci .devices = SM501_USE_ALL, 15348c2ecf20Sopenharmony_ci 15358c2ecf20Sopenharmony_ci /* Errata AB-3 says that 72MHz is the fastest available 15368c2ecf20Sopenharmony_ci * for 33MHZ PCI with proper bus-mastering operation */ 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci .mclk = 72 * MHZ, 15398c2ecf20Sopenharmony_ci .m1xclk = 144 * MHZ, 15408c2ecf20Sopenharmony_ci}; 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_cistatic struct sm501_platdata_fbsub sm501_pdata_fbsub = { 15438c2ecf20Sopenharmony_ci .flags = (SM501FB_FLAG_USE_INIT_MODE | 15448c2ecf20Sopenharmony_ci SM501FB_FLAG_USE_HWCURSOR | 15458c2ecf20Sopenharmony_ci SM501FB_FLAG_USE_HWACCEL | 15468c2ecf20Sopenharmony_ci SM501FB_FLAG_DISABLE_AT_EXIT), 15478c2ecf20Sopenharmony_ci}; 15488c2ecf20Sopenharmony_ci 15498c2ecf20Sopenharmony_cistatic struct sm501_platdata_fb sm501_fb_pdata = { 15508c2ecf20Sopenharmony_ci .fb_route = SM501_FB_OWN, 15518c2ecf20Sopenharmony_ci .fb_crt = &sm501_pdata_fbsub, 15528c2ecf20Sopenharmony_ci .fb_pnl = &sm501_pdata_fbsub, 15538c2ecf20Sopenharmony_ci}; 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_cistatic struct sm501_platdata sm501_pci_platdata = { 15568c2ecf20Sopenharmony_ci .init = &sm501_pci_initdata, 15578c2ecf20Sopenharmony_ci .fb = &sm501_fb_pdata, 15588c2ecf20Sopenharmony_ci .gpio_base = -1, 15598c2ecf20Sopenharmony_ci}; 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_cistatic int sm501_pci_probe(struct pci_dev *dev, 15628c2ecf20Sopenharmony_ci const struct pci_device_id *id) 15638c2ecf20Sopenharmony_ci{ 15648c2ecf20Sopenharmony_ci struct sm501_devdata *sm; 15658c2ecf20Sopenharmony_ci int err; 15668c2ecf20Sopenharmony_ci 15678c2ecf20Sopenharmony_ci sm = kzalloc(sizeof(*sm), GFP_KERNEL); 15688c2ecf20Sopenharmony_ci if (!sm) { 15698c2ecf20Sopenharmony_ci err = -ENOMEM; 15708c2ecf20Sopenharmony_ci goto err1; 15718c2ecf20Sopenharmony_ci } 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_ci /* set a default set of platform data */ 15748c2ecf20Sopenharmony_ci dev->dev.platform_data = sm->platdata = &sm501_pci_platdata; 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_ci /* set a hopefully unique id for our child platform devices */ 15778c2ecf20Sopenharmony_ci sm->pdev_id = 32 + dev->devfn; 15788c2ecf20Sopenharmony_ci 15798c2ecf20Sopenharmony_ci pci_set_drvdata(dev, sm); 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci err = pci_enable_device(dev); 15828c2ecf20Sopenharmony_ci if (err) { 15838c2ecf20Sopenharmony_ci dev_err(&dev->dev, "cannot enable device\n"); 15848c2ecf20Sopenharmony_ci goto err2; 15858c2ecf20Sopenharmony_ci } 15868c2ecf20Sopenharmony_ci 15878c2ecf20Sopenharmony_ci sm->dev = &dev->dev; 15888c2ecf20Sopenharmony_ci sm->irq = dev->irq; 15898c2ecf20Sopenharmony_ci 15908c2ecf20Sopenharmony_ci#ifdef __BIG_ENDIAN 15918c2ecf20Sopenharmony_ci /* if the system is big-endian, we most probably have a 15928c2ecf20Sopenharmony_ci * translation in the IO layer making the PCI bus little endian 15938c2ecf20Sopenharmony_ci * so make the framebuffer swapped pixels */ 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_ci sm501_fb_pdata.flags |= SM501_FBPD_SWAP_FB_ENDIAN; 15968c2ecf20Sopenharmony_ci#endif 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_ci /* check our resources */ 15998c2ecf20Sopenharmony_ci 16008c2ecf20Sopenharmony_ci if (!(pci_resource_flags(dev, 0) & IORESOURCE_MEM)) { 16018c2ecf20Sopenharmony_ci dev_err(&dev->dev, "region #0 is not memory?\n"); 16028c2ecf20Sopenharmony_ci err = -EINVAL; 16038c2ecf20Sopenharmony_ci goto err3; 16048c2ecf20Sopenharmony_ci } 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_ci if (!(pci_resource_flags(dev, 1) & IORESOURCE_MEM)) { 16078c2ecf20Sopenharmony_ci dev_err(&dev->dev, "region #1 is not memory?\n"); 16088c2ecf20Sopenharmony_ci err = -EINVAL; 16098c2ecf20Sopenharmony_ci goto err3; 16108c2ecf20Sopenharmony_ci } 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci /* make our resources ready for sharing */ 16138c2ecf20Sopenharmony_ci 16148c2ecf20Sopenharmony_ci sm->io_res = &dev->resource[1]; 16158c2ecf20Sopenharmony_ci sm->mem_res = &dev->resource[0]; 16168c2ecf20Sopenharmony_ci 16178c2ecf20Sopenharmony_ci sm->regs_claim = request_mem_region(sm->io_res->start, 16188c2ecf20Sopenharmony_ci 0x100, "sm501"); 16198c2ecf20Sopenharmony_ci if (!sm->regs_claim) { 16208c2ecf20Sopenharmony_ci dev_err(&dev->dev, "cannot claim registers\n"); 16218c2ecf20Sopenharmony_ci err= -EBUSY; 16228c2ecf20Sopenharmony_ci goto err3; 16238c2ecf20Sopenharmony_ci } 16248c2ecf20Sopenharmony_ci 16258c2ecf20Sopenharmony_ci sm->regs = pci_ioremap_bar(dev, 1); 16268c2ecf20Sopenharmony_ci if (!sm->regs) { 16278c2ecf20Sopenharmony_ci dev_err(&dev->dev, "cannot remap registers\n"); 16288c2ecf20Sopenharmony_ci err = -EIO; 16298c2ecf20Sopenharmony_ci goto err4; 16308c2ecf20Sopenharmony_ci } 16318c2ecf20Sopenharmony_ci 16328c2ecf20Sopenharmony_ci sm501_init_dev(sm); 16338c2ecf20Sopenharmony_ci return 0; 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_ci err4: 16368c2ecf20Sopenharmony_ci release_mem_region(sm->io_res->start, 0x100); 16378c2ecf20Sopenharmony_ci err3: 16388c2ecf20Sopenharmony_ci pci_disable_device(dev); 16398c2ecf20Sopenharmony_ci err2: 16408c2ecf20Sopenharmony_ci kfree(sm); 16418c2ecf20Sopenharmony_ci err1: 16428c2ecf20Sopenharmony_ci return err; 16438c2ecf20Sopenharmony_ci} 16448c2ecf20Sopenharmony_ci 16458c2ecf20Sopenharmony_cistatic void sm501_remove_sub(struct sm501_devdata *sm, 16468c2ecf20Sopenharmony_ci struct sm501_device *smdev) 16478c2ecf20Sopenharmony_ci{ 16488c2ecf20Sopenharmony_ci list_del(&smdev->list); 16498c2ecf20Sopenharmony_ci platform_device_unregister(&smdev->pdev); 16508c2ecf20Sopenharmony_ci} 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_cistatic void sm501_dev_remove(struct sm501_devdata *sm) 16538c2ecf20Sopenharmony_ci{ 16548c2ecf20Sopenharmony_ci struct sm501_device *smdev, *tmp; 16558c2ecf20Sopenharmony_ci 16568c2ecf20Sopenharmony_ci list_for_each_entry_safe(smdev, tmp, &sm->devices, list) 16578c2ecf20Sopenharmony_ci sm501_remove_sub(sm, smdev); 16588c2ecf20Sopenharmony_ci 16598c2ecf20Sopenharmony_ci device_remove_file(sm->dev, &dev_attr_dbg_regs); 16608c2ecf20Sopenharmony_ci 16618c2ecf20Sopenharmony_ci sm501_gpio_remove(sm); 16628c2ecf20Sopenharmony_ci} 16638c2ecf20Sopenharmony_ci 16648c2ecf20Sopenharmony_cistatic void sm501_pci_remove(struct pci_dev *dev) 16658c2ecf20Sopenharmony_ci{ 16668c2ecf20Sopenharmony_ci struct sm501_devdata *sm = pci_get_drvdata(dev); 16678c2ecf20Sopenharmony_ci 16688c2ecf20Sopenharmony_ci sm501_dev_remove(sm); 16698c2ecf20Sopenharmony_ci iounmap(sm->regs); 16708c2ecf20Sopenharmony_ci 16718c2ecf20Sopenharmony_ci release_mem_region(sm->io_res->start, 0x100); 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_ci pci_disable_device(dev); 16748c2ecf20Sopenharmony_ci} 16758c2ecf20Sopenharmony_ci 16768c2ecf20Sopenharmony_cistatic int sm501_plat_remove(struct platform_device *dev) 16778c2ecf20Sopenharmony_ci{ 16788c2ecf20Sopenharmony_ci struct sm501_devdata *sm = platform_get_drvdata(dev); 16798c2ecf20Sopenharmony_ci 16808c2ecf20Sopenharmony_ci sm501_dev_remove(sm); 16818c2ecf20Sopenharmony_ci iounmap(sm->regs); 16828c2ecf20Sopenharmony_ci 16838c2ecf20Sopenharmony_ci release_mem_region(sm->io_res->start, 0x100); 16848c2ecf20Sopenharmony_ci 16858c2ecf20Sopenharmony_ci return 0; 16868c2ecf20Sopenharmony_ci} 16878c2ecf20Sopenharmony_ci 16888c2ecf20Sopenharmony_cistatic const struct pci_device_id sm501_pci_tbl[] = { 16898c2ecf20Sopenharmony_ci { 0x126f, 0x0501, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, 16908c2ecf20Sopenharmony_ci { 0, }, 16918c2ecf20Sopenharmony_ci}; 16928c2ecf20Sopenharmony_ci 16938c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, sm501_pci_tbl); 16948c2ecf20Sopenharmony_ci 16958c2ecf20Sopenharmony_cistatic struct pci_driver sm501_pci_driver = { 16968c2ecf20Sopenharmony_ci .name = "sm501", 16978c2ecf20Sopenharmony_ci .id_table = sm501_pci_tbl, 16988c2ecf20Sopenharmony_ci .probe = sm501_pci_probe, 16998c2ecf20Sopenharmony_ci .remove = sm501_pci_remove, 17008c2ecf20Sopenharmony_ci}; 17018c2ecf20Sopenharmony_ci 17028c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:sm501"); 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_cistatic const struct of_device_id of_sm501_match_tbl[] = { 17058c2ecf20Sopenharmony_ci { .compatible = "smi,sm501", }, 17068c2ecf20Sopenharmony_ci { /* end */ } 17078c2ecf20Sopenharmony_ci}; 17088c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, of_sm501_match_tbl); 17098c2ecf20Sopenharmony_ci 17108c2ecf20Sopenharmony_cistatic struct platform_driver sm501_plat_driver = { 17118c2ecf20Sopenharmony_ci .driver = { 17128c2ecf20Sopenharmony_ci .name = "sm501", 17138c2ecf20Sopenharmony_ci .of_match_table = of_sm501_match_tbl, 17148c2ecf20Sopenharmony_ci }, 17158c2ecf20Sopenharmony_ci .probe = sm501_plat_probe, 17168c2ecf20Sopenharmony_ci .remove = sm501_plat_remove, 17178c2ecf20Sopenharmony_ci .suspend = sm501_plat_suspend, 17188c2ecf20Sopenharmony_ci .resume = sm501_plat_resume, 17198c2ecf20Sopenharmony_ci}; 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_cistatic int __init sm501_base_init(void) 17228c2ecf20Sopenharmony_ci{ 17238c2ecf20Sopenharmony_ci int ret; 17248c2ecf20Sopenharmony_ci 17258c2ecf20Sopenharmony_ci ret = platform_driver_register(&sm501_plat_driver); 17268c2ecf20Sopenharmony_ci if (ret < 0) 17278c2ecf20Sopenharmony_ci return ret; 17288c2ecf20Sopenharmony_ci 17298c2ecf20Sopenharmony_ci return pci_register_driver(&sm501_pci_driver); 17308c2ecf20Sopenharmony_ci} 17318c2ecf20Sopenharmony_ci 17328c2ecf20Sopenharmony_cistatic void __exit sm501_base_exit(void) 17338c2ecf20Sopenharmony_ci{ 17348c2ecf20Sopenharmony_ci platform_driver_unregister(&sm501_plat_driver); 17358c2ecf20Sopenharmony_ci pci_unregister_driver(&sm501_pci_driver); 17368c2ecf20Sopenharmony_ci} 17378c2ecf20Sopenharmony_ci 17388c2ecf20Sopenharmony_cimodule_init(sm501_base_init); 17398c2ecf20Sopenharmony_cimodule_exit(sm501_base_exit); 17408c2ecf20Sopenharmony_ci 17418c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SM501 Core Driver"); 17428c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>, Vincent Sanders"); 17438c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1744