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