diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-08-05 11:53:20 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-08-05 11:53:20 -0400 |
commit | c3d1f1746b966907ba5ad2f75ddca24db8b21147 (patch) | |
tree | 548a25e104d8bdb906030b8d3bf78fbfde0e5817 /drivers | |
parent | 66eddbfcc1f6610fa7c73c8d20a57eaf8e284e2f (diff) | |
parent | 0d365753d0b7c26043fdfa97790411606fb40112 (diff) |
Merge branch 'upstream' of git://git.linux-mips.org/pub/scm/upstream-linus
* 'upstream' of git://git.linux-mips.org/pub/scm/upstream-linus: (150 commits)
MIPS: PowerTV: Separate PowerTV USB support from non-USB code
MIPS: strip the un-needed sections of vmlinuz
MIPS: Clean up the calculation of VMLINUZ_LOAD_ADDRESS
MIPS: Clean up arch/mips/boot/compressed/decompress.c
MIPS: Clean up arch/mips/boot/compressed/ld.script
MIPS: Unify the suffix of compressed vmlinux.bin
MIPS: PowerTV: Add Gaia platform definitions.
MIPS: BCM47xx: Fix nvram_getenv return value.
MIPS: Octeon: Allow more than 3.75GB of memory with PCIe
MIPS: Clean up notify_die() usage.
MIPS: Remove unused task_struct.trap_no field.
Documentation: Mention that KProbes is supported on MIPS
SAMPLES: kprobe_example: Make it print something on MIPS.
MIPS: kprobe: Add support.
MIPS: Add instrunction format for BREAK and SYSCALL
MIPS: kprobes: Define regs_return_value()
MIPS: Ritually kill stupid printk.
MIPS: Octeon: Disallow MSI-X interrupt and fall back to MSI interrupts.
MIPS: Octeon: Support 256 MSI on PCIe
MIPS: Decode core number for R2 CPUs.
...
Diffstat (limited to 'drivers')
31 files changed, 4603 insertions, 50 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index e19cf8eb6ccf..c57e530d07c7 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig | |||
@@ -446,6 +446,16 @@ config SENSORS_IT87 | |||
446 | This driver can also be built as a module. If so, the module | 446 | This driver can also be built as a module. If so, the module |
447 | will be called it87. | 447 | will be called it87. |
448 | 448 | ||
449 | config SENSORS_JZ4740 | ||
450 | tristate "Ingenic JZ4740 SoC ADC driver" | ||
451 | depends on MACH_JZ4740 && MFD_JZ4740_ADC | ||
452 | help | ||
453 | If you say yes here you get support for reading adc values from the ADCIN | ||
454 | pin on Ingenic JZ4740 SoC based boards. | ||
455 | |||
456 | This driver can also be build as a module. If so, the module will be | ||
457 | called jz4740-hwmon. | ||
458 | |||
449 | config SENSORS_LM63 | 459 | config SENSORS_LM63 |
450 | tristate "National Semiconductor LM63 and LM64" | 460 | tristate "National Semiconductor LM63 and LM64" |
451 | depends on I2C | 461 | depends on I2C |
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 2138ceb1a713..c5057745b068 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile | |||
@@ -55,6 +55,7 @@ obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o | |||
55 | obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o | 55 | obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o |
56 | obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o | 56 | obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o |
57 | obj-$(CONFIG_SENSORS_IT87) += it87.o | 57 | obj-$(CONFIG_SENSORS_IT87) += it87.o |
58 | obj-$(CONFIG_SENSORS_JZ4740) += jz4740-hwmon.o | ||
58 | obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o | 59 | obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o |
59 | obj-$(CONFIG_SENSORS_K10TEMP) += k10temp.o | 60 | obj-$(CONFIG_SENSORS_K10TEMP) += k10temp.o |
60 | obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o hp_accel.o | 61 | obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o hp_accel.o |
diff --git a/drivers/hwmon/jz4740-hwmon.c b/drivers/hwmon/jz4740-hwmon.c new file mode 100644 index 000000000000..1c8b3d9e2051 --- /dev/null +++ b/drivers/hwmon/jz4740-hwmon.c | |||
@@ -0,0 +1,230 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> | ||
3 | * JZ4740 SoC HWMON driver | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License as published by the | ||
7 | * Free Software Foundation; either version 2 of the License, or (at your | ||
8 | * option) any later version. | ||
9 | * | ||
10 | * You should have received a copy of the GNU General Public License along | ||
11 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
12 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #include <linux/err.h> | ||
17 | #include <linux/interrupt.h> | ||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/module.h> | ||
20 | #include <linux/mutex.h> | ||
21 | #include <linux/platform_device.h> | ||
22 | #include <linux/slab.h> | ||
23 | |||
24 | #include <linux/completion.h> | ||
25 | #include <linux/mfd/core.h> | ||
26 | |||
27 | #include <linux/hwmon.h> | ||
28 | |||
29 | struct jz4740_hwmon { | ||
30 | struct resource *mem; | ||
31 | void __iomem *base; | ||
32 | |||
33 | int irq; | ||
34 | |||
35 | struct mfd_cell *cell; | ||
36 | struct device *hwmon; | ||
37 | |||
38 | struct completion read_completion; | ||
39 | |||
40 | struct mutex lock; | ||
41 | }; | ||
42 | |||
43 | static ssize_t jz4740_hwmon_show_name(struct device *dev, | ||
44 | struct device_attribute *dev_attr, char *buf) | ||
45 | { | ||
46 | return sprintf(buf, "jz4740\n"); | ||
47 | } | ||
48 | |||
49 | static irqreturn_t jz4740_hwmon_irq(int irq, void *data) | ||
50 | { | ||
51 | struct jz4740_hwmon *hwmon = data; | ||
52 | |||
53 | complete(&hwmon->read_completion); | ||
54 | return IRQ_HANDLED; | ||
55 | } | ||
56 | |||
57 | static ssize_t jz4740_hwmon_read_adcin(struct device *dev, | ||
58 | struct device_attribute *dev_attr, char *buf) | ||
59 | { | ||
60 | struct jz4740_hwmon *hwmon = dev_get_drvdata(dev); | ||
61 | struct completion *completion = &hwmon->read_completion; | ||
62 | unsigned long t; | ||
63 | unsigned long val; | ||
64 | int ret; | ||
65 | |||
66 | mutex_lock(&hwmon->lock); | ||
67 | |||
68 | INIT_COMPLETION(*completion); | ||
69 | |||
70 | enable_irq(hwmon->irq); | ||
71 | hwmon->cell->enable(to_platform_device(dev)); | ||
72 | |||
73 | t = wait_for_completion_interruptible_timeout(completion, HZ); | ||
74 | |||
75 | if (t > 0) { | ||
76 | val = readw(hwmon->base) & 0xfff; | ||
77 | val = (val * 3300) >> 12; | ||
78 | ret = sprintf(buf, "%lu\n", val); | ||
79 | } else { | ||
80 | ret = t ? t : -ETIMEDOUT; | ||
81 | } | ||
82 | |||
83 | hwmon->cell->disable(to_platform_device(dev)); | ||
84 | disable_irq(hwmon->irq); | ||
85 | |||
86 | mutex_unlock(&hwmon->lock); | ||
87 | |||
88 | return ret; | ||
89 | } | ||
90 | |||
91 | static DEVICE_ATTR(name, S_IRUGO, jz4740_hwmon_show_name, NULL); | ||
92 | static DEVICE_ATTR(in0_input, S_IRUGO, jz4740_hwmon_read_adcin, NULL); | ||
93 | |||
94 | static struct attribute *jz4740_hwmon_attributes[] = { | ||
95 | &dev_attr_name.attr, | ||
96 | &dev_attr_in0_input.attr, | ||
97 | NULL | ||
98 | }; | ||
99 | |||
100 | static const struct attribute_group jz4740_hwmon_attr_group = { | ||
101 | .attrs = jz4740_hwmon_attributes, | ||
102 | }; | ||
103 | |||
104 | static int __devinit jz4740_hwmon_probe(struct platform_device *pdev) | ||
105 | { | ||
106 | int ret; | ||
107 | struct jz4740_hwmon *hwmon; | ||
108 | |||
109 | hwmon = kmalloc(sizeof(*hwmon), GFP_KERNEL); | ||
110 | if (!hwmon) { | ||
111 | dev_err(&pdev->dev, "Failed to allocate driver structure\n"); | ||
112 | return -ENOMEM; | ||
113 | } | ||
114 | |||
115 | hwmon->cell = pdev->dev.platform_data; | ||
116 | |||
117 | hwmon->irq = platform_get_irq(pdev, 0); | ||
118 | if (hwmon->irq < 0) { | ||
119 | ret = hwmon->irq; | ||
120 | dev_err(&pdev->dev, "Failed to get platform irq: %d\n", ret); | ||
121 | goto err_free; | ||
122 | } | ||
123 | |||
124 | hwmon->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
125 | if (!hwmon->mem) { | ||
126 | ret = -ENOENT; | ||
127 | dev_err(&pdev->dev, "Failed to get platform mmio resource\n"); | ||
128 | goto err_free; | ||
129 | } | ||
130 | |||
131 | hwmon->mem = request_mem_region(hwmon->mem->start, | ||
132 | resource_size(hwmon->mem), pdev->name); | ||
133 | if (!hwmon->mem) { | ||
134 | ret = -EBUSY; | ||
135 | dev_err(&pdev->dev, "Failed to request mmio memory region\n"); | ||
136 | goto err_free; | ||
137 | } | ||
138 | |||
139 | hwmon->base = ioremap_nocache(hwmon->mem->start, | ||
140 | resource_size(hwmon->mem)); | ||
141 | if (!hwmon->base) { | ||
142 | ret = -EBUSY; | ||
143 | dev_err(&pdev->dev, "Failed to ioremap mmio memory\n"); | ||
144 | goto err_release_mem_region; | ||
145 | } | ||
146 | |||
147 | init_completion(&hwmon->read_completion); | ||
148 | mutex_init(&hwmon->lock); | ||
149 | |||
150 | platform_set_drvdata(pdev, hwmon); | ||
151 | |||
152 | ret = request_irq(hwmon->irq, jz4740_hwmon_irq, 0, pdev->name, hwmon); | ||
153 | if (ret) { | ||
154 | dev_err(&pdev->dev, "Failed to request irq: %d\n", ret); | ||
155 | goto err_iounmap; | ||
156 | } | ||
157 | disable_irq(hwmon->irq); | ||
158 | |||
159 | ret = sysfs_create_group(&pdev->dev.kobj, &jz4740_hwmon_attr_group); | ||
160 | if (ret) { | ||
161 | dev_err(&pdev->dev, "Failed to create sysfs group: %d\n", ret); | ||
162 | goto err_free_irq; | ||
163 | } | ||
164 | |||
165 | hwmon->hwmon = hwmon_device_register(&pdev->dev); | ||
166 | if (IS_ERR(hwmon->hwmon)) { | ||
167 | ret = PTR_ERR(hwmon->hwmon); | ||
168 | goto err_remove_file; | ||
169 | } | ||
170 | |||
171 | return 0; | ||
172 | |||
173 | err_remove_file: | ||
174 | sysfs_remove_group(&pdev->dev.kobj, &jz4740_hwmon_attr_group); | ||
175 | err_free_irq: | ||
176 | free_irq(hwmon->irq, hwmon); | ||
177 | err_iounmap: | ||
178 | platform_set_drvdata(pdev, NULL); | ||
179 | iounmap(hwmon->base); | ||
180 | err_release_mem_region: | ||
181 | release_mem_region(hwmon->mem->start, resource_size(hwmon->mem)); | ||
182 | err_free: | ||
183 | kfree(hwmon); | ||
184 | |||
185 | return ret; | ||
186 | } | ||
187 | |||
188 | static int __devexit jz4740_hwmon_remove(struct platform_device *pdev) | ||
189 | { | ||
190 | struct jz4740_hwmon *hwmon = platform_get_drvdata(pdev); | ||
191 | |||
192 | hwmon_device_unregister(hwmon->hwmon); | ||
193 | sysfs_remove_group(&pdev->dev.kobj, &jz4740_hwmon_attr_group); | ||
194 | |||
195 | free_irq(hwmon->irq, hwmon); | ||
196 | |||
197 | iounmap(hwmon->base); | ||
198 | release_mem_region(hwmon->mem->start, resource_size(hwmon->mem)); | ||
199 | |||
200 | platform_set_drvdata(pdev, NULL); | ||
201 | kfree(hwmon); | ||
202 | |||
203 | return 0; | ||
204 | } | ||
205 | |||
206 | struct platform_driver jz4740_hwmon_driver = { | ||
207 | .probe = jz4740_hwmon_probe, | ||
208 | .remove = __devexit_p(jz4740_hwmon_remove), | ||
209 | .driver = { | ||
210 | .name = "jz4740-hwmon", | ||
211 | .owner = THIS_MODULE, | ||
212 | }, | ||
213 | }; | ||
214 | |||
215 | static int __init jz4740_hwmon_init(void) | ||
216 | { | ||
217 | return platform_driver_register(&jz4740_hwmon_driver); | ||
218 | } | ||
219 | module_init(jz4740_hwmon_init); | ||
220 | |||
221 | static void __exit jz4740_hwmon_exit(void) | ||
222 | { | ||
223 | platform_driver_unregister(&jz4740_hwmon_driver); | ||
224 | } | ||
225 | module_exit(jz4740_hwmon_exit); | ||
226 | |||
227 | MODULE_DESCRIPTION("JZ4740 SoC HWMON driver"); | ||
228 | MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); | ||
229 | MODULE_LICENSE("GPL"); | ||
230 | MODULE_ALIAS("platform:jz4740-hwmon"); | ||
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index f06d06e7fdfa..d25e22cee4c4 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig | |||
@@ -432,3 +432,12 @@ config MMC_SH_MMCIF | |||
432 | This selects the MMC Host Interface controler (MMCIF). | 432 | This selects the MMC Host Interface controler (MMCIF). |
433 | 433 | ||
434 | This driver supports MMCIF in sh7724/sh7757/sh7372. | 434 | This driver supports MMCIF in sh7724/sh7757/sh7372. |
435 | |||
436 | config MMC_JZ4740 | ||
437 | tristate "JZ4740 SD/Multimedia Card Interface support" | ||
438 | depends on MACH_JZ4740 | ||
439 | help | ||
440 | This selects support for the SD/MMC controller on Ingenic JZ4740 | ||
441 | SoCs. | ||
442 | If you have a board based on such a SoC and with a SD/MMC slot, | ||
443 | say Y or M here. | ||
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index e30c2ee48894..f4e53c98d944 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile | |||
@@ -36,6 +36,7 @@ obj-$(CONFIG_MMC_CB710) += cb710-mmc.o | |||
36 | obj-$(CONFIG_MMC_VIA_SDMMC) += via-sdmmc.o | 36 | obj-$(CONFIG_MMC_VIA_SDMMC) += via-sdmmc.o |
37 | obj-$(CONFIG_SDH_BFIN) += bfin_sdh.o | 37 | obj-$(CONFIG_SDH_BFIN) += bfin_sdh.o |
38 | obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o | 38 | obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o |
39 | obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o | ||
39 | 40 | ||
40 | obj-$(CONFIG_MMC_SDHCI_OF) += sdhci-of.o | 41 | obj-$(CONFIG_MMC_SDHCI_OF) += sdhci-of.o |
41 | sdhci-of-y := sdhci-of-core.o | 42 | sdhci-of-y := sdhci-of-core.o |
diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c new file mode 100644 index 000000000000..ad4f9870e3ca --- /dev/null +++ b/drivers/mmc/host/jz4740_mmc.c | |||
@@ -0,0 +1,1029 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> | ||
3 | * JZ4740 SD/MMC controller driver | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License as published by the | ||
7 | * Free Software Foundation; either version 2 of the License, or (at your | ||
8 | * option) any later version. | ||
9 | * | ||
10 | * You should have received a copy of the GNU General Public License along | ||
11 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
12 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #include <linux/mmc/host.h> | ||
17 | #include <linux/io.h> | ||
18 | #include <linux/irq.h> | ||
19 | #include <linux/interrupt.h> | ||
20 | #include <linux/module.h> | ||
21 | #include <linux/platform_device.h> | ||
22 | #include <linux/delay.h> | ||
23 | #include <linux/scatterlist.h> | ||
24 | #include <linux/clk.h> | ||
25 | |||
26 | #include <linux/bitops.h> | ||
27 | #include <linux/gpio.h> | ||
28 | #include <asm/mach-jz4740/gpio.h> | ||
29 | #include <asm/cacheflush.h> | ||
30 | #include <linux/dma-mapping.h> | ||
31 | |||
32 | #include <asm/mach-jz4740/jz4740_mmc.h> | ||
33 | |||
34 | #define JZ_REG_MMC_STRPCL 0x00 | ||
35 | #define JZ_REG_MMC_STATUS 0x04 | ||
36 | #define JZ_REG_MMC_CLKRT 0x08 | ||
37 | #define JZ_REG_MMC_CMDAT 0x0C | ||
38 | #define JZ_REG_MMC_RESTO 0x10 | ||
39 | #define JZ_REG_MMC_RDTO 0x14 | ||
40 | #define JZ_REG_MMC_BLKLEN 0x18 | ||
41 | #define JZ_REG_MMC_NOB 0x1C | ||
42 | #define JZ_REG_MMC_SNOB 0x20 | ||
43 | #define JZ_REG_MMC_IMASK 0x24 | ||
44 | #define JZ_REG_MMC_IREG 0x28 | ||
45 | #define JZ_REG_MMC_CMD 0x2C | ||
46 | #define JZ_REG_MMC_ARG 0x30 | ||
47 | #define JZ_REG_MMC_RESP_FIFO 0x34 | ||
48 | #define JZ_REG_MMC_RXFIFO 0x38 | ||
49 | #define JZ_REG_MMC_TXFIFO 0x3C | ||
50 | |||
51 | #define JZ_MMC_STRPCL_EXIT_MULTIPLE BIT(7) | ||
52 | #define JZ_MMC_STRPCL_EXIT_TRANSFER BIT(6) | ||
53 | #define JZ_MMC_STRPCL_START_READWAIT BIT(5) | ||
54 | #define JZ_MMC_STRPCL_STOP_READWAIT BIT(4) | ||
55 | #define JZ_MMC_STRPCL_RESET BIT(3) | ||
56 | #define JZ_MMC_STRPCL_START_OP BIT(2) | ||
57 | #define JZ_MMC_STRPCL_CLOCK_CONTROL (BIT(1) | BIT(0)) | ||
58 | #define JZ_MMC_STRPCL_CLOCK_STOP BIT(0) | ||
59 | #define JZ_MMC_STRPCL_CLOCK_START BIT(1) | ||
60 | |||
61 | |||
62 | #define JZ_MMC_STATUS_IS_RESETTING BIT(15) | ||
63 | #define JZ_MMC_STATUS_SDIO_INT_ACTIVE BIT(14) | ||
64 | #define JZ_MMC_STATUS_PRG_DONE BIT(13) | ||
65 | #define JZ_MMC_STATUS_DATA_TRAN_DONE BIT(12) | ||
66 | #define JZ_MMC_STATUS_END_CMD_RES BIT(11) | ||
67 | #define JZ_MMC_STATUS_DATA_FIFO_AFULL BIT(10) | ||
68 | #define JZ_MMC_STATUS_IS_READWAIT BIT(9) | ||
69 | #define JZ_MMC_STATUS_CLK_EN BIT(8) | ||
70 | #define JZ_MMC_STATUS_DATA_FIFO_FULL BIT(7) | ||
71 | #define JZ_MMC_STATUS_DATA_FIFO_EMPTY BIT(6) | ||
72 | #define JZ_MMC_STATUS_CRC_RES_ERR BIT(5) | ||
73 | #define JZ_MMC_STATUS_CRC_READ_ERROR BIT(4) | ||
74 | #define JZ_MMC_STATUS_TIMEOUT_WRITE BIT(3) | ||
75 | #define JZ_MMC_STATUS_CRC_WRITE_ERROR BIT(2) | ||
76 | #define JZ_MMC_STATUS_TIMEOUT_RES BIT(1) | ||
77 | #define JZ_MMC_STATUS_TIMEOUT_READ BIT(0) | ||
78 | |||
79 | #define JZ_MMC_STATUS_READ_ERROR_MASK (BIT(4) | BIT(0)) | ||
80 | #define JZ_MMC_STATUS_WRITE_ERROR_MASK (BIT(3) | BIT(2)) | ||
81 | |||
82 | |||
83 | #define JZ_MMC_CMDAT_IO_ABORT BIT(11) | ||
84 | #define JZ_MMC_CMDAT_BUS_WIDTH_4BIT BIT(10) | ||
85 | #define JZ_MMC_CMDAT_DMA_EN BIT(8) | ||
86 | #define JZ_MMC_CMDAT_INIT BIT(7) | ||
87 | #define JZ_MMC_CMDAT_BUSY BIT(6) | ||
88 | #define JZ_MMC_CMDAT_STREAM BIT(5) | ||
89 | #define JZ_MMC_CMDAT_WRITE BIT(4) | ||
90 | #define JZ_MMC_CMDAT_DATA_EN BIT(3) | ||
91 | #define JZ_MMC_CMDAT_RESPONSE_FORMAT (BIT(2) | BIT(1) | BIT(0)) | ||
92 | #define JZ_MMC_CMDAT_RSP_R1 1 | ||
93 | #define JZ_MMC_CMDAT_RSP_R2 2 | ||
94 | #define JZ_MMC_CMDAT_RSP_R3 3 | ||
95 | |||
96 | #define JZ_MMC_IRQ_SDIO BIT(7) | ||
97 | #define JZ_MMC_IRQ_TXFIFO_WR_REQ BIT(6) | ||
98 | #define JZ_MMC_IRQ_RXFIFO_RD_REQ BIT(5) | ||
99 | #define JZ_MMC_IRQ_END_CMD_RES BIT(2) | ||
100 | #define JZ_MMC_IRQ_PRG_DONE BIT(1) | ||
101 | #define JZ_MMC_IRQ_DATA_TRAN_DONE BIT(0) | ||
102 | |||
103 | |||
104 | #define JZ_MMC_CLK_RATE 24000000 | ||
105 | |||
106 | enum jz4740_mmc_state { | ||
107 | JZ4740_MMC_STATE_READ_RESPONSE, | ||
108 | JZ4740_MMC_STATE_TRANSFER_DATA, | ||
109 | JZ4740_MMC_STATE_SEND_STOP, | ||
110 | JZ4740_MMC_STATE_DONE, | ||
111 | }; | ||
112 | |||
113 | struct jz4740_mmc_host { | ||
114 | struct mmc_host *mmc; | ||
115 | struct platform_device *pdev; | ||
116 | struct jz4740_mmc_platform_data *pdata; | ||
117 | struct clk *clk; | ||
118 | |||
119 | int irq; | ||
120 | int card_detect_irq; | ||
121 | |||
122 | struct resource *mem; | ||
123 | void __iomem *base; | ||
124 | struct mmc_request *req; | ||
125 | struct mmc_command *cmd; | ||
126 | |||
127 | unsigned long waiting; | ||
128 | |||
129 | uint32_t cmdat; | ||
130 | |||
131 | uint16_t irq_mask; | ||
132 | |||
133 | spinlock_t lock; | ||
134 | |||
135 | struct timer_list timeout_timer; | ||
136 | struct sg_mapping_iter miter; | ||
137 | enum jz4740_mmc_state state; | ||
138 | }; | ||
139 | |||
140 | static void jz4740_mmc_set_irq_enabled(struct jz4740_mmc_host *host, | ||
141 | unsigned int irq, bool enabled) | ||
142 | { | ||
143 | unsigned long flags; | ||
144 | |||
145 | spin_lock_irqsave(&host->lock, flags); | ||
146 | if (enabled) | ||
147 | host->irq_mask &= ~irq; | ||
148 | else | ||
149 | host->irq_mask |= irq; | ||
150 | spin_unlock_irqrestore(&host->lock, flags); | ||
151 | |||
152 | writew(host->irq_mask, host->base + JZ_REG_MMC_IMASK); | ||
153 | } | ||
154 | |||
155 | static void jz4740_mmc_clock_enable(struct jz4740_mmc_host *host, | ||
156 | bool start_transfer) | ||
157 | { | ||
158 | uint16_t val = JZ_MMC_STRPCL_CLOCK_START; | ||
159 | |||
160 | if (start_transfer) | ||
161 | val |= JZ_MMC_STRPCL_START_OP; | ||
162 | |||
163 | writew(val, host->base + JZ_REG_MMC_STRPCL); | ||
164 | } | ||
165 | |||
166 | static void jz4740_mmc_clock_disable(struct jz4740_mmc_host *host) | ||
167 | { | ||
168 | uint32_t status; | ||
169 | unsigned int timeout = 1000; | ||
170 | |||
171 | writew(JZ_MMC_STRPCL_CLOCK_STOP, host->base + JZ_REG_MMC_STRPCL); | ||
172 | do { | ||
173 | status = readl(host->base + JZ_REG_MMC_STATUS); | ||
174 | } while (status & JZ_MMC_STATUS_CLK_EN && --timeout); | ||
175 | } | ||
176 | |||
177 | static void jz4740_mmc_reset(struct jz4740_mmc_host *host) | ||
178 | { | ||
179 | uint32_t status; | ||
180 | unsigned int timeout = 1000; | ||
181 | |||
182 | writew(JZ_MMC_STRPCL_RESET, host->base + JZ_REG_MMC_STRPCL); | ||
183 | udelay(10); | ||
184 | do { | ||
185 | status = readl(host->base + JZ_REG_MMC_STATUS); | ||
186 | } while (status & JZ_MMC_STATUS_IS_RESETTING && --timeout); | ||
187 | } | ||
188 | |||
189 | static void jz4740_mmc_request_done(struct jz4740_mmc_host *host) | ||
190 | { | ||
191 | struct mmc_request *req; | ||
192 | |||
193 | req = host->req; | ||
194 | host->req = NULL; | ||
195 | |||
196 | mmc_request_done(host->mmc, req); | ||
197 | } | ||
198 | |||
199 | static unsigned int jz4740_mmc_poll_irq(struct jz4740_mmc_host *host, | ||
200 | unsigned int irq) | ||
201 | { | ||
202 | unsigned int timeout = 0x800; | ||
203 | uint16_t status; | ||
204 | |||
205 | do { | ||
206 | status = readw(host->base + JZ_REG_MMC_IREG); | ||
207 | } while (!(status & irq) && --timeout); | ||
208 | |||
209 | if (timeout == 0) { | ||
210 | set_bit(0, &host->waiting); | ||
211 | mod_timer(&host->timeout_timer, jiffies + 5*HZ); | ||
212 | jz4740_mmc_set_irq_enabled(host, irq, true); | ||
213 | return true; | ||
214 | } | ||
215 | |||
216 | return false; | ||
217 | } | ||
218 | |||
219 | static void jz4740_mmc_transfer_check_state(struct jz4740_mmc_host *host, | ||
220 | struct mmc_data *data) | ||
221 | { | ||
222 | int status; | ||
223 | |||
224 | status = readl(host->base + JZ_REG_MMC_STATUS); | ||
225 | if (status & JZ_MMC_STATUS_WRITE_ERROR_MASK) { | ||
226 | if (status & (JZ_MMC_STATUS_TIMEOUT_WRITE)) { | ||
227 | host->req->cmd->error = -ETIMEDOUT; | ||
228 | data->error = -ETIMEDOUT; | ||
229 | } else { | ||
230 | host->req->cmd->error = -EIO; | ||
231 | data->error = -EIO; | ||
232 | } | ||
233 | } | ||
234 | } | ||
235 | |||
236 | static bool jz4740_mmc_write_data(struct jz4740_mmc_host *host, | ||
237 | struct mmc_data *data) | ||
238 | { | ||
239 | struct sg_mapping_iter *miter = &host->miter; | ||
240 | void __iomem *fifo_addr = host->base + JZ_REG_MMC_TXFIFO; | ||
241 | uint32_t *buf; | ||
242 | bool timeout; | ||
243 | size_t i, j; | ||
244 | |||
245 | while (sg_miter_next(miter)) { | ||
246 | buf = miter->addr; | ||
247 | i = miter->length / 4; | ||
248 | j = i / 8; | ||
249 | i = i & 0x7; | ||
250 | while (j) { | ||
251 | timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_TXFIFO_WR_REQ); | ||
252 | if (unlikely(timeout)) | ||
253 | goto poll_timeout; | ||
254 | |||
255 | writel(buf[0], fifo_addr); | ||
256 | writel(buf[1], fifo_addr); | ||
257 | writel(buf[2], fifo_addr); | ||
258 | writel(buf[3], fifo_addr); | ||
259 | writel(buf[4], fifo_addr); | ||
260 | writel(buf[5], fifo_addr); | ||
261 | writel(buf[6], fifo_addr); | ||
262 | writel(buf[7], fifo_addr); | ||
263 | buf += 8; | ||
264 | --j; | ||
265 | } | ||
266 | if (unlikely(i)) { | ||
267 | timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_TXFIFO_WR_REQ); | ||
268 | if (unlikely(timeout)) | ||
269 | goto poll_timeout; | ||
270 | |||
271 | while (i) { | ||
272 | writel(*buf, fifo_addr); | ||
273 | ++buf; | ||
274 | --i; | ||
275 | } | ||
276 | } | ||
277 | data->bytes_xfered += miter->length; | ||
278 | } | ||
279 | sg_miter_stop(miter); | ||
280 | |||
281 | return false; | ||
282 | |||
283 | poll_timeout: | ||
284 | miter->consumed = (void *)buf - miter->addr; | ||
285 | data->bytes_xfered += miter->consumed; | ||
286 | sg_miter_stop(miter); | ||
287 | |||
288 | return true; | ||
289 | } | ||
290 | |||
291 | static bool jz4740_mmc_read_data(struct jz4740_mmc_host *host, | ||
292 | struct mmc_data *data) | ||
293 | { | ||
294 | struct sg_mapping_iter *miter = &host->miter; | ||
295 | void __iomem *fifo_addr = host->base + JZ_REG_MMC_RXFIFO; | ||
296 | uint32_t *buf; | ||
297 | uint32_t d; | ||
298 | uint16_t status; | ||
299 | size_t i, j; | ||
300 | unsigned int timeout; | ||
301 | |||
302 | while (sg_miter_next(miter)) { | ||
303 | buf = miter->addr; | ||
304 | i = miter->length; | ||
305 | j = i / 32; | ||
306 | i = i & 0x1f; | ||
307 | while (j) { | ||
308 | timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_RXFIFO_RD_REQ); | ||
309 | if (unlikely(timeout)) | ||
310 | goto poll_timeout; | ||
311 | |||
312 | buf[0] = readl(fifo_addr); | ||
313 | buf[1] = readl(fifo_addr); | ||
314 | buf[2] = readl(fifo_addr); | ||
315 | buf[3] = readl(fifo_addr); | ||
316 | buf[4] = readl(fifo_addr); | ||
317 | buf[5] = readl(fifo_addr); | ||
318 | buf[6] = readl(fifo_addr); | ||
319 | buf[7] = readl(fifo_addr); | ||
320 | |||
321 | buf += 8; | ||
322 | --j; | ||
323 | } | ||
324 | |||
325 | if (unlikely(i)) { | ||
326 | timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_RXFIFO_RD_REQ); | ||
327 | if (unlikely(timeout)) | ||
328 | goto poll_timeout; | ||
329 | |||
330 | while (i >= 4) { | ||
331 | *buf++ = readl(fifo_addr); | ||
332 | i -= 4; | ||
333 | } | ||
334 | if (unlikely(i > 0)) { | ||
335 | d = readl(fifo_addr); | ||
336 | memcpy(buf, &d, i); | ||
337 | } | ||
338 | } | ||
339 | data->bytes_xfered += miter->length; | ||
340 | |||
341 | /* This can go away once MIPS implements | ||
342 | * flush_kernel_dcache_page */ | ||
343 | flush_dcache_page(miter->page); | ||
344 | } | ||
345 | sg_miter_stop(miter); | ||
346 | |||
347 | /* For whatever reason there is sometime one word more in the fifo then | ||
348 | * requested */ | ||
349 | timeout = 1000; | ||
350 | status = readl(host->base + JZ_REG_MMC_STATUS); | ||
351 | while (!(status & JZ_MMC_STATUS_DATA_FIFO_EMPTY) && --timeout) { | ||
352 | d = readl(fifo_addr); | ||
353 | status = readl(host->base + JZ_REG_MMC_STATUS); | ||
354 | } | ||
355 | |||
356 | return false; | ||
357 | |||
358 | poll_timeout: | ||
359 | miter->consumed = (void *)buf - miter->addr; | ||
360 | data->bytes_xfered += miter->consumed; | ||
361 | sg_miter_stop(miter); | ||
362 | |||
363 | return true; | ||
364 | } | ||
365 | |||
366 | static void jz4740_mmc_timeout(unsigned long data) | ||
367 | { | ||
368 | struct jz4740_mmc_host *host = (struct jz4740_mmc_host *)data; | ||
369 | |||
370 | if (!test_and_clear_bit(0, &host->waiting)) | ||
371 | return; | ||
372 | |||
373 | jz4740_mmc_set_irq_enabled(host, JZ_MMC_IRQ_END_CMD_RES, false); | ||
374 | |||
375 | host->req->cmd->error = -ETIMEDOUT; | ||
376 | jz4740_mmc_request_done(host); | ||
377 | } | ||
378 | |||
379 | static void jz4740_mmc_read_response(struct jz4740_mmc_host *host, | ||
380 | struct mmc_command *cmd) | ||
381 | { | ||
382 | int i; | ||
383 | uint16_t tmp; | ||
384 | void __iomem *fifo_addr = host->base + JZ_REG_MMC_RESP_FIFO; | ||
385 | |||
386 | if (cmd->flags & MMC_RSP_136) { | ||
387 | tmp = readw(fifo_addr); | ||
388 | for (i = 0; i < 4; ++i) { | ||
389 | cmd->resp[i] = tmp << 24; | ||
390 | tmp = readw(fifo_addr); | ||
391 | cmd->resp[i] |= tmp << 8; | ||
392 | tmp = readw(fifo_addr); | ||
393 | cmd->resp[i] |= tmp >> 8; | ||
394 | } | ||
395 | } else { | ||
396 | cmd->resp[0] = readw(fifo_addr) << 24; | ||
397 | cmd->resp[0] |= readw(fifo_addr) << 8; | ||
398 | cmd->resp[0] |= readw(fifo_addr) & 0xff; | ||
399 | } | ||
400 | } | ||
401 | |||
402 | static void jz4740_mmc_send_command(struct jz4740_mmc_host *host, | ||
403 | struct mmc_command *cmd) | ||
404 | { | ||
405 | uint32_t cmdat = host->cmdat; | ||
406 | |||
407 | host->cmdat &= ~JZ_MMC_CMDAT_INIT; | ||
408 | jz4740_mmc_clock_disable(host); | ||
409 | |||
410 | host->cmd = cmd; | ||
411 | |||
412 | if (cmd->flags & MMC_RSP_BUSY) | ||
413 | cmdat |= JZ_MMC_CMDAT_BUSY; | ||
414 | |||
415 | switch (mmc_resp_type(cmd)) { | ||
416 | case MMC_RSP_R1B: | ||
417 | case MMC_RSP_R1: | ||
418 | cmdat |= JZ_MMC_CMDAT_RSP_R1; | ||
419 | break; | ||
420 | case MMC_RSP_R2: | ||
421 | cmdat |= JZ_MMC_CMDAT_RSP_R2; | ||
422 | break; | ||
423 | case MMC_RSP_R3: | ||
424 | cmdat |= JZ_MMC_CMDAT_RSP_R3; | ||
425 | break; | ||
426 | default: | ||
427 | break; | ||
428 | } | ||
429 | |||
430 | if (cmd->data) { | ||
431 | cmdat |= JZ_MMC_CMDAT_DATA_EN; | ||
432 | if (cmd->data->flags & MMC_DATA_WRITE) | ||
433 | cmdat |= JZ_MMC_CMDAT_WRITE; | ||
434 | if (cmd->data->flags & MMC_DATA_STREAM) | ||
435 | cmdat |= JZ_MMC_CMDAT_STREAM; | ||
436 | |||
437 | writew(cmd->data->blksz, host->base + JZ_REG_MMC_BLKLEN); | ||
438 | writew(cmd->data->blocks, host->base + JZ_REG_MMC_NOB); | ||
439 | } | ||
440 | |||
441 | writeb(cmd->opcode, host->base + JZ_REG_MMC_CMD); | ||
442 | writel(cmd->arg, host->base + JZ_REG_MMC_ARG); | ||
443 | writel(cmdat, host->base + JZ_REG_MMC_CMDAT); | ||
444 | |||
445 | jz4740_mmc_clock_enable(host, 1); | ||
446 | } | ||
447 | |||
448 | static void jz_mmc_prepare_data_transfer(struct jz4740_mmc_host *host) | ||
449 | { | ||
450 | struct mmc_command *cmd = host->req->cmd; | ||
451 | struct mmc_data *data = cmd->data; | ||
452 | int direction; | ||
453 | |||
454 | if (data->flags & MMC_DATA_READ) | ||
455 | direction = SG_MITER_TO_SG; | ||
456 | else | ||
457 | direction = SG_MITER_FROM_SG; | ||
458 | |||
459 | sg_miter_start(&host->miter, data->sg, data->sg_len, direction); | ||
460 | } | ||
461 | |||
462 | |||
463 | static irqreturn_t jz_mmc_irq_worker(int irq, void *devid) | ||
464 | { | ||
465 | struct jz4740_mmc_host *host = (struct jz4740_mmc_host *)devid; | ||
466 | struct mmc_command *cmd = host->req->cmd; | ||
467 | struct mmc_request *req = host->req; | ||
468 | bool timeout = false; | ||
469 | |||
470 | if (cmd->error) | ||
471 | host->state = JZ4740_MMC_STATE_DONE; | ||
472 | |||
473 | switch (host->state) { | ||
474 | case JZ4740_MMC_STATE_READ_RESPONSE: | ||
475 | if (cmd->flags & MMC_RSP_PRESENT) | ||
476 | jz4740_mmc_read_response(host, cmd); | ||
477 | |||
478 | if (!cmd->data) | ||
479 | break; | ||
480 | |||
481 | jz_mmc_prepare_data_transfer(host); | ||
482 | |||
483 | case JZ4740_MMC_STATE_TRANSFER_DATA: | ||
484 | if (cmd->data->flags & MMC_DATA_READ) | ||
485 | timeout = jz4740_mmc_read_data(host, cmd->data); | ||
486 | else | ||
487 | timeout = jz4740_mmc_write_data(host, cmd->data); | ||
488 | |||
489 | if (unlikely(timeout)) { | ||
490 | host->state = JZ4740_MMC_STATE_TRANSFER_DATA; | ||
491 | break; | ||
492 | } | ||
493 | |||
494 | jz4740_mmc_transfer_check_state(host, cmd->data); | ||
495 | |||
496 | timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_DATA_TRAN_DONE); | ||
497 | if (unlikely(timeout)) { | ||
498 | host->state = JZ4740_MMC_STATE_SEND_STOP; | ||
499 | break; | ||
500 | } | ||
501 | writew(JZ_MMC_IRQ_DATA_TRAN_DONE, host->base + JZ_REG_MMC_IREG); | ||
502 | |||
503 | case JZ4740_MMC_STATE_SEND_STOP: | ||
504 | if (!req->stop) | ||
505 | break; | ||
506 | |||
507 | jz4740_mmc_send_command(host, req->stop); | ||
508 | |||
509 | timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_PRG_DONE); | ||
510 | if (timeout) { | ||
511 | host->state = JZ4740_MMC_STATE_DONE; | ||
512 | break; | ||
513 | } | ||
514 | case JZ4740_MMC_STATE_DONE: | ||
515 | break; | ||
516 | } | ||
517 | |||
518 | if (!timeout) | ||
519 | jz4740_mmc_request_done(host); | ||
520 | |||
521 | return IRQ_HANDLED; | ||
522 | } | ||
523 | |||
524 | static irqreturn_t jz_mmc_irq(int irq, void *devid) | ||
525 | { | ||
526 | struct jz4740_mmc_host *host = devid; | ||
527 | struct mmc_command *cmd = host->cmd; | ||
528 | uint16_t irq_reg, status, tmp; | ||
529 | |||
530 | irq_reg = readw(host->base + JZ_REG_MMC_IREG); | ||
531 | |||
532 | tmp = irq_reg; | ||
533 | irq_reg &= ~host->irq_mask; | ||
534 | |||
535 | tmp &= ~(JZ_MMC_IRQ_TXFIFO_WR_REQ | JZ_MMC_IRQ_RXFIFO_RD_REQ | | ||
536 | JZ_MMC_IRQ_PRG_DONE | JZ_MMC_IRQ_DATA_TRAN_DONE); | ||
537 | |||
538 | if (tmp != irq_reg) | ||
539 | writew(tmp & ~irq_reg, host->base + JZ_REG_MMC_IREG); | ||
540 | |||
541 | if (irq_reg & JZ_MMC_IRQ_SDIO) { | ||
542 | writew(JZ_MMC_IRQ_SDIO, host->base + JZ_REG_MMC_IREG); | ||
543 | mmc_signal_sdio_irq(host->mmc); | ||
544 | irq_reg &= ~JZ_MMC_IRQ_SDIO; | ||
545 | } | ||
546 | |||
547 | if (host->req && cmd && irq_reg) { | ||
548 | if (test_and_clear_bit(0, &host->waiting)) { | ||
549 | del_timer(&host->timeout_timer); | ||
550 | |||
551 | status = readl(host->base + JZ_REG_MMC_STATUS); | ||
552 | |||
553 | if (status & JZ_MMC_STATUS_TIMEOUT_RES) { | ||
554 | cmd->error = -ETIMEDOUT; | ||
555 | } else if (status & JZ_MMC_STATUS_CRC_RES_ERR) { | ||
556 | cmd->error = -EIO; | ||
557 | } else if (status & (JZ_MMC_STATUS_CRC_READ_ERROR | | ||
558 | JZ_MMC_STATUS_CRC_WRITE_ERROR)) { | ||
559 | if (cmd->data) | ||
560 | cmd->data->error = -EIO; | ||
561 | cmd->error = -EIO; | ||
562 | } else if (status & (JZ_MMC_STATUS_CRC_READ_ERROR | | ||
563 | JZ_MMC_STATUS_CRC_WRITE_ERROR)) { | ||
564 | if (cmd->data) | ||
565 | cmd->data->error = -EIO; | ||
566 | cmd->error = -EIO; | ||
567 | } | ||
568 | |||
569 | jz4740_mmc_set_irq_enabled(host, irq_reg, false); | ||
570 | writew(irq_reg, host->base + JZ_REG_MMC_IREG); | ||
571 | |||
572 | return IRQ_WAKE_THREAD; | ||
573 | } | ||
574 | } | ||
575 | |||
576 | return IRQ_HANDLED; | ||
577 | } | ||
578 | |||
579 | static int jz4740_mmc_set_clock_rate(struct jz4740_mmc_host *host, int rate) | ||
580 | { | ||
581 | int div = 0; | ||
582 | int real_rate; | ||
583 | |||
584 | jz4740_mmc_clock_disable(host); | ||
585 | clk_set_rate(host->clk, JZ_MMC_CLK_RATE); | ||
586 | |||
587 | real_rate = clk_get_rate(host->clk); | ||
588 | |||
589 | while (real_rate > rate && div < 7) { | ||
590 | ++div; | ||
591 | real_rate >>= 1; | ||
592 | } | ||
593 | |||
594 | writew(div, host->base + JZ_REG_MMC_CLKRT); | ||
595 | return real_rate; | ||
596 | } | ||
597 | |||
598 | static void jz4740_mmc_request(struct mmc_host *mmc, struct mmc_request *req) | ||
599 | { | ||
600 | struct jz4740_mmc_host *host = mmc_priv(mmc); | ||
601 | |||
602 | host->req = req; | ||
603 | |||
604 | writew(0xffff, host->base + JZ_REG_MMC_IREG); | ||
605 | |||
606 | writew(JZ_MMC_IRQ_END_CMD_RES, host->base + JZ_REG_MMC_IREG); | ||
607 | jz4740_mmc_set_irq_enabled(host, JZ_MMC_IRQ_END_CMD_RES, true); | ||
608 | |||
609 | host->state = JZ4740_MMC_STATE_READ_RESPONSE; | ||
610 | set_bit(0, &host->waiting); | ||
611 | mod_timer(&host->timeout_timer, jiffies + 5*HZ); | ||
612 | jz4740_mmc_send_command(host, req->cmd); | ||
613 | } | ||
614 | |||
615 | static void jz4740_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | ||
616 | { | ||
617 | struct jz4740_mmc_host *host = mmc_priv(mmc); | ||
618 | if (ios->clock) | ||
619 | jz4740_mmc_set_clock_rate(host, ios->clock); | ||
620 | |||
621 | switch (ios->power_mode) { | ||
622 | case MMC_POWER_UP: | ||
623 | jz4740_mmc_reset(host); | ||
624 | if (gpio_is_valid(host->pdata->gpio_power)) | ||
625 | gpio_set_value(host->pdata->gpio_power, | ||
626 | !host->pdata->power_active_low); | ||
627 | host->cmdat |= JZ_MMC_CMDAT_INIT; | ||
628 | clk_enable(host->clk); | ||
629 | break; | ||
630 | case MMC_POWER_ON: | ||
631 | break; | ||
632 | default: | ||
633 | if (gpio_is_valid(host->pdata->gpio_power)) | ||
634 | gpio_set_value(host->pdata->gpio_power, | ||
635 | host->pdata->power_active_low); | ||
636 | clk_disable(host->clk); | ||
637 | break; | ||
638 | } | ||
639 | |||
640 | switch (ios->bus_width) { | ||
641 | case MMC_BUS_WIDTH_1: | ||
642 | host->cmdat &= ~JZ_MMC_CMDAT_BUS_WIDTH_4BIT; | ||
643 | break; | ||
644 | case MMC_BUS_WIDTH_4: | ||
645 | host->cmdat |= JZ_MMC_CMDAT_BUS_WIDTH_4BIT; | ||
646 | break; | ||
647 | default: | ||
648 | break; | ||
649 | } | ||
650 | } | ||
651 | |||
652 | static int jz4740_mmc_get_ro(struct mmc_host *mmc) | ||
653 | { | ||
654 | struct jz4740_mmc_host *host = mmc_priv(mmc); | ||
655 | if (!gpio_is_valid(host->pdata->gpio_read_only)) | ||
656 | return -ENOSYS; | ||
657 | |||
658 | return gpio_get_value(host->pdata->gpio_read_only) ^ | ||
659 | host->pdata->read_only_active_low; | ||
660 | } | ||
661 | |||
662 | static int jz4740_mmc_get_cd(struct mmc_host *mmc) | ||
663 | { | ||
664 | struct jz4740_mmc_host *host = mmc_priv(mmc); | ||
665 | if (!gpio_is_valid(host->pdata->gpio_card_detect)) | ||
666 | return -ENOSYS; | ||
667 | |||
668 | return gpio_get_value(host->pdata->gpio_card_detect) ^ | ||
669 | host->pdata->card_detect_active_low; | ||
670 | } | ||
671 | |||
672 | static irqreturn_t jz4740_mmc_card_detect_irq(int irq, void *devid) | ||
673 | { | ||
674 | struct jz4740_mmc_host *host = devid; | ||
675 | |||
676 | mmc_detect_change(host->mmc, HZ / 2); | ||
677 | |||
678 | return IRQ_HANDLED; | ||
679 | } | ||
680 | |||
681 | static void jz4740_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) | ||
682 | { | ||
683 | struct jz4740_mmc_host *host = mmc_priv(mmc); | ||
684 | jz4740_mmc_set_irq_enabled(host, JZ_MMC_IRQ_SDIO, enable); | ||
685 | } | ||
686 | |||
687 | static const struct mmc_host_ops jz4740_mmc_ops = { | ||
688 | .request = jz4740_mmc_request, | ||
689 | .set_ios = jz4740_mmc_set_ios, | ||
690 | .get_ro = jz4740_mmc_get_ro, | ||
691 | .get_cd = jz4740_mmc_get_cd, | ||
692 | .enable_sdio_irq = jz4740_mmc_enable_sdio_irq, | ||
693 | }; | ||
694 | |||
695 | static const struct jz_gpio_bulk_request jz4740_mmc_pins[] = { | ||
696 | JZ_GPIO_BULK_PIN(MSC_CMD), | ||
697 | JZ_GPIO_BULK_PIN(MSC_CLK), | ||
698 | JZ_GPIO_BULK_PIN(MSC_DATA0), | ||
699 | JZ_GPIO_BULK_PIN(MSC_DATA1), | ||
700 | JZ_GPIO_BULK_PIN(MSC_DATA2), | ||
701 | JZ_GPIO_BULK_PIN(MSC_DATA3), | ||
702 | }; | ||
703 | |||
704 | static int __devinit jz4740_mmc_request_gpio(struct device *dev, int gpio, | ||
705 | const char *name, bool output, int value) | ||
706 | { | ||
707 | int ret; | ||
708 | |||
709 | if (!gpio_is_valid(gpio)) | ||
710 | return 0; | ||
711 | |||
712 | ret = gpio_request(gpio, name); | ||
713 | if (ret) { | ||
714 | dev_err(dev, "Failed to request %s gpio: %d\n", name, ret); | ||
715 | return ret; | ||
716 | } | ||
717 | |||
718 | if (output) | ||
719 | gpio_direction_output(gpio, value); | ||
720 | else | ||
721 | gpio_direction_input(gpio); | ||
722 | |||
723 | return 0; | ||
724 | } | ||
725 | |||
726 | static int __devinit jz4740_mmc_request_gpios(struct platform_device *pdev) | ||
727 | { | ||
728 | int ret; | ||
729 | struct jz4740_mmc_platform_data *pdata = pdev->dev.platform_data; | ||
730 | |||
731 | if (!pdata) | ||
732 | return 0; | ||
733 | |||
734 | ret = jz4740_mmc_request_gpio(&pdev->dev, pdata->gpio_card_detect, | ||
735 | "MMC detect change", false, 0); | ||
736 | if (ret) | ||
737 | goto err; | ||
738 | |||
739 | ret = jz4740_mmc_request_gpio(&pdev->dev, pdata->gpio_read_only, | ||
740 | "MMC read only", false, 0); | ||
741 | if (ret) | ||
742 | goto err_free_gpio_card_detect; | ||
743 | |||
744 | ret = jz4740_mmc_request_gpio(&pdev->dev, pdata->gpio_power, | ||
745 | "MMC read only", true, pdata->power_active_low); | ||
746 | if (ret) | ||
747 | goto err_free_gpio_read_only; | ||
748 | |||
749 | return 0; | ||
750 | |||
751 | err_free_gpio_read_only: | ||
752 | if (gpio_is_valid(pdata->gpio_read_only)) | ||
753 | gpio_free(pdata->gpio_read_only); | ||
754 | err_free_gpio_card_detect: | ||
755 | if (gpio_is_valid(pdata->gpio_card_detect)) | ||
756 | gpio_free(pdata->gpio_card_detect); | ||
757 | err: | ||
758 | return ret; | ||
759 | } | ||
760 | |||
761 | static int __devinit jz4740_mmc_request_cd_irq(struct platform_device *pdev, | ||
762 | struct jz4740_mmc_host *host) | ||
763 | { | ||
764 | struct jz4740_mmc_platform_data *pdata = pdev->dev.platform_data; | ||
765 | |||
766 | if (!gpio_is_valid(pdata->gpio_card_detect)) | ||
767 | return 0; | ||
768 | |||
769 | host->card_detect_irq = gpio_to_irq(pdata->gpio_card_detect); | ||
770 | if (host->card_detect_irq < 0) { | ||
771 | dev_warn(&pdev->dev, "Failed to get card detect irq\n"); | ||
772 | return 0; | ||
773 | } | ||
774 | |||
775 | return request_irq(host->card_detect_irq, jz4740_mmc_card_detect_irq, | ||
776 | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, | ||
777 | "MMC card detect", host); | ||
778 | } | ||
779 | |||
780 | static void jz4740_mmc_free_gpios(struct platform_device *pdev) | ||
781 | { | ||
782 | struct jz4740_mmc_platform_data *pdata = pdev->dev.platform_data; | ||
783 | |||
784 | if (!pdata) | ||
785 | return; | ||
786 | |||
787 | if (gpio_is_valid(pdata->gpio_power)) | ||
788 | gpio_free(pdata->gpio_power); | ||
789 | if (gpio_is_valid(pdata->gpio_read_only)) | ||
790 | gpio_free(pdata->gpio_read_only); | ||
791 | if (gpio_is_valid(pdata->gpio_card_detect)) | ||
792 | gpio_free(pdata->gpio_card_detect); | ||
793 | } | ||
794 | |||
795 | static inline size_t jz4740_mmc_num_pins(struct jz4740_mmc_host *host) | ||
796 | { | ||
797 | size_t num_pins = ARRAY_SIZE(jz4740_mmc_pins); | ||
798 | if (host->pdata && host->pdata->data_1bit) | ||
799 | num_pins -= 3; | ||
800 | |||
801 | return num_pins; | ||
802 | } | ||
803 | |||
804 | static int __devinit jz4740_mmc_probe(struct platform_device* pdev) | ||
805 | { | ||
806 | int ret; | ||
807 | struct mmc_host *mmc; | ||
808 | struct jz4740_mmc_host *host; | ||
809 | struct jz4740_mmc_platform_data *pdata; | ||
810 | |||
811 | pdata = pdev->dev.platform_data; | ||
812 | |||
813 | mmc = mmc_alloc_host(sizeof(struct jz4740_mmc_host), &pdev->dev); | ||
814 | if (!mmc) { | ||
815 | dev_err(&pdev->dev, "Failed to alloc mmc host structure\n"); | ||
816 | return -ENOMEM; | ||
817 | } | ||
818 | |||
819 | host = mmc_priv(mmc); | ||
820 | host->pdata = pdata; | ||
821 | |||
822 | host->irq = platform_get_irq(pdev, 0); | ||
823 | if (host->irq < 0) { | ||
824 | ret = host->irq; | ||
825 | dev_err(&pdev->dev, "Failed to get platform irq: %d\n", ret); | ||
826 | goto err_free_host; | ||
827 | } | ||
828 | |||
829 | host->clk = clk_get(&pdev->dev, "mmc"); | ||
830 | if (!host->clk) { | ||
831 | ret = -ENOENT; | ||
832 | dev_err(&pdev->dev, "Failed to get mmc clock\n"); | ||
833 | goto err_free_host; | ||
834 | } | ||
835 | |||
836 | host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
837 | if (!host->mem) { | ||
838 | ret = -ENOENT; | ||
839 | dev_err(&pdev->dev, "Failed to get base platform memory\n"); | ||
840 | goto err_clk_put; | ||
841 | } | ||
842 | |||
843 | host->mem = request_mem_region(host->mem->start, | ||
844 | resource_size(host->mem), pdev->name); | ||
845 | if (!host->mem) { | ||
846 | ret = -EBUSY; | ||
847 | dev_err(&pdev->dev, "Failed to request base memory region\n"); | ||
848 | goto err_clk_put; | ||
849 | } | ||
850 | |||
851 | host->base = ioremap_nocache(host->mem->start, resource_size(host->mem)); | ||
852 | if (!host->base) { | ||
853 | ret = -EBUSY; | ||
854 | dev_err(&pdev->dev, "Failed to ioremap base memory\n"); | ||
855 | goto err_release_mem_region; | ||
856 | } | ||
857 | |||
858 | ret = jz_gpio_bulk_request(jz4740_mmc_pins, jz4740_mmc_num_pins(host)); | ||
859 | if (ret) { | ||
860 | dev_err(&pdev->dev, "Failed to request mmc pins: %d\n", ret); | ||
861 | goto err_iounmap; | ||
862 | } | ||
863 | |||
864 | ret = jz4740_mmc_request_gpios(pdev); | ||
865 | if (ret) | ||
866 | goto err_gpio_bulk_free; | ||
867 | |||
868 | mmc->ops = &jz4740_mmc_ops; | ||
869 | mmc->f_min = JZ_MMC_CLK_RATE / 128; | ||
870 | mmc->f_max = JZ_MMC_CLK_RATE; | ||
871 | mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; | ||
872 | mmc->caps = (pdata && pdata->data_1bit) ? 0 : MMC_CAP_4_BIT_DATA; | ||
873 | mmc->caps |= MMC_CAP_SDIO_IRQ; | ||
874 | |||
875 | mmc->max_blk_size = (1 << 10) - 1; | ||
876 | mmc->max_blk_count = (1 << 15) - 1; | ||
877 | mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; | ||
878 | |||
879 | mmc->max_phys_segs = 128; | ||
880 | mmc->max_hw_segs = 128; | ||
881 | mmc->max_seg_size = mmc->max_req_size; | ||
882 | |||
883 | host->mmc = mmc; | ||
884 | host->pdev = pdev; | ||
885 | spin_lock_init(&host->lock); | ||
886 | host->irq_mask = 0xffff; | ||
887 | |||
888 | ret = jz4740_mmc_request_cd_irq(pdev, host); | ||
889 | if (ret) { | ||
890 | dev_err(&pdev->dev, "Failed to request card detect irq\n"); | ||
891 | goto err_free_gpios; | ||
892 | } | ||
893 | |||
894 | ret = request_threaded_irq(host->irq, jz_mmc_irq, jz_mmc_irq_worker, 0, | ||
895 | dev_name(&pdev->dev), host); | ||
896 | if (ret) { | ||
897 | dev_err(&pdev->dev, "Failed to request irq: %d\n", ret); | ||
898 | goto err_free_card_detect_irq; | ||
899 | } | ||
900 | |||
901 | jz4740_mmc_reset(host); | ||
902 | jz4740_mmc_clock_disable(host); | ||
903 | setup_timer(&host->timeout_timer, jz4740_mmc_timeout, | ||
904 | (unsigned long)host); | ||
905 | /* It is not important when it times out, it just needs to timeout. */ | ||
906 | set_timer_slack(&host->timeout_timer, HZ); | ||
907 | |||
908 | platform_set_drvdata(pdev, host); | ||
909 | ret = mmc_add_host(mmc); | ||
910 | |||
911 | if (ret) { | ||
912 | dev_err(&pdev->dev, "Failed to add mmc host: %d\n", ret); | ||
913 | goto err_free_irq; | ||
914 | } | ||
915 | dev_info(&pdev->dev, "JZ SD/MMC card driver registered\n"); | ||
916 | |||
917 | return 0; | ||
918 | |||
919 | err_free_irq: | ||
920 | free_irq(host->irq, host); | ||
921 | err_free_card_detect_irq: | ||
922 | if (host->card_detect_irq >= 0) | ||
923 | free_irq(host->card_detect_irq, host); | ||
924 | err_free_gpios: | ||
925 | jz4740_mmc_free_gpios(pdev); | ||
926 | err_gpio_bulk_free: | ||
927 | jz_gpio_bulk_free(jz4740_mmc_pins, jz4740_mmc_num_pins(host)); | ||
928 | err_iounmap: | ||
929 | iounmap(host->base); | ||
930 | err_release_mem_region: | ||
931 | release_mem_region(host->mem->start, resource_size(host->mem)); | ||
932 | err_clk_put: | ||
933 | clk_put(host->clk); | ||
934 | err_free_host: | ||
935 | platform_set_drvdata(pdev, NULL); | ||
936 | mmc_free_host(mmc); | ||
937 | |||
938 | return ret; | ||
939 | } | ||
940 | |||
941 | static int __devexit jz4740_mmc_remove(struct platform_device *pdev) | ||
942 | { | ||
943 | struct jz4740_mmc_host *host = platform_get_drvdata(pdev); | ||
944 | |||
945 | del_timer_sync(&host->timeout_timer); | ||
946 | jz4740_mmc_set_irq_enabled(host, 0xff, false); | ||
947 | jz4740_mmc_reset(host); | ||
948 | |||
949 | mmc_remove_host(host->mmc); | ||
950 | |||
951 | free_irq(host->irq, host); | ||
952 | if (host->card_detect_irq >= 0) | ||
953 | free_irq(host->card_detect_irq, host); | ||
954 | |||
955 | jz4740_mmc_free_gpios(pdev); | ||
956 | jz_gpio_bulk_free(jz4740_mmc_pins, jz4740_mmc_num_pins(host)); | ||
957 | |||
958 | iounmap(host->base); | ||
959 | release_mem_region(host->mem->start, resource_size(host->mem)); | ||
960 | |||
961 | clk_put(host->clk); | ||
962 | |||
963 | platform_set_drvdata(pdev, NULL); | ||
964 | mmc_free_host(host->mmc); | ||
965 | |||
966 | return 0; | ||
967 | } | ||
968 | |||
969 | #ifdef CONFIG_PM | ||
970 | |||
971 | static int jz4740_mmc_suspend(struct device *dev) | ||
972 | { | ||
973 | struct jz4740_mmc_host *host = dev_get_drvdata(dev); | ||
974 | |||
975 | mmc_suspend_host(host->mmc); | ||
976 | |||
977 | jz_gpio_bulk_suspend(jz4740_mmc_pins, jz4740_mmc_num_pins(host)); | ||
978 | |||
979 | return 0; | ||
980 | } | ||
981 | |||
982 | static int jz4740_mmc_resume(struct device *dev) | ||
983 | { | ||
984 | struct jz4740_mmc_host *host = dev_get_drvdata(dev); | ||
985 | |||
986 | jz_gpio_bulk_resume(jz4740_mmc_pins, jz4740_mmc_num_pins(host)); | ||
987 | |||
988 | mmc_resume_host(host->mmc); | ||
989 | |||
990 | return 0; | ||
991 | } | ||
992 | |||
993 | const struct dev_pm_ops jz4740_mmc_pm_ops = { | ||
994 | .suspend = jz4740_mmc_suspend, | ||
995 | .resume = jz4740_mmc_resume, | ||
996 | .poweroff = jz4740_mmc_suspend, | ||
997 | .restore = jz4740_mmc_resume, | ||
998 | }; | ||
999 | |||
1000 | #define JZ4740_MMC_PM_OPS (&jz4740_mmc_pm_ops) | ||
1001 | #else | ||
1002 | #define JZ4740_MMC_PM_OPS NULL | ||
1003 | #endif | ||
1004 | |||
1005 | static struct platform_driver jz4740_mmc_driver = { | ||
1006 | .probe = jz4740_mmc_probe, | ||
1007 | .remove = __devexit_p(jz4740_mmc_remove), | ||
1008 | .driver = { | ||
1009 | .name = "jz4740-mmc", | ||
1010 | .owner = THIS_MODULE, | ||
1011 | .pm = JZ4740_MMC_PM_OPS, | ||
1012 | }, | ||
1013 | }; | ||
1014 | |||
1015 | static int __init jz4740_mmc_init(void) | ||
1016 | { | ||
1017 | return platform_driver_register(&jz4740_mmc_driver); | ||
1018 | } | ||
1019 | module_init(jz4740_mmc_init); | ||
1020 | |||
1021 | static void __exit jz4740_mmc_exit(void) | ||
1022 | { | ||
1023 | platform_driver_unregister(&jz4740_mmc_driver); | ||
1024 | } | ||
1025 | module_exit(jz4740_mmc_exit); | ||
1026 | |||
1027 | MODULE_DESCRIPTION("JZ4740 SD/MMC controller driver"); | ||
1028 | MODULE_LICENSE("GPL"); | ||
1029 | MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); | ||
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index ffc3720929f1..362d177efe1b 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig | |||
@@ -526,4 +526,10 @@ config MTD_NAND_NUC900 | |||
526 | This enables the driver for the NAND Flash on evaluation board based | 526 | This enables the driver for the NAND Flash on evaluation board based |
527 | on w90p910 / NUC9xx. | 527 | on w90p910 / NUC9xx. |
528 | 528 | ||
529 | config MTD_NAND_JZ4740 | ||
530 | tristate "Support for JZ4740 SoC NAND controller" | ||
531 | depends on MACH_JZ4740 | ||
532 | help | ||
533 | Enables support for NAND Flash on JZ4740 SoC based boards. | ||
534 | |||
529 | endif # MTD_NAND | 535 | endif # MTD_NAND |
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index e8ab884ba47b..ac83dcdac5d6 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile | |||
@@ -46,5 +46,6 @@ obj-$(CONFIG_MTD_NAND_NOMADIK) += nomadik_nand.o | |||
46 | obj-$(CONFIG_MTD_NAND_BCM_UMI) += bcm_umi_nand.o nand_bcm_umi.o | 46 | obj-$(CONFIG_MTD_NAND_BCM_UMI) += bcm_umi_nand.o nand_bcm_umi.o |
47 | obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o | 47 | obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o |
48 | obj-$(CONFIG_MTD_NAND_RICOH) += r852.o | 48 | obj-$(CONFIG_MTD_NAND_RICOH) += r852.o |
49 | obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o | ||
49 | 50 | ||
50 | nand-objs := nand_base.o nand_bbt.o | 51 | nand-objs := nand_base.o nand_bbt.o |
diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c new file mode 100644 index 000000000000..67343fc31bd5 --- /dev/null +++ b/drivers/mtd/nand/jz4740_nand.c | |||
@@ -0,0 +1,516 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> | ||
3 | * JZ4740 SoC NAND controller driver | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License as published by the | ||
7 | * Free Software Foundation; either version 2 of the License, or (at your | ||
8 | * option) any later version. | ||
9 | * | ||
10 | * You should have received a copy of the GNU General Public License along | ||
11 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
12 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #include <linux/ioport.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/platform_device.h> | ||
20 | #include <linux/slab.h> | ||
21 | |||
22 | #include <linux/mtd/mtd.h> | ||
23 | #include <linux/mtd/nand.h> | ||
24 | #include <linux/mtd/partitions.h> | ||
25 | |||
26 | #include <linux/gpio.h> | ||
27 | |||
28 | #include <asm/mach-jz4740/jz4740_nand.h> | ||
29 | |||
30 | #define JZ_REG_NAND_CTRL 0x50 | ||
31 | #define JZ_REG_NAND_ECC_CTRL 0x100 | ||
32 | #define JZ_REG_NAND_DATA 0x104 | ||
33 | #define JZ_REG_NAND_PAR0 0x108 | ||
34 | #define JZ_REG_NAND_PAR1 0x10C | ||
35 | #define JZ_REG_NAND_PAR2 0x110 | ||
36 | #define JZ_REG_NAND_IRQ_STAT 0x114 | ||
37 | #define JZ_REG_NAND_IRQ_CTRL 0x118 | ||
38 | #define JZ_REG_NAND_ERR(x) (0x11C + ((x) << 2)) | ||
39 | |||
40 | #define JZ_NAND_ECC_CTRL_PAR_READY BIT(4) | ||
41 | #define JZ_NAND_ECC_CTRL_ENCODING BIT(3) | ||
42 | #define JZ_NAND_ECC_CTRL_RS BIT(2) | ||
43 | #define JZ_NAND_ECC_CTRL_RESET BIT(1) | ||
44 | #define JZ_NAND_ECC_CTRL_ENABLE BIT(0) | ||
45 | |||
46 | #define JZ_NAND_STATUS_ERR_COUNT (BIT(31) | BIT(30) | BIT(29)) | ||
47 | #define JZ_NAND_STATUS_PAD_FINISH BIT(4) | ||
48 | #define JZ_NAND_STATUS_DEC_FINISH BIT(3) | ||
49 | #define JZ_NAND_STATUS_ENC_FINISH BIT(2) | ||
50 | #define JZ_NAND_STATUS_UNCOR_ERROR BIT(1) | ||
51 | #define JZ_NAND_STATUS_ERROR BIT(0) | ||
52 | |||
53 | #define JZ_NAND_CTRL_ENABLE_CHIP(x) BIT((x) << 1) | ||
54 | #define JZ_NAND_CTRL_ASSERT_CHIP(x) BIT(((x) << 1) + 1) | ||
55 | |||
56 | #define JZ_NAND_MEM_ADDR_OFFSET 0x10000 | ||
57 | #define JZ_NAND_MEM_CMD_OFFSET 0x08000 | ||
58 | |||
59 | struct jz_nand { | ||
60 | struct mtd_info mtd; | ||
61 | struct nand_chip chip; | ||
62 | void __iomem *base; | ||
63 | struct resource *mem; | ||
64 | |||
65 | void __iomem *bank_base; | ||
66 | struct resource *bank_mem; | ||
67 | |||
68 | struct jz_nand_platform_data *pdata; | ||
69 | bool is_reading; | ||
70 | }; | ||
71 | |||
72 | static inline struct jz_nand *mtd_to_jz_nand(struct mtd_info *mtd) | ||
73 | { | ||
74 | return container_of(mtd, struct jz_nand, mtd); | ||
75 | } | ||
76 | |||
77 | static void jz_nand_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl) | ||
78 | { | ||
79 | struct jz_nand *nand = mtd_to_jz_nand(mtd); | ||
80 | struct nand_chip *chip = mtd->priv; | ||
81 | uint32_t reg; | ||
82 | |||
83 | if (ctrl & NAND_CTRL_CHANGE) { | ||
84 | BUG_ON((ctrl & NAND_ALE) && (ctrl & NAND_CLE)); | ||
85 | if (ctrl & NAND_ALE) | ||
86 | chip->IO_ADDR_W = nand->bank_base + JZ_NAND_MEM_ADDR_OFFSET; | ||
87 | else if (ctrl & NAND_CLE) | ||
88 | chip->IO_ADDR_W = nand->bank_base + JZ_NAND_MEM_CMD_OFFSET; | ||
89 | else | ||
90 | chip->IO_ADDR_W = nand->bank_base; | ||
91 | |||
92 | reg = readl(nand->base + JZ_REG_NAND_CTRL); | ||
93 | if (ctrl & NAND_NCE) | ||
94 | reg |= JZ_NAND_CTRL_ASSERT_CHIP(0); | ||
95 | else | ||
96 | reg &= ~JZ_NAND_CTRL_ASSERT_CHIP(0); | ||
97 | writel(reg, nand->base + JZ_REG_NAND_CTRL); | ||
98 | } | ||
99 | if (dat != NAND_CMD_NONE) | ||
100 | writeb(dat, chip->IO_ADDR_W); | ||
101 | } | ||
102 | |||
103 | static int jz_nand_dev_ready(struct mtd_info *mtd) | ||
104 | { | ||
105 | struct jz_nand *nand = mtd_to_jz_nand(mtd); | ||
106 | return gpio_get_value_cansleep(nand->pdata->busy_gpio); | ||
107 | } | ||
108 | |||
109 | static void jz_nand_hwctl(struct mtd_info *mtd, int mode) | ||
110 | { | ||
111 | struct jz_nand *nand = mtd_to_jz_nand(mtd); | ||
112 | uint32_t reg; | ||
113 | |||
114 | writel(0, nand->base + JZ_REG_NAND_IRQ_STAT); | ||
115 | reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL); | ||
116 | |||
117 | reg |= JZ_NAND_ECC_CTRL_RESET; | ||
118 | reg |= JZ_NAND_ECC_CTRL_ENABLE; | ||
119 | reg |= JZ_NAND_ECC_CTRL_RS; | ||
120 | |||
121 | switch (mode) { | ||
122 | case NAND_ECC_READ: | ||
123 | reg &= ~JZ_NAND_ECC_CTRL_ENCODING; | ||
124 | nand->is_reading = true; | ||
125 | break; | ||
126 | case NAND_ECC_WRITE: | ||
127 | reg |= JZ_NAND_ECC_CTRL_ENCODING; | ||
128 | nand->is_reading = false; | ||
129 | break; | ||
130 | default: | ||
131 | break; | ||
132 | } | ||
133 | |||
134 | writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL); | ||
135 | } | ||
136 | |||
137 | static int jz_nand_calculate_ecc_rs(struct mtd_info *mtd, const uint8_t *dat, | ||
138 | uint8_t *ecc_code) | ||
139 | { | ||
140 | struct jz_nand *nand = mtd_to_jz_nand(mtd); | ||
141 | uint32_t reg, status; | ||
142 | int i; | ||
143 | unsigned int timeout = 1000; | ||
144 | static uint8_t empty_block_ecc[] = {0xcd, 0x9d, 0x90, 0x58, 0xf4, | ||
145 | 0x8b, 0xff, 0xb7, 0x6f}; | ||
146 | |||
147 | if (nand->is_reading) | ||
148 | return 0; | ||
149 | |||
150 | do { | ||
151 | status = readl(nand->base + JZ_REG_NAND_IRQ_STAT); | ||
152 | } while (!(status & JZ_NAND_STATUS_ENC_FINISH) && --timeout); | ||
153 | |||
154 | if (timeout == 0) | ||
155 | return -1; | ||
156 | |||
157 | reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL); | ||
158 | reg &= ~JZ_NAND_ECC_CTRL_ENABLE; | ||
159 | writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL); | ||
160 | |||
161 | for (i = 0; i < 9; ++i) | ||
162 | ecc_code[i] = readb(nand->base + JZ_REG_NAND_PAR0 + i); | ||
163 | |||
164 | /* If the written data is completly 0xff, we also want to write 0xff as | ||
165 | * ecc, otherwise we will get in trouble when doing subpage writes. */ | ||
166 | if (memcmp(ecc_code, empty_block_ecc, 9) == 0) | ||
167 | memset(ecc_code, 0xff, 9); | ||
168 | |||
169 | return 0; | ||
170 | } | ||
171 | |||
172 | static void jz_nand_correct_data(uint8_t *dat, int index, int mask) | ||
173 | { | ||
174 | int offset = index & 0x7; | ||
175 | uint16_t data; | ||
176 | |||
177 | index += (index >> 3); | ||
178 | |||
179 | data = dat[index]; | ||
180 | data |= dat[index+1] << 8; | ||
181 | |||
182 | mask ^= (data >> offset) & 0x1ff; | ||
183 | data &= ~(0x1ff << offset); | ||
184 | data |= (mask << offset); | ||
185 | |||
186 | dat[index] = data & 0xff; | ||
187 | dat[index+1] = (data >> 8) & 0xff; | ||
188 | } | ||
189 | |||
190 | static int jz_nand_correct_ecc_rs(struct mtd_info *mtd, uint8_t *dat, | ||
191 | uint8_t *read_ecc, uint8_t *calc_ecc) | ||
192 | { | ||
193 | struct jz_nand *nand = mtd_to_jz_nand(mtd); | ||
194 | int i, error_count, index; | ||
195 | uint32_t reg, status, error; | ||
196 | uint32_t t; | ||
197 | unsigned int timeout = 1000; | ||
198 | |||
199 | t = read_ecc[0]; | ||
200 | |||
201 | if (t == 0xff) { | ||
202 | for (i = 1; i < 9; ++i) | ||
203 | t &= read_ecc[i]; | ||
204 | |||
205 | t &= dat[0]; | ||
206 | t &= dat[nand->chip.ecc.size / 2]; | ||
207 | t &= dat[nand->chip.ecc.size - 1]; | ||
208 | |||
209 | if (t == 0xff) { | ||
210 | for (i = 1; i < nand->chip.ecc.size - 1; ++i) | ||
211 | t &= dat[i]; | ||
212 | if (t == 0xff) | ||
213 | return 0; | ||
214 | } | ||
215 | } | ||
216 | |||
217 | for (i = 0; i < 9; ++i) | ||
218 | writeb(read_ecc[i], nand->base + JZ_REG_NAND_PAR0 + i); | ||
219 | |||
220 | reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL); | ||
221 | reg |= JZ_NAND_ECC_CTRL_PAR_READY; | ||
222 | writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL); | ||
223 | |||
224 | do { | ||
225 | status = readl(nand->base + JZ_REG_NAND_IRQ_STAT); | ||
226 | } while (!(status & JZ_NAND_STATUS_DEC_FINISH) && --timeout); | ||
227 | |||
228 | if (timeout == 0) | ||
229 | return -1; | ||
230 | |||
231 | reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL); | ||
232 | reg &= ~JZ_NAND_ECC_CTRL_ENABLE; | ||
233 | writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL); | ||
234 | |||
235 | if (status & JZ_NAND_STATUS_ERROR) { | ||
236 | if (status & JZ_NAND_STATUS_UNCOR_ERROR) | ||
237 | return -1; | ||
238 | |||
239 | error_count = (status & JZ_NAND_STATUS_ERR_COUNT) >> 29; | ||
240 | |||
241 | for (i = 0; i < error_count; ++i) { | ||
242 | error = readl(nand->base + JZ_REG_NAND_ERR(i)); | ||
243 | index = ((error >> 16) & 0x1ff) - 1; | ||
244 | if (index >= 0 && index < 512) | ||
245 | jz_nand_correct_data(dat, index, error & 0x1ff); | ||
246 | } | ||
247 | |||
248 | return error_count; | ||
249 | } | ||
250 | |||
251 | return 0; | ||
252 | } | ||
253 | |||
254 | |||
255 | /* Copy paste of nand_read_page_hwecc_oob_first except for different eccpos | ||
256 | * handling. The ecc area is for 4k chips 72 bytes long and thus does not fit | ||
257 | * into the eccpos array. */ | ||
258 | static int jz_nand_read_page_hwecc_oob_first(struct mtd_info *mtd, | ||
259 | struct nand_chip *chip, uint8_t *buf, int page) | ||
260 | { | ||
261 | int i, eccsize = chip->ecc.size; | ||
262 | int eccbytes = chip->ecc.bytes; | ||
263 | int eccsteps = chip->ecc.steps; | ||
264 | uint8_t *p = buf; | ||
265 | unsigned int ecc_offset = chip->page_shift; | ||
266 | |||
267 | /* Read the OOB area first */ | ||
268 | chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); | ||
269 | chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); | ||
270 | chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); | ||
271 | |||
272 | for (i = ecc_offset; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { | ||
273 | int stat; | ||
274 | |||
275 | chip->ecc.hwctl(mtd, NAND_ECC_READ); | ||
276 | chip->read_buf(mtd, p, eccsize); | ||
277 | |||
278 | stat = chip->ecc.correct(mtd, p, &chip->oob_poi[i], NULL); | ||
279 | if (stat < 0) | ||
280 | mtd->ecc_stats.failed++; | ||
281 | else | ||
282 | mtd->ecc_stats.corrected += stat; | ||
283 | } | ||
284 | return 0; | ||
285 | } | ||
286 | |||
287 | /* Copy-and-paste of nand_write_page_hwecc with different eccpos handling. */ | ||
288 | static void jz_nand_write_page_hwecc(struct mtd_info *mtd, | ||
289 | struct nand_chip *chip, const uint8_t *buf) | ||
290 | { | ||
291 | int i, eccsize = chip->ecc.size; | ||
292 | int eccbytes = chip->ecc.bytes; | ||
293 | int eccsteps = chip->ecc.steps; | ||
294 | const uint8_t *p = buf; | ||
295 | unsigned int ecc_offset = chip->page_shift; | ||
296 | |||
297 | for (i = ecc_offset; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { | ||
298 | chip->ecc.hwctl(mtd, NAND_ECC_WRITE); | ||
299 | chip->write_buf(mtd, p, eccsize); | ||
300 | chip->ecc.calculate(mtd, p, &chip->oob_poi[i]); | ||
301 | } | ||
302 | |||
303 | chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); | ||
304 | } | ||
305 | |||
306 | #ifdef CONFIG_MTD_CMDLINE_PARTS | ||
307 | static const char *part_probes[] = {"cmdline", NULL}; | ||
308 | #endif | ||
309 | |||
310 | static int jz_nand_ioremap_resource(struct platform_device *pdev, | ||
311 | const char *name, struct resource **res, void __iomem **base) | ||
312 | { | ||
313 | int ret; | ||
314 | |||
315 | *res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); | ||
316 | if (!*res) { | ||
317 | dev_err(&pdev->dev, "Failed to get platform %s memory\n", name); | ||
318 | ret = -ENXIO; | ||
319 | goto err; | ||
320 | } | ||
321 | |||
322 | *res = request_mem_region((*res)->start, resource_size(*res), | ||
323 | pdev->name); | ||
324 | if (!*res) { | ||
325 | dev_err(&pdev->dev, "Failed to request %s memory region\n", name); | ||
326 | ret = -EBUSY; | ||
327 | goto err; | ||
328 | } | ||
329 | |||
330 | *base = ioremap((*res)->start, resource_size(*res)); | ||
331 | if (!*base) { | ||
332 | dev_err(&pdev->dev, "Failed to ioremap %s memory region\n", name); | ||
333 | ret = -EBUSY; | ||
334 | goto err_release_mem; | ||
335 | } | ||
336 | |||
337 | return 0; | ||
338 | |||
339 | err_release_mem: | ||
340 | release_mem_region((*res)->start, resource_size(*res)); | ||
341 | err: | ||
342 | *res = NULL; | ||
343 | *base = NULL; | ||
344 | return ret; | ||
345 | } | ||
346 | |||
347 | static int __devinit jz_nand_probe(struct platform_device *pdev) | ||
348 | { | ||
349 | int ret; | ||
350 | struct jz_nand *nand; | ||
351 | struct nand_chip *chip; | ||
352 | struct mtd_info *mtd; | ||
353 | struct jz_nand_platform_data *pdata = pdev->dev.platform_data; | ||
354 | #ifdef CONFIG_MTD_PARTITIONS | ||
355 | struct mtd_partition *partition_info; | ||
356 | int num_partitions = 0; | ||
357 | #endif | ||
358 | |||
359 | nand = kzalloc(sizeof(*nand), GFP_KERNEL); | ||
360 | if (!nand) { | ||
361 | dev_err(&pdev->dev, "Failed to allocate device structure.\n"); | ||
362 | return -ENOMEM; | ||
363 | } | ||
364 | |||
365 | ret = jz_nand_ioremap_resource(pdev, "mmio", &nand->mem, &nand->base); | ||
366 | if (ret) | ||
367 | goto err_free; | ||
368 | ret = jz_nand_ioremap_resource(pdev, "bank", &nand->bank_mem, | ||
369 | &nand->bank_base); | ||
370 | if (ret) | ||
371 | goto err_iounmap_mmio; | ||
372 | |||
373 | if (pdata && gpio_is_valid(pdata->busy_gpio)) { | ||
374 | ret = gpio_request(pdata->busy_gpio, "NAND busy pin"); | ||
375 | if (ret) { | ||
376 | dev_err(&pdev->dev, | ||
377 | "Failed to request busy gpio %d: %d\n", | ||
378 | pdata->busy_gpio, ret); | ||
379 | goto err_iounmap_mem; | ||
380 | } | ||
381 | } | ||
382 | |||
383 | mtd = &nand->mtd; | ||
384 | chip = &nand->chip; | ||
385 | mtd->priv = chip; | ||
386 | mtd->owner = THIS_MODULE; | ||
387 | mtd->name = "jz4740-nand"; | ||
388 | |||
389 | chip->ecc.hwctl = jz_nand_hwctl; | ||
390 | chip->ecc.calculate = jz_nand_calculate_ecc_rs; | ||
391 | chip->ecc.correct = jz_nand_correct_ecc_rs; | ||
392 | chip->ecc.mode = NAND_ECC_HW_OOB_FIRST; | ||
393 | chip->ecc.size = 512; | ||
394 | chip->ecc.bytes = 9; | ||
395 | |||
396 | chip->ecc.read_page = jz_nand_read_page_hwecc_oob_first; | ||
397 | chip->ecc.write_page = jz_nand_write_page_hwecc; | ||
398 | |||
399 | if (pdata) | ||
400 | chip->ecc.layout = pdata->ecc_layout; | ||
401 | |||
402 | chip->chip_delay = 50; | ||
403 | chip->cmd_ctrl = jz_nand_cmd_ctrl; | ||
404 | |||
405 | if (pdata && gpio_is_valid(pdata->busy_gpio)) | ||
406 | chip->dev_ready = jz_nand_dev_ready; | ||
407 | |||
408 | chip->IO_ADDR_R = nand->bank_base; | ||
409 | chip->IO_ADDR_W = nand->bank_base; | ||
410 | |||
411 | nand->pdata = pdata; | ||
412 | platform_set_drvdata(pdev, nand); | ||
413 | |||
414 | writel(JZ_NAND_CTRL_ENABLE_CHIP(0), nand->base + JZ_REG_NAND_CTRL); | ||
415 | |||
416 | ret = nand_scan_ident(mtd, 1, NULL); | ||
417 | if (ret) { | ||
418 | dev_err(&pdev->dev, "Failed to scan nand\n"); | ||
419 | goto err_gpio_free; | ||
420 | } | ||
421 | |||
422 | if (pdata && pdata->ident_callback) { | ||
423 | pdata->ident_callback(pdev, chip, &pdata->partitions, | ||
424 | &pdata->num_partitions); | ||
425 | } | ||
426 | |||
427 | ret = nand_scan_tail(mtd); | ||
428 | if (ret) { | ||
429 | dev_err(&pdev->dev, "Failed to scan nand\n"); | ||
430 | goto err_gpio_free; | ||
431 | } | ||
432 | |||
433 | #ifdef CONFIG_MTD_PARTITIONS | ||
434 | #ifdef CONFIG_MTD_CMDLINE_PARTS | ||
435 | num_partitions = parse_mtd_partitions(mtd, part_probes, | ||
436 | &partition_info, 0); | ||
437 | #endif | ||
438 | if (num_partitions <= 0 && pdata) { | ||
439 | num_partitions = pdata->num_partitions; | ||
440 | partition_info = pdata->partitions; | ||
441 | } | ||
442 | |||
443 | if (num_partitions > 0) | ||
444 | ret = add_mtd_partitions(mtd, partition_info, num_partitions); | ||
445 | else | ||
446 | #endif | ||
447 | ret = add_mtd_device(mtd); | ||
448 | |||
449 | if (ret) { | ||
450 | dev_err(&pdev->dev, "Failed to add mtd device\n"); | ||
451 | goto err_nand_release; | ||
452 | } | ||
453 | |||
454 | dev_info(&pdev->dev, "Successfully registered JZ4740 NAND driver\n"); | ||
455 | |||
456 | return 0; | ||
457 | |||
458 | err_nand_release: | ||
459 | nand_release(&nand->mtd); | ||
460 | err_gpio_free: | ||
461 | platform_set_drvdata(pdev, NULL); | ||
462 | gpio_free(pdata->busy_gpio); | ||
463 | err_iounmap_mem: | ||
464 | iounmap(nand->bank_base); | ||
465 | err_iounmap_mmio: | ||
466 | iounmap(nand->base); | ||
467 | err_free: | ||
468 | kfree(nand); | ||
469 | return ret; | ||
470 | } | ||
471 | |||
472 | static int __devexit jz_nand_remove(struct platform_device *pdev) | ||
473 | { | ||
474 | struct jz_nand *nand = platform_get_drvdata(pdev); | ||
475 | |||
476 | nand_release(&nand->mtd); | ||
477 | |||
478 | /* Deassert and disable all chips */ | ||
479 | writel(0, nand->base + JZ_REG_NAND_CTRL); | ||
480 | |||
481 | iounmap(nand->bank_base); | ||
482 | release_mem_region(nand->bank_mem->start, resource_size(nand->bank_mem)); | ||
483 | iounmap(nand->base); | ||
484 | release_mem_region(nand->mem->start, resource_size(nand->mem)); | ||
485 | |||
486 | platform_set_drvdata(pdev, NULL); | ||
487 | kfree(nand); | ||
488 | |||
489 | return 0; | ||
490 | } | ||
491 | |||
492 | struct platform_driver jz_nand_driver = { | ||
493 | .probe = jz_nand_probe, | ||
494 | .remove = __devexit_p(jz_nand_remove), | ||
495 | .driver = { | ||
496 | .name = "jz4740-nand", | ||
497 | .owner = THIS_MODULE, | ||
498 | }, | ||
499 | }; | ||
500 | |||
501 | static int __init jz_nand_init(void) | ||
502 | { | ||
503 | return platform_driver_register(&jz_nand_driver); | ||
504 | } | ||
505 | module_init(jz_nand_init); | ||
506 | |||
507 | static void __exit jz_nand_exit(void) | ||
508 | { | ||
509 | platform_driver_unregister(&jz_nand_driver); | ||
510 | } | ||
511 | module_exit(jz_nand_exit); | ||
512 | |||
513 | MODULE_LICENSE("GPL"); | ||
514 | MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); | ||
515 | MODULE_DESCRIPTION("NAND controller driver for JZ4740 SoC"); | ||
516 | MODULE_ALIAS("platform:jz4740-nand"); | ||
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index ebe68395ecf8..23c13180ff14 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig | |||
@@ -484,7 +484,7 @@ config XTENSA_XT2000_SONIC | |||
484 | 484 | ||
485 | config MIPS_AU1X00_ENET | 485 | config MIPS_AU1X00_ENET |
486 | tristate "MIPS AU1000 Ethernet support" | 486 | tristate "MIPS AU1000 Ethernet support" |
487 | depends on SOC_AU1X00 | 487 | depends on MIPS_ALCHEMY |
488 | select PHYLIB | 488 | select PHYLIB |
489 | select CRC32 | 489 | select CRC32 |
490 | help | 490 | help |
diff --git a/drivers/net/au1000_eth.c b/drivers/net/au1000_eth.c index 386d4feec652..15ae6df2ff00 100644 --- a/drivers/net/au1000_eth.c +++ b/drivers/net/au1000_eth.c | |||
@@ -104,14 +104,6 @@ MODULE_VERSION(DRV_VERSION); | |||
104 | * complete immediately. | 104 | * complete immediately. |
105 | */ | 105 | */ |
106 | 106 | ||
107 | /* These addresses are only used if yamon doesn't tell us what | ||
108 | * the mac address is, and the mac address is not passed on the | ||
109 | * command line. | ||
110 | */ | ||
111 | static unsigned char au1000_mac_addr[6] __devinitdata = { | ||
112 | 0x00, 0x50, 0xc2, 0x0c, 0x30, 0x00 | ||
113 | }; | ||
114 | |||
115 | struct au1000_private *au_macs[NUM_ETH_INTERFACES]; | 107 | struct au1000_private *au_macs[NUM_ETH_INTERFACES]; |
116 | 108 | ||
117 | /* | 109 | /* |
@@ -1002,7 +994,6 @@ static int __devinit au1000_probe(struct platform_device *pdev) | |||
1002 | db_dest_t *pDB, *pDBfree; | 994 | db_dest_t *pDB, *pDBfree; |
1003 | int irq, i, err = 0; | 995 | int irq, i, err = 0; |
1004 | struct resource *base, *macen; | 996 | struct resource *base, *macen; |
1005 | char ethaddr[6]; | ||
1006 | 997 | ||
1007 | base = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 998 | base = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
1008 | if (!base) { | 999 | if (!base) { |
@@ -1079,24 +1070,13 @@ static int __devinit au1000_probe(struct platform_device *pdev) | |||
1079 | } | 1070 | } |
1080 | aup->mac_id = pdev->id; | 1071 | aup->mac_id = pdev->id; |
1081 | 1072 | ||
1082 | if (pdev->id == 0) { | 1073 | if (pdev->id == 0) |
1083 | if (prom_get_ethernet_addr(ethaddr) == 0) | ||
1084 | memcpy(au1000_mac_addr, ethaddr, sizeof(au1000_mac_addr)); | ||
1085 | else { | ||
1086 | netdev_info(dev, "No MAC address found\n"); | ||
1087 | /* Use the hard coded MAC addresses */ | ||
1088 | } | ||
1089 | |||
1090 | au1000_setup_hw_rings(aup, MAC0_RX_DMA_ADDR, MAC0_TX_DMA_ADDR); | 1074 | au1000_setup_hw_rings(aup, MAC0_RX_DMA_ADDR, MAC0_TX_DMA_ADDR); |
1091 | } else if (pdev->id == 1) | 1075 | else if (pdev->id == 1) |
1092 | au1000_setup_hw_rings(aup, MAC1_RX_DMA_ADDR, MAC1_TX_DMA_ADDR); | 1076 | au1000_setup_hw_rings(aup, MAC1_RX_DMA_ADDR, MAC1_TX_DMA_ADDR); |
1093 | 1077 | ||
1094 | /* | 1078 | /* set a random MAC now in case platform_data doesn't provide one */ |
1095 | * Assign to the Ethernet ports two consecutive MAC addresses | 1079 | random_ether_addr(dev->dev_addr); |
1096 | * to match those that are printed on their stickers | ||
1097 | */ | ||
1098 | memcpy(dev->dev_addr, au1000_mac_addr, sizeof(au1000_mac_addr)); | ||
1099 | dev->dev_addr[5] += pdev->id; | ||
1100 | 1080 | ||
1101 | *aup->enable = 0; | 1081 | *aup->enable = 0; |
1102 | aup->mac_enabled = 0; | 1082 | aup->mac_enabled = 0; |
@@ -1106,6 +1086,9 @@ static int __devinit au1000_probe(struct platform_device *pdev) | |||
1106 | dev_info(&pdev->dev, "no platform_data passed, PHY search on MAC0\n"); | 1086 | dev_info(&pdev->dev, "no platform_data passed, PHY search on MAC0\n"); |
1107 | aup->phy1_search_mac0 = 1; | 1087 | aup->phy1_search_mac0 = 1; |
1108 | } else { | 1088 | } else { |
1089 | if (is_valid_ether_addr(pd->mac)) | ||
1090 | memcpy(dev->dev_addr, pd->mac, 6); | ||
1091 | |||
1109 | aup->phy_static_config = pd->phy_static_config; | 1092 | aup->phy_static_config = pd->phy_static_config; |
1110 | aup->phy_search_highest_addr = pd->phy_search_highest_addr; | 1093 | aup->phy_search_highest_addr = pd->phy_search_highest_addr; |
1111 | aup->phy1_search_mac0 = pd->phy1_search_mac0; | 1094 | aup->phy1_search_mac0 = pd->phy1_search_mac0; |
diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index d0f5ad306078..c988514eb551 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig | |||
@@ -157,11 +157,11 @@ config PCMCIA_M8XX | |||
157 | 157 | ||
158 | config PCMCIA_AU1X00 | 158 | config PCMCIA_AU1X00 |
159 | tristate "Au1x00 pcmcia support" | 159 | tristate "Au1x00 pcmcia support" |
160 | depends on SOC_AU1X00 && PCMCIA | 160 | depends on MIPS_ALCHEMY && PCMCIA |
161 | 161 | ||
162 | config PCMCIA_ALCHEMY_DEVBOARD | 162 | config PCMCIA_ALCHEMY_DEVBOARD |
163 | tristate "Alchemy Db/Pb1xxx PCMCIA socket services" | 163 | tristate "Alchemy Db/Pb1xxx PCMCIA socket services" |
164 | depends on SOC_AU1X00 && PCMCIA | 164 | depends on MIPS_ALCHEMY && PCMCIA |
165 | select 64BIT_PHYS_ADDR | 165 | select 64BIT_PHYS_ADDR |
166 | help | 166 | help |
167 | Enable this driver of you want PCMCIA support on your Alchemy | 167 | Enable this driver of you want PCMCIA support on your Alchemy |
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 8e9ba177d817..1e5506be39b4 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig | |||
@@ -142,4 +142,15 @@ config CHARGER_PCF50633 | |||
142 | help | 142 | help |
143 | Say Y to include support for NXP PCF50633 Main Battery Charger. | 143 | Say Y to include support for NXP PCF50633 Main Battery Charger. |
144 | 144 | ||
145 | config BATTERY_JZ4740 | ||
146 | tristate "Ingenic JZ4740 battery" | ||
147 | depends on MACH_JZ4740 | ||
148 | depends on MFD_JZ4740_ADC | ||
149 | help | ||
150 | Say Y to enable support for the battery on Ingenic JZ4740 based | ||
151 | boards. | ||
152 | |||
153 | This driver can be build as a module. If so, the module will be | ||
154 | called jz4740-battery. | ||
155 | |||
145 | endif # POWER_SUPPLY | 156 | endif # POWER_SUPPLY |
diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 00050809a6c7..cf95009d9bcd 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile | |||
@@ -34,3 +34,4 @@ obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o | |||
34 | obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o | 34 | obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o |
35 | obj-$(CONFIG_BATTERY_Z2) += z2_battery.o | 35 | obj-$(CONFIG_BATTERY_Z2) += z2_battery.o |
36 | obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o | 36 | obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o |
37 | obj-$(CONFIG_BATTERY_JZ4740) += jz4740-battery.o | ||
diff --git a/drivers/power/jz4740-battery.c b/drivers/power/jz4740-battery.c new file mode 100644 index 000000000000..20c4b952e9bd --- /dev/null +++ b/drivers/power/jz4740-battery.c | |||
@@ -0,0 +1,445 @@ | |||
1 | /* | ||
2 | * Battery measurement code for Ingenic JZ SOC. | ||
3 | * | ||
4 | * Copyright (C) 2009 Jiejing Zhang <kzjeef@gmail.com> | ||
5 | * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> | ||
6 | * | ||
7 | * based on tosa_battery.c | ||
8 | * | ||
9 | * Copyright (C) 2008 Marek Vasut <marek.vasut@gmail.com> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License version 2 as | ||
13 | * published by the Free Software Foundation. | ||
14 | * | ||
15 | */ | ||
16 | |||
17 | #include <linux/interrupt.h> | ||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/module.h> | ||
20 | #include <linux/platform_device.h> | ||
21 | #include <linux/slab.h> | ||
22 | |||
23 | #include <linux/delay.h> | ||
24 | #include <linux/gpio.h> | ||
25 | #include <linux/mfd/core.h> | ||
26 | #include <linux/power_supply.h> | ||
27 | |||
28 | #include <linux/power/jz4740-battery.h> | ||
29 | #include <linux/jz4740-adc.h> | ||
30 | |||
31 | struct jz_battery { | ||
32 | struct jz_battery_platform_data *pdata; | ||
33 | struct platform_device *pdev; | ||
34 | |||
35 | struct resource *mem; | ||
36 | void __iomem *base; | ||
37 | |||
38 | int irq; | ||
39 | int charge_irq; | ||
40 | |||
41 | struct mfd_cell *cell; | ||
42 | |||
43 | int status; | ||
44 | long voltage; | ||
45 | |||
46 | struct completion read_completion; | ||
47 | |||
48 | struct power_supply battery; | ||
49 | struct delayed_work work; | ||
50 | }; | ||
51 | |||
52 | static inline struct jz_battery *psy_to_jz_battery(struct power_supply *psy) | ||
53 | { | ||
54 | return container_of(psy, struct jz_battery, battery); | ||
55 | } | ||
56 | |||
57 | static irqreturn_t jz_battery_irq_handler(int irq, void *devid) | ||
58 | { | ||
59 | struct jz_battery *battery = devid; | ||
60 | |||
61 | complete(&battery->read_completion); | ||
62 | return IRQ_HANDLED; | ||
63 | } | ||
64 | |||
65 | static long jz_battery_read_voltage(struct jz_battery *battery) | ||
66 | { | ||
67 | unsigned long t; | ||
68 | unsigned long val; | ||
69 | long voltage; | ||
70 | |||
71 | INIT_COMPLETION(battery->read_completion); | ||
72 | |||
73 | enable_irq(battery->irq); | ||
74 | battery->cell->enable(battery->pdev); | ||
75 | |||
76 | t = wait_for_completion_interruptible_timeout(&battery->read_completion, | ||
77 | HZ); | ||
78 | |||
79 | if (t > 0) { | ||
80 | val = readw(battery->base) & 0xfff; | ||
81 | |||
82 | if (battery->pdata->info.voltage_max_design <= 2500000) | ||
83 | val = (val * 78125UL) >> 7UL; | ||
84 | else | ||
85 | val = ((val * 924375UL) >> 9UL) + 33000; | ||
86 | voltage = (long)val; | ||
87 | } else { | ||
88 | voltage = t ? t : -ETIMEDOUT; | ||
89 | } | ||
90 | |||
91 | battery->cell->disable(battery->pdev); | ||
92 | disable_irq(battery->irq); | ||
93 | |||
94 | return voltage; | ||
95 | } | ||
96 | |||
97 | static int jz_battery_get_capacity(struct power_supply *psy) | ||
98 | { | ||
99 | struct jz_battery *jz_battery = psy_to_jz_battery(psy); | ||
100 | struct power_supply_info *info = &jz_battery->pdata->info; | ||
101 | long voltage; | ||
102 | int ret; | ||
103 | int voltage_span; | ||
104 | |||
105 | voltage = jz_battery_read_voltage(jz_battery); | ||
106 | |||
107 | if (voltage < 0) | ||
108 | return voltage; | ||
109 | |||
110 | voltage_span = info->voltage_max_design - info->voltage_min_design; | ||
111 | ret = ((voltage - info->voltage_min_design) * 100) / voltage_span; | ||
112 | |||
113 | if (ret > 100) | ||
114 | ret = 100; | ||
115 | else if (ret < 0) | ||
116 | ret = 0; | ||
117 | |||
118 | return ret; | ||
119 | } | ||
120 | |||
121 | static int jz_battery_get_property(struct power_supply *psy, | ||
122 | enum power_supply_property psp, union power_supply_propval *val) | ||
123 | { | ||
124 | struct jz_battery *jz_battery = psy_to_jz_battery(psy); | ||
125 | struct power_supply_info *info = &jz_battery->pdata->info; | ||
126 | long voltage; | ||
127 | |||
128 | switch (psp) { | ||
129 | case POWER_SUPPLY_PROP_STATUS: | ||
130 | val->intval = jz_battery->status; | ||
131 | break; | ||
132 | case POWER_SUPPLY_PROP_TECHNOLOGY: | ||
133 | val->intval = jz_battery->pdata->info.technology; | ||
134 | break; | ||
135 | case POWER_SUPPLY_PROP_HEALTH: | ||
136 | voltage = jz_battery_read_voltage(jz_battery); | ||
137 | if (voltage < info->voltage_min_design) | ||
138 | val->intval = POWER_SUPPLY_HEALTH_DEAD; | ||
139 | else | ||
140 | val->intval = POWER_SUPPLY_HEALTH_GOOD; | ||
141 | break; | ||
142 | case POWER_SUPPLY_PROP_CAPACITY: | ||
143 | val->intval = jz_battery_get_capacity(psy); | ||
144 | break; | ||
145 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: | ||
146 | val->intval = jz_battery_read_voltage(jz_battery); | ||
147 | if (val->intval < 0) | ||
148 | return val->intval; | ||
149 | break; | ||
150 | case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: | ||
151 | val->intval = info->voltage_max_design; | ||
152 | break; | ||
153 | case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: | ||
154 | val->intval = info->voltage_min_design; | ||
155 | break; | ||
156 | case POWER_SUPPLY_PROP_PRESENT: | ||
157 | val->intval = 1; | ||
158 | break; | ||
159 | default: | ||
160 | return -EINVAL; | ||
161 | } | ||
162 | return 0; | ||
163 | } | ||
164 | |||
165 | static void jz_battery_external_power_changed(struct power_supply *psy) | ||
166 | { | ||
167 | struct jz_battery *jz_battery = psy_to_jz_battery(psy); | ||
168 | |||
169 | cancel_delayed_work(&jz_battery->work); | ||
170 | schedule_delayed_work(&jz_battery->work, 0); | ||
171 | } | ||
172 | |||
173 | static irqreturn_t jz_battery_charge_irq(int irq, void *data) | ||
174 | { | ||
175 | struct jz_battery *jz_battery = data; | ||
176 | |||
177 | cancel_delayed_work(&jz_battery->work); | ||
178 | schedule_delayed_work(&jz_battery->work, 0); | ||
179 | |||
180 | return IRQ_HANDLED; | ||
181 | } | ||
182 | |||
183 | static void jz_battery_update(struct jz_battery *jz_battery) | ||
184 | { | ||
185 | int status; | ||
186 | long voltage; | ||
187 | bool has_changed = false; | ||
188 | int is_charging; | ||
189 | |||
190 | if (gpio_is_valid(jz_battery->pdata->gpio_charge)) { | ||
191 | is_charging = gpio_get_value(jz_battery->pdata->gpio_charge); | ||
192 | is_charging ^= jz_battery->pdata->gpio_charge_active_low; | ||
193 | if (is_charging) | ||
194 | status = POWER_SUPPLY_STATUS_CHARGING; | ||
195 | else | ||
196 | status = POWER_SUPPLY_STATUS_NOT_CHARGING; | ||
197 | |||
198 | if (status != jz_battery->status) { | ||
199 | jz_battery->status = status; | ||
200 | has_changed = true; | ||
201 | } | ||
202 | } | ||
203 | |||
204 | voltage = jz_battery_read_voltage(jz_battery); | ||
205 | if (abs(voltage - jz_battery->voltage) < 50000) { | ||
206 | jz_battery->voltage = voltage; | ||
207 | has_changed = true; | ||
208 | } | ||
209 | |||
210 | if (has_changed) | ||
211 | power_supply_changed(&jz_battery->battery); | ||
212 | } | ||
213 | |||
214 | static enum power_supply_property jz_battery_properties[] = { | ||
215 | POWER_SUPPLY_PROP_STATUS, | ||
216 | POWER_SUPPLY_PROP_TECHNOLOGY, | ||
217 | POWER_SUPPLY_PROP_HEALTH, | ||
218 | POWER_SUPPLY_PROP_CAPACITY, | ||
219 | POWER_SUPPLY_PROP_VOLTAGE_NOW, | ||
220 | POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, | ||
221 | POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, | ||
222 | POWER_SUPPLY_PROP_PRESENT, | ||
223 | }; | ||
224 | |||
225 | static void jz_battery_work(struct work_struct *work) | ||
226 | { | ||
227 | /* Too small interval will increase system workload */ | ||
228 | const int interval = HZ * 30; | ||
229 | struct jz_battery *jz_battery = container_of(work, struct jz_battery, | ||
230 | work.work); | ||
231 | |||
232 | jz_battery_update(jz_battery); | ||
233 | schedule_delayed_work(&jz_battery->work, interval); | ||
234 | } | ||
235 | |||
236 | static int __devinit jz_battery_probe(struct platform_device *pdev) | ||
237 | { | ||
238 | int ret = 0; | ||
239 | struct jz_battery_platform_data *pdata = pdev->dev.parent->platform_data; | ||
240 | struct jz_battery *jz_battery; | ||
241 | struct power_supply *battery; | ||
242 | |||
243 | jz_battery = kzalloc(sizeof(*jz_battery), GFP_KERNEL); | ||
244 | if (!jz_battery) { | ||
245 | dev_err(&pdev->dev, "Failed to allocate driver structure\n"); | ||
246 | return -ENOMEM; | ||
247 | } | ||
248 | |||
249 | jz_battery->cell = pdev->dev.platform_data; | ||
250 | |||
251 | jz_battery->irq = platform_get_irq(pdev, 0); | ||
252 | if (jz_battery->irq < 0) { | ||
253 | ret = jz_battery->irq; | ||
254 | dev_err(&pdev->dev, "Failed to get platform irq: %d\n", ret); | ||
255 | goto err_free; | ||
256 | } | ||
257 | |||
258 | jz_battery->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
259 | if (!jz_battery->mem) { | ||
260 | ret = -ENOENT; | ||
261 | dev_err(&pdev->dev, "Failed to get platform mmio resource\n"); | ||
262 | goto err_free; | ||
263 | } | ||
264 | |||
265 | jz_battery->mem = request_mem_region(jz_battery->mem->start, | ||
266 | resource_size(jz_battery->mem), pdev->name); | ||
267 | if (!jz_battery->mem) { | ||
268 | ret = -EBUSY; | ||
269 | dev_err(&pdev->dev, "Failed to request mmio memory region\n"); | ||
270 | goto err_free; | ||
271 | } | ||
272 | |||
273 | jz_battery->base = ioremap_nocache(jz_battery->mem->start, | ||
274 | resource_size(jz_battery->mem)); | ||
275 | if (!jz_battery->base) { | ||
276 | ret = -EBUSY; | ||
277 | dev_err(&pdev->dev, "Failed to ioremap mmio memory\n"); | ||
278 | goto err_release_mem_region; | ||
279 | } | ||
280 | |||
281 | battery = &jz_battery->battery; | ||
282 | battery->name = pdata->info.name; | ||
283 | battery->type = POWER_SUPPLY_TYPE_BATTERY; | ||
284 | battery->properties = jz_battery_properties; | ||
285 | battery->num_properties = ARRAY_SIZE(jz_battery_properties); | ||
286 | battery->get_property = jz_battery_get_property; | ||
287 | battery->external_power_changed = jz_battery_external_power_changed; | ||
288 | battery->use_for_apm = 1; | ||
289 | |||
290 | jz_battery->pdata = pdata; | ||
291 | jz_battery->pdev = pdev; | ||
292 | |||
293 | init_completion(&jz_battery->read_completion); | ||
294 | |||
295 | INIT_DELAYED_WORK(&jz_battery->work, jz_battery_work); | ||
296 | |||
297 | ret = request_irq(jz_battery->irq, jz_battery_irq_handler, 0, pdev->name, | ||
298 | jz_battery); | ||
299 | if (ret) { | ||
300 | dev_err(&pdev->dev, "Failed to request irq %d\n", ret); | ||
301 | goto err_iounmap; | ||
302 | } | ||
303 | disable_irq(jz_battery->irq); | ||
304 | |||
305 | if (gpio_is_valid(pdata->gpio_charge)) { | ||
306 | ret = gpio_request(pdata->gpio_charge, dev_name(&pdev->dev)); | ||
307 | if (ret) { | ||
308 | dev_err(&pdev->dev, "charger state gpio request failed.\n"); | ||
309 | goto err_free_irq; | ||
310 | } | ||
311 | ret = gpio_direction_input(pdata->gpio_charge); | ||
312 | if (ret) { | ||
313 | dev_err(&pdev->dev, "charger state gpio set direction failed.\n"); | ||
314 | goto err_free_gpio; | ||
315 | } | ||
316 | |||
317 | jz_battery->charge_irq = gpio_to_irq(pdata->gpio_charge); | ||
318 | |||
319 | if (jz_battery->charge_irq >= 0) { | ||
320 | ret = request_irq(jz_battery->charge_irq, | ||
321 | jz_battery_charge_irq, | ||
322 | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, | ||
323 | dev_name(&pdev->dev), jz_battery); | ||
324 | if (ret) { | ||
325 | dev_err(&pdev->dev, "Failed to request charge irq: %d\n", ret); | ||
326 | goto err_free_gpio; | ||
327 | } | ||
328 | } | ||
329 | } else { | ||
330 | jz_battery->charge_irq = -1; | ||
331 | } | ||
332 | |||
333 | if (jz_battery->pdata->info.voltage_max_design <= 2500000) | ||
334 | jz4740_adc_set_config(pdev->dev.parent, JZ_ADC_CONFIG_BAT_MB, | ||
335 | JZ_ADC_CONFIG_BAT_MB); | ||
336 | else | ||
337 | jz4740_adc_set_config(pdev->dev.parent, JZ_ADC_CONFIG_BAT_MB, 0); | ||
338 | |||
339 | ret = power_supply_register(&pdev->dev, &jz_battery->battery); | ||
340 | if (ret) { | ||
341 | dev_err(&pdev->dev, "power supply battery register failed.\n"); | ||
342 | goto err_free_charge_irq; | ||
343 | } | ||
344 | |||
345 | platform_set_drvdata(pdev, jz_battery); | ||
346 | schedule_delayed_work(&jz_battery->work, 0); | ||
347 | |||
348 | return 0; | ||
349 | |||
350 | err_free_charge_irq: | ||
351 | if (jz_battery->charge_irq >= 0) | ||
352 | free_irq(jz_battery->charge_irq, jz_battery); | ||
353 | err_free_gpio: | ||
354 | if (gpio_is_valid(pdata->gpio_charge)) | ||
355 | gpio_free(jz_battery->pdata->gpio_charge); | ||
356 | err_free_irq: | ||
357 | free_irq(jz_battery->irq, jz_battery); | ||
358 | err_iounmap: | ||
359 | platform_set_drvdata(pdev, NULL); | ||
360 | iounmap(jz_battery->base); | ||
361 | err_release_mem_region: | ||
362 | release_mem_region(jz_battery->mem->start, resource_size(jz_battery->mem)); | ||
363 | err_free: | ||
364 | kfree(jz_battery); | ||
365 | return ret; | ||
366 | } | ||
367 | |||
368 | static int __devexit jz_battery_remove(struct platform_device *pdev) | ||
369 | { | ||
370 | struct jz_battery *jz_battery = platform_get_drvdata(pdev); | ||
371 | |||
372 | cancel_delayed_work_sync(&jz_battery->work); | ||
373 | |||
374 | if (gpio_is_valid(jz_battery->pdata->gpio_charge)) { | ||
375 | if (jz_battery->charge_irq >= 0) | ||
376 | free_irq(jz_battery->charge_irq, jz_battery); | ||
377 | gpio_free(jz_battery->pdata->gpio_charge); | ||
378 | } | ||
379 | |||
380 | power_supply_unregister(&jz_battery->battery); | ||
381 | |||
382 | free_irq(jz_battery->irq, jz_battery); | ||
383 | |||
384 | iounmap(jz_battery->base); | ||
385 | release_mem_region(jz_battery->mem->start, resource_size(jz_battery->mem)); | ||
386 | |||
387 | return 0; | ||
388 | } | ||
389 | |||
390 | #ifdef CONFIG_PM | ||
391 | static int jz_battery_suspend(struct device *dev) | ||
392 | { | ||
393 | struct jz_battery *jz_battery = dev_get_drvdata(dev); | ||
394 | |||
395 | cancel_delayed_work_sync(&jz_battery->work); | ||
396 | jz_battery->status = POWER_SUPPLY_STATUS_UNKNOWN; | ||
397 | |||
398 | return 0; | ||
399 | } | ||
400 | |||
401 | static int jz_battery_resume(struct device *dev) | ||
402 | { | ||
403 | struct jz_battery *jz_battery = dev_get_drvdata(dev); | ||
404 | |||
405 | schedule_delayed_work(&jz_battery->work, 0); | ||
406 | |||
407 | return 0; | ||
408 | } | ||
409 | |||
410 | static const struct dev_pm_ops jz_battery_pm_ops = { | ||
411 | .suspend = jz_battery_suspend, | ||
412 | .resume = jz_battery_resume, | ||
413 | }; | ||
414 | |||
415 | #define JZ_BATTERY_PM_OPS (&jz_battery_pm_ops) | ||
416 | #else | ||
417 | #define JZ_BATTERY_PM_OPS NULL | ||
418 | #endif | ||
419 | |||
420 | static struct platform_driver jz_battery_driver = { | ||
421 | .probe = jz_battery_probe, | ||
422 | .remove = __devexit_p(jz_battery_remove), | ||
423 | .driver = { | ||
424 | .name = "jz4740-battery", | ||
425 | .owner = THIS_MODULE, | ||
426 | .pm = JZ_BATTERY_PM_OPS, | ||
427 | }, | ||
428 | }; | ||
429 | |||
430 | static int __init jz_battery_init(void) | ||
431 | { | ||
432 | return platform_driver_register(&jz_battery_driver); | ||
433 | } | ||
434 | module_init(jz_battery_init); | ||
435 | |||
436 | static void __exit jz_battery_exit(void) | ||
437 | { | ||
438 | platform_driver_unregister(&jz_battery_driver); | ||
439 | } | ||
440 | module_exit(jz_battery_exit); | ||
441 | |||
442 | MODULE_ALIAS("platform:jz4740-battery"); | ||
443 | MODULE_LICENSE("GPL"); | ||
444 | MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); | ||
445 | MODULE_DESCRIPTION("JZ4740 SoC battery driver"); | ||
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 10ba12c8c5e0..4301a6c7ed3b 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig | |||
@@ -774,7 +774,7 @@ config RTC_DRV_AT91SAM9_GPBR | |||
774 | 774 | ||
775 | config RTC_DRV_AU1XXX | 775 | config RTC_DRV_AU1XXX |
776 | tristate "Au1xxx Counter0 RTC support" | 776 | tristate "Au1xxx Counter0 RTC support" |
777 | depends on SOC_AU1X00 | 777 | depends on MIPS_ALCHEMY |
778 | help | 778 | help |
779 | This is a driver for the Au1xxx on-chip Counter0 (Time-Of-Year | 779 | This is a driver for the Au1xxx on-chip Counter0 (Time-Of-Year |
780 | counter) to be used as a RTC. | 780 | counter) to be used as a RTC. |
@@ -905,4 +905,15 @@ config RTC_DRV_MPC5121 | |||
905 | This driver can also be built as a module. If so, the module | 905 | This driver can also be built as a module. If so, the module |
906 | will be called rtc-mpc5121. | 906 | will be called rtc-mpc5121. |
907 | 907 | ||
908 | config RTC_DRV_JZ4740 | ||
909 | tristate "Ingenic JZ4740 SoC" | ||
910 | depends on RTC_CLASS | ||
911 | depends on MACH_JZ4740 | ||
912 | help | ||
913 | If you say yes here you get support for the Ingenic JZ4740 SoC RTC | ||
914 | controller. | ||
915 | |||
916 | This driver can also be buillt as a module. If so, the module | ||
917 | will be called rtc-jz4740. | ||
918 | |||
908 | endif # RTC_CLASS | 919 | endif # RTC_CLASS |
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 5adbba7cf89c..fedf9bb36593 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile | |||
@@ -47,6 +47,7 @@ obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o | |||
47 | obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o | 47 | obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o |
48 | obj-$(CONFIG_RTC_DRV_GENERIC) += rtc-generic.o | 48 | obj-$(CONFIG_RTC_DRV_GENERIC) += rtc-generic.o |
49 | obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o | 49 | obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o |
50 | obj-$(CONFIG_RTC_DRV_JZ4740) += rtc-jz4740.o | ||
50 | obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o | 51 | obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o |
51 | obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o | 52 | obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o |
52 | obj-$(CONFIG_RTC_DRV_M48T35) += rtc-m48t35.o | 53 | obj-$(CONFIG_RTC_DRV_M48T35) += rtc-m48t35.o |
diff --git a/drivers/rtc/rtc-jz4740.c b/drivers/rtc/rtc-jz4740.c new file mode 100644 index 000000000000..2619d57b91d7 --- /dev/null +++ b/drivers/rtc/rtc-jz4740.c | |||
@@ -0,0 +1,345 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> | ||
3 | * JZ4740 SoC RTC driver | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License as published by the | ||
7 | * Free Software Foundation; either version 2 of the License, or (at your | ||
8 | * option) any later version. | ||
9 | * | ||
10 | * You should have received a copy of the GNU General Public License along | ||
11 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
12 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/platform_device.h> | ||
19 | #include <linux/rtc.h> | ||
20 | #include <linux/slab.h> | ||
21 | #include <linux/spinlock.h> | ||
22 | |||
23 | #define JZ_REG_RTC_CTRL 0x00 | ||
24 | #define JZ_REG_RTC_SEC 0x04 | ||
25 | #define JZ_REG_RTC_SEC_ALARM 0x08 | ||
26 | #define JZ_REG_RTC_REGULATOR 0x0C | ||
27 | #define JZ_REG_RTC_HIBERNATE 0x20 | ||
28 | #define JZ_REG_RTC_SCRATCHPAD 0x34 | ||
29 | |||
30 | #define JZ_RTC_CTRL_WRDY BIT(7) | ||
31 | #define JZ_RTC_CTRL_1HZ BIT(6) | ||
32 | #define JZ_RTC_CTRL_1HZ_IRQ BIT(5) | ||
33 | #define JZ_RTC_CTRL_AF BIT(4) | ||
34 | #define JZ_RTC_CTRL_AF_IRQ BIT(3) | ||
35 | #define JZ_RTC_CTRL_AE BIT(2) | ||
36 | #define JZ_RTC_CTRL_ENABLE BIT(0) | ||
37 | |||
38 | struct jz4740_rtc { | ||
39 | struct resource *mem; | ||
40 | void __iomem *base; | ||
41 | |||
42 | struct rtc_device *rtc; | ||
43 | |||
44 | unsigned int irq; | ||
45 | |||
46 | spinlock_t lock; | ||
47 | }; | ||
48 | |||
49 | static inline uint32_t jz4740_rtc_reg_read(struct jz4740_rtc *rtc, size_t reg) | ||
50 | { | ||
51 | return readl(rtc->base + reg); | ||
52 | } | ||
53 | |||
54 | static int jz4740_rtc_wait_write_ready(struct jz4740_rtc *rtc) | ||
55 | { | ||
56 | uint32_t ctrl; | ||
57 | int timeout = 1000; | ||
58 | |||
59 | do { | ||
60 | ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL); | ||
61 | } while (!(ctrl & JZ_RTC_CTRL_WRDY) && --timeout); | ||
62 | |||
63 | return timeout ? 0 : -EIO; | ||
64 | } | ||
65 | |||
66 | static inline int jz4740_rtc_reg_write(struct jz4740_rtc *rtc, size_t reg, | ||
67 | uint32_t val) | ||
68 | { | ||
69 | int ret; | ||
70 | ret = jz4740_rtc_wait_write_ready(rtc); | ||
71 | if (ret == 0) | ||
72 | writel(val, rtc->base + reg); | ||
73 | |||
74 | return ret; | ||
75 | } | ||
76 | |||
77 | static int jz4740_rtc_ctrl_set_bits(struct jz4740_rtc *rtc, uint32_t mask, | ||
78 | bool set) | ||
79 | { | ||
80 | int ret; | ||
81 | unsigned long flags; | ||
82 | uint32_t ctrl; | ||
83 | |||
84 | spin_lock_irqsave(&rtc->lock, flags); | ||
85 | |||
86 | ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL); | ||
87 | |||
88 | /* Don't clear interrupt flags by accident */ | ||
89 | ctrl |= JZ_RTC_CTRL_1HZ | JZ_RTC_CTRL_AF; | ||
90 | |||
91 | if (set) | ||
92 | ctrl |= mask; | ||
93 | else | ||
94 | ctrl &= ~mask; | ||
95 | |||
96 | ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_CTRL, ctrl); | ||
97 | |||
98 | spin_unlock_irqrestore(&rtc->lock, flags); | ||
99 | |||
100 | return ret; | ||
101 | } | ||
102 | |||
103 | static int jz4740_rtc_read_time(struct device *dev, struct rtc_time *time) | ||
104 | { | ||
105 | struct jz4740_rtc *rtc = dev_get_drvdata(dev); | ||
106 | uint32_t secs, secs2; | ||
107 | int timeout = 5; | ||
108 | |||
109 | /* If the seconds register is read while it is updated, it can contain a | ||
110 | * bogus value. This can be avoided by making sure that two consecutive | ||
111 | * reads have the same value. | ||
112 | */ | ||
113 | secs = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC); | ||
114 | secs2 = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC); | ||
115 | |||
116 | while (secs != secs2 && --timeout) { | ||
117 | secs = secs2; | ||
118 | secs2 = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC); | ||
119 | } | ||
120 | |||
121 | if (timeout == 0) | ||
122 | return -EIO; | ||
123 | |||
124 | rtc_time_to_tm(secs, time); | ||
125 | |||
126 | return rtc_valid_tm(time); | ||
127 | } | ||
128 | |||
129 | static int jz4740_rtc_set_mmss(struct device *dev, unsigned long secs) | ||
130 | { | ||
131 | struct jz4740_rtc *rtc = dev_get_drvdata(dev); | ||
132 | |||
133 | return jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC, secs); | ||
134 | } | ||
135 | |||
136 | static int jz4740_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) | ||
137 | { | ||
138 | struct jz4740_rtc *rtc = dev_get_drvdata(dev); | ||
139 | uint32_t secs; | ||
140 | uint32_t ctrl; | ||
141 | |||
142 | secs = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC_ALARM); | ||
143 | |||
144 | ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL); | ||
145 | |||
146 | alrm->enabled = !!(ctrl & JZ_RTC_CTRL_AE); | ||
147 | alrm->pending = !!(ctrl & JZ_RTC_CTRL_AF); | ||
148 | |||
149 | rtc_time_to_tm(secs, &alrm->time); | ||
150 | |||
151 | return rtc_valid_tm(&alrm->time); | ||
152 | } | ||
153 | |||
154 | static int jz4740_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) | ||
155 | { | ||
156 | int ret; | ||
157 | struct jz4740_rtc *rtc = dev_get_drvdata(dev); | ||
158 | unsigned long secs; | ||
159 | |||
160 | rtc_tm_to_time(&alrm->time, &secs); | ||
161 | |||
162 | ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC_ALARM, secs); | ||
163 | if (!ret) | ||
164 | ret = jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_AE, alrm->enabled); | ||
165 | |||
166 | return ret; | ||
167 | } | ||
168 | |||
169 | static int jz4740_rtc_update_irq_enable(struct device *dev, unsigned int enable) | ||
170 | { | ||
171 | struct jz4740_rtc *rtc = dev_get_drvdata(dev); | ||
172 | return jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_1HZ_IRQ, enable); | ||
173 | } | ||
174 | |||
175 | static int jz4740_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) | ||
176 | { | ||
177 | struct jz4740_rtc *rtc = dev_get_drvdata(dev); | ||
178 | return jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_AF_IRQ, enable); | ||
179 | } | ||
180 | |||
181 | static struct rtc_class_ops jz4740_rtc_ops = { | ||
182 | .read_time = jz4740_rtc_read_time, | ||
183 | .set_mmss = jz4740_rtc_set_mmss, | ||
184 | .read_alarm = jz4740_rtc_read_alarm, | ||
185 | .set_alarm = jz4740_rtc_set_alarm, | ||
186 | .update_irq_enable = jz4740_rtc_update_irq_enable, | ||
187 | .alarm_irq_enable = jz4740_rtc_alarm_irq_enable, | ||
188 | }; | ||
189 | |||
190 | static irqreturn_t jz4740_rtc_irq(int irq, void *data) | ||
191 | { | ||
192 | struct jz4740_rtc *rtc = data; | ||
193 | uint32_t ctrl; | ||
194 | unsigned long events = 0; | ||
195 | |||
196 | ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL); | ||
197 | |||
198 | if (ctrl & JZ_RTC_CTRL_1HZ) | ||
199 | events |= (RTC_UF | RTC_IRQF); | ||
200 | |||
201 | if (ctrl & JZ_RTC_CTRL_AF) | ||
202 | events |= (RTC_AF | RTC_IRQF); | ||
203 | |||
204 | rtc_update_irq(rtc->rtc, 1, events); | ||
205 | |||
206 | jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_1HZ | JZ_RTC_CTRL_AF, false); | ||
207 | |||
208 | return IRQ_HANDLED; | ||
209 | } | ||
210 | |||
211 | void jz4740_rtc_poweroff(struct device *dev) | ||
212 | { | ||
213 | struct jz4740_rtc *rtc = dev_get_drvdata(dev); | ||
214 | jz4740_rtc_reg_write(rtc, JZ_REG_RTC_HIBERNATE, 1); | ||
215 | } | ||
216 | EXPORT_SYMBOL_GPL(jz4740_rtc_poweroff); | ||
217 | |||
218 | static int __devinit jz4740_rtc_probe(struct platform_device *pdev) | ||
219 | { | ||
220 | int ret; | ||
221 | struct jz4740_rtc *rtc; | ||
222 | uint32_t scratchpad; | ||
223 | |||
224 | rtc = kzalloc(sizeof(*rtc), GFP_KERNEL); | ||
225 | if (!rtc) | ||
226 | return -ENOMEM; | ||
227 | |||
228 | rtc->irq = platform_get_irq(pdev, 0); | ||
229 | if (rtc->irq < 0) { | ||
230 | ret = -ENOENT; | ||
231 | dev_err(&pdev->dev, "Failed to get platform irq\n"); | ||
232 | goto err_free; | ||
233 | } | ||
234 | |||
235 | rtc->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
236 | if (!rtc->mem) { | ||
237 | ret = -ENOENT; | ||
238 | dev_err(&pdev->dev, "Failed to get platform mmio memory\n"); | ||
239 | goto err_free; | ||
240 | } | ||
241 | |||
242 | rtc->mem = request_mem_region(rtc->mem->start, resource_size(rtc->mem), | ||
243 | pdev->name); | ||
244 | if (!rtc->mem) { | ||
245 | ret = -EBUSY; | ||
246 | dev_err(&pdev->dev, "Failed to request mmio memory region\n"); | ||
247 | goto err_free; | ||
248 | } | ||
249 | |||
250 | rtc->base = ioremap_nocache(rtc->mem->start, resource_size(rtc->mem)); | ||
251 | if (!rtc->base) { | ||
252 | ret = -EBUSY; | ||
253 | dev_err(&pdev->dev, "Failed to ioremap mmio memory\n"); | ||
254 | goto err_release_mem_region; | ||
255 | } | ||
256 | |||
257 | spin_lock_init(&rtc->lock); | ||
258 | |||
259 | platform_set_drvdata(pdev, rtc); | ||
260 | |||
261 | rtc->rtc = rtc_device_register(pdev->name, &pdev->dev, &jz4740_rtc_ops, | ||
262 | THIS_MODULE); | ||
263 | if (IS_ERR(rtc->rtc)) { | ||
264 | ret = PTR_ERR(rtc->rtc); | ||
265 | dev_err(&pdev->dev, "Failed to register rtc device: %d\n", ret); | ||
266 | goto err_iounmap; | ||
267 | } | ||
268 | |||
269 | ret = request_irq(rtc->irq, jz4740_rtc_irq, 0, | ||
270 | pdev->name, rtc); | ||
271 | if (ret) { | ||
272 | dev_err(&pdev->dev, "Failed to request rtc irq: %d\n", ret); | ||
273 | goto err_unregister_rtc; | ||
274 | } | ||
275 | |||
276 | scratchpad = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SCRATCHPAD); | ||
277 | if (scratchpad != 0x12345678) { | ||
278 | ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SCRATCHPAD, 0x12345678); | ||
279 | ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC, 0); | ||
280 | if (ret) { | ||
281 | dev_err(&pdev->dev, "Could not write write to RTC registers\n"); | ||
282 | goto err_free_irq; | ||
283 | } | ||
284 | } | ||
285 | |||
286 | return 0; | ||
287 | |||
288 | err_free_irq: | ||
289 | free_irq(rtc->irq, rtc); | ||
290 | err_unregister_rtc: | ||
291 | rtc_device_unregister(rtc->rtc); | ||
292 | err_iounmap: | ||
293 | platform_set_drvdata(pdev, NULL); | ||
294 | iounmap(rtc->base); | ||
295 | err_release_mem_region: | ||
296 | release_mem_region(rtc->mem->start, resource_size(rtc->mem)); | ||
297 | err_free: | ||
298 | kfree(rtc); | ||
299 | |||
300 | return ret; | ||
301 | } | ||
302 | |||
303 | static int __devexit jz4740_rtc_remove(struct platform_device *pdev) | ||
304 | { | ||
305 | struct jz4740_rtc *rtc = platform_get_drvdata(pdev); | ||
306 | |||
307 | free_irq(rtc->irq, rtc); | ||
308 | |||
309 | rtc_device_unregister(rtc->rtc); | ||
310 | |||
311 | iounmap(rtc->base); | ||
312 | release_mem_region(rtc->mem->start, resource_size(rtc->mem)); | ||
313 | |||
314 | kfree(rtc); | ||
315 | |||
316 | platform_set_drvdata(pdev, NULL); | ||
317 | |||
318 | return 0; | ||
319 | } | ||
320 | |||
321 | struct platform_driver jz4740_rtc_driver = { | ||
322 | .probe = jz4740_rtc_probe, | ||
323 | .remove = __devexit_p(jz4740_rtc_remove), | ||
324 | .driver = { | ||
325 | .name = "jz4740-rtc", | ||
326 | .owner = THIS_MODULE, | ||
327 | }, | ||
328 | }; | ||
329 | |||
330 | static int __init jz4740_rtc_init(void) | ||
331 | { | ||
332 | return platform_driver_register(&jz4740_rtc_driver); | ||
333 | } | ||
334 | module_init(jz4740_rtc_init); | ||
335 | |||
336 | static void __exit jz4740_rtc_exit(void) | ||
337 | { | ||
338 | platform_driver_unregister(&jz4740_rtc_driver); | ||
339 | } | ||
340 | module_exit(jz4740_rtc_exit); | ||
341 | |||
342 | MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); | ||
343 | MODULE_LICENSE("GPL"); | ||
344 | MODULE_DESCRIPTION("RTC driver for the JZ4740 SoC\n"); | ||
345 | MODULE_ALIAS("platform:jz4740-rtc"); | ||
diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c index 891e1dd65f24..09ef57034c9c 100644 --- a/drivers/serial/8250.c +++ b/drivers/serial/8250.c | |||
@@ -302,7 +302,7 @@ static const struct serial8250_config uart_config[] = { | |||
302 | }, | 302 | }, |
303 | }; | 303 | }; |
304 | 304 | ||
305 | #if defined (CONFIG_SERIAL_8250_AU1X00) | 305 | #if defined(CONFIG_MIPS_ALCHEMY) |
306 | 306 | ||
307 | /* Au1x00 UART hardware has a weird register layout */ | 307 | /* Au1x00 UART hardware has a weird register layout */ |
308 | static const u8 au_io_in_map[] = { | 308 | static const u8 au_io_in_map[] = { |
@@ -422,7 +422,6 @@ static unsigned int mem32_serial_in(struct uart_port *p, int offset) | |||
422 | return readl(p->membase + offset); | 422 | return readl(p->membase + offset); |
423 | } | 423 | } |
424 | 424 | ||
425 | #ifdef CONFIG_SERIAL_8250_AU1X00 | ||
426 | static unsigned int au_serial_in(struct uart_port *p, int offset) | 425 | static unsigned int au_serial_in(struct uart_port *p, int offset) |
427 | { | 426 | { |
428 | offset = map_8250_in_reg(p, offset) << p->regshift; | 427 | offset = map_8250_in_reg(p, offset) << p->regshift; |
@@ -434,7 +433,6 @@ static void au_serial_out(struct uart_port *p, int offset, int value) | |||
434 | offset = map_8250_out_reg(p, offset) << p->regshift; | 433 | offset = map_8250_out_reg(p, offset) << p->regshift; |
435 | __raw_writel(value, p->membase + offset); | 434 | __raw_writel(value, p->membase + offset); |
436 | } | 435 | } |
437 | #endif | ||
438 | 436 | ||
439 | static unsigned int tsi_serial_in(struct uart_port *p, int offset) | 437 | static unsigned int tsi_serial_in(struct uart_port *p, int offset) |
440 | { | 438 | { |
@@ -503,12 +501,11 @@ static void set_io_from_upio(struct uart_port *p) | |||
503 | p->serial_out = mem32_serial_out; | 501 | p->serial_out = mem32_serial_out; |
504 | break; | 502 | break; |
505 | 503 | ||
506 | #ifdef CONFIG_SERIAL_8250_AU1X00 | ||
507 | case UPIO_AU: | 504 | case UPIO_AU: |
508 | p->serial_in = au_serial_in; | 505 | p->serial_in = au_serial_in; |
509 | p->serial_out = au_serial_out; | 506 | p->serial_out = au_serial_out; |
510 | break; | 507 | break; |
511 | #endif | 508 | |
512 | case UPIO_TSI: | 509 | case UPIO_TSI: |
513 | p->serial_in = tsi_serial_in; | 510 | p->serial_in = tsi_serial_in; |
514 | p->serial_out = tsi_serial_out; | 511 | p->serial_out = tsi_serial_out; |
@@ -535,9 +532,7 @@ serial_out_sync(struct uart_8250_port *up, int offset, int value) | |||
535 | switch (p->iotype) { | 532 | switch (p->iotype) { |
536 | case UPIO_MEM: | 533 | case UPIO_MEM: |
537 | case UPIO_MEM32: | 534 | case UPIO_MEM32: |
538 | #ifdef CONFIG_SERIAL_8250_AU1X00 | ||
539 | case UPIO_AU: | 535 | case UPIO_AU: |
540 | #endif | ||
541 | case UPIO_DWAPB: | 536 | case UPIO_DWAPB: |
542 | p->serial_out(p, offset, value); | 537 | p->serial_out(p, offset, value); |
543 | p->serial_in(p, UART_LCR); /* safe, no side-effects */ | 538 | p->serial_in(p, UART_LCR); /* safe, no side-effects */ |
@@ -573,7 +568,7 @@ static inline void _serial_dl_write(struct uart_8250_port *up, int value) | |||
573 | serial_outp(up, UART_DLM, value >> 8 & 0xff); | 568 | serial_outp(up, UART_DLM, value >> 8 & 0xff); |
574 | } | 569 | } |
575 | 570 | ||
576 | #if defined(CONFIG_SERIAL_8250_AU1X00) | 571 | #if defined(CONFIG_MIPS_ALCHEMY) |
577 | /* Au1x00 haven't got a standard divisor latch */ | 572 | /* Au1x00 haven't got a standard divisor latch */ |
578 | static int serial_dl_read(struct uart_8250_port *up) | 573 | static int serial_dl_read(struct uart_8250_port *up) |
579 | { | 574 | { |
@@ -2596,11 +2591,9 @@ static void serial8250_config_port(struct uart_port *port, int flags) | |||
2596 | if (flags & UART_CONFIG_TYPE) | 2591 | if (flags & UART_CONFIG_TYPE) |
2597 | autoconfig(up, probeflags); | 2592 | autoconfig(up, probeflags); |
2598 | 2593 | ||
2599 | #ifdef CONFIG_SERIAL_8250_AU1X00 | ||
2600 | /* if access method is AU, it is a 16550 with a quirk */ | 2594 | /* if access method is AU, it is a 16550 with a quirk */ |
2601 | if (up->port.type == PORT_16550A && up->port.iotype == UPIO_AU) | 2595 | if (up->port.type == PORT_16550A && up->port.iotype == UPIO_AU) |
2602 | up->bugs |= UART_BUG_NOMSR; | 2596 | up->bugs |= UART_BUG_NOMSR; |
2603 | #endif | ||
2604 | 2597 | ||
2605 | if (up->port.type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ) | 2598 | if (up->port.type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ) |
2606 | autoconfig_irq(up); | 2599 | autoconfig_irq(up); |
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 8b23165bc5dc..e437ce8c1748 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig | |||
@@ -258,14 +258,6 @@ config SERIAL_8250_ACORN | |||
258 | system, say Y to this option. The driver can handle 1, 2, or 3 port | 258 | system, say Y to this option. The driver can handle 1, 2, or 3 port |
259 | cards. If unsure, say N. | 259 | cards. If unsure, say N. |
260 | 260 | ||
261 | config SERIAL_8250_AU1X00 | ||
262 | bool "Au1x00 serial port support" | ||
263 | depends on SERIAL_8250 != n && SOC_AU1X00 | ||
264 | help | ||
265 | If you have an Au1x00 SOC based board and want to use the serial port, | ||
266 | say Y to this option. The driver can handle up to 4 serial ports, | ||
267 | depending on the SOC. If unsure, say N. | ||
268 | |||
269 | config SERIAL_8250_RM9K | 261 | config SERIAL_8250_RM9K |
270 | bool "Support for MIPS RM9xxx integrated serial port" | 262 | bool "Support for MIPS RM9xxx integrated serial port" |
271 | depends on SERIAL_8250 != n && SERIAL_RM9000 | 263 | depends on SERIAL_8250 != n && SERIAL_RM9000 |
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 6a58cb1330c1..4aa00e6e57ad 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig | |||
@@ -45,7 +45,8 @@ config USB_ARCH_HAS_OHCI | |||
45 | default y if STB03xxx | 45 | default y if STB03xxx |
46 | default y if PPC_MPC52xx | 46 | default y if PPC_MPC52xx |
47 | # MIPS: | 47 | # MIPS: |
48 | default y if SOC_AU1X00 | 48 | default y if MIPS_ALCHEMY |
49 | default y if MACH_JZ4740 | ||
49 | # SH: | 50 | # SH: |
50 | default y if CPU_SUBTYPE_SH7720 | 51 | default y if CPU_SUBTYPE_SH7720 |
51 | default y if CPU_SUBTYPE_SH7721 | 52 | default y if CPU_SUBTYPE_SH7721 |
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index fc576557d8a5..02864a237a2c 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c | |||
@@ -1031,7 +1031,7 @@ MODULE_LICENSE ("GPL"); | |||
1031 | #define PLATFORM_DRIVER ohci_hcd_ep93xx_driver | 1031 | #define PLATFORM_DRIVER ohci_hcd_ep93xx_driver |
1032 | #endif | 1032 | #endif |
1033 | 1033 | ||
1034 | #ifdef CONFIG_SOC_AU1X00 | 1034 | #ifdef CONFIG_MIPS_ALCHEMY |
1035 | #include "ohci-au1xxx.c" | 1035 | #include "ohci-au1xxx.c" |
1036 | #define PLATFORM_DRIVER ohci_hcd_au1xxx_driver | 1036 | #define PLATFORM_DRIVER ohci_hcd_au1xxx_driver |
1037 | #endif | 1037 | #endif |
@@ -1095,6 +1095,11 @@ MODULE_LICENSE ("GPL"); | |||
1095 | #define TMIO_OHCI_DRIVER ohci_hcd_tmio_driver | 1095 | #define TMIO_OHCI_DRIVER ohci_hcd_tmio_driver |
1096 | #endif | 1096 | #endif |
1097 | 1097 | ||
1098 | #ifdef CONFIG_MACH_JZ4740 | ||
1099 | #include "ohci-jz4740.c" | ||
1100 | #define PLATFORM_DRIVER ohci_hcd_jz4740_driver | ||
1101 | #endif | ||
1102 | |||
1098 | #if !defined(PCI_DRIVER) && \ | 1103 | #if !defined(PCI_DRIVER) && \ |
1099 | !defined(PLATFORM_DRIVER) && \ | 1104 | !defined(PLATFORM_DRIVER) && \ |
1100 | !defined(OMAP1_PLATFORM_DRIVER) && \ | 1105 | !defined(OMAP1_PLATFORM_DRIVER) && \ |
diff --git a/drivers/usb/host/ohci-jz4740.c b/drivers/usb/host/ohci-jz4740.c new file mode 100644 index 000000000000..10e1872f3ab9 --- /dev/null +++ b/drivers/usb/host/ohci-jz4740.c | |||
@@ -0,0 +1,276 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms of the GNU General Public License as published by the | ||
6 | * Free Software Foundation; either version 2 of the License, or (at your | ||
7 | * option) any later version. | ||
8 | * | ||
9 | * You should have received a copy of the GNU General Public License along | ||
10 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
11 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
12 | * | ||
13 | */ | ||
14 | |||
15 | #include <linux/platform_device.h> | ||
16 | #include <linux/clk.h> | ||
17 | #include <linux/regulator/consumer.h> | ||
18 | |||
19 | struct jz4740_ohci_hcd { | ||
20 | struct ohci_hcd ohci_hcd; | ||
21 | |||
22 | struct regulator *vbus; | ||
23 | bool vbus_enabled; | ||
24 | struct clk *clk; | ||
25 | }; | ||
26 | |||
27 | static inline struct jz4740_ohci_hcd *hcd_to_jz4740_hcd(struct usb_hcd *hcd) | ||
28 | { | ||
29 | return (struct jz4740_ohci_hcd *)(hcd->hcd_priv); | ||
30 | } | ||
31 | |||
32 | static inline struct usb_hcd *jz4740_hcd_to_hcd(struct jz4740_ohci_hcd *jz4740_ohci) | ||
33 | { | ||
34 | return container_of((void *)jz4740_ohci, struct usb_hcd, hcd_priv); | ||
35 | } | ||
36 | |||
37 | static int ohci_jz4740_start(struct usb_hcd *hcd) | ||
38 | { | ||
39 | struct ohci_hcd *ohci = hcd_to_ohci(hcd); | ||
40 | int ret; | ||
41 | |||
42 | ret = ohci_init(ohci); | ||
43 | if (ret < 0) | ||
44 | return ret; | ||
45 | |||
46 | ohci->num_ports = 1; | ||
47 | |||
48 | ret = ohci_run(ohci); | ||
49 | if (ret < 0) { | ||
50 | dev_err(hcd->self.controller, "Can not start %s", | ||
51 | hcd->self.bus_name); | ||
52 | ohci_stop(hcd); | ||
53 | return ret; | ||
54 | } | ||
55 | return 0; | ||
56 | } | ||
57 | |||
58 | static int ohci_jz4740_set_vbus_power(struct jz4740_ohci_hcd *jz4740_ohci, | ||
59 | bool enabled) | ||
60 | { | ||
61 | int ret = 0; | ||
62 | |||
63 | if (!jz4740_ohci->vbus) | ||
64 | return 0; | ||
65 | |||
66 | if (enabled && !jz4740_ohci->vbus_enabled) { | ||
67 | ret = regulator_enable(jz4740_ohci->vbus); | ||
68 | if (ret) | ||
69 | dev_err(jz4740_hcd_to_hcd(jz4740_ohci)->self.controller, | ||
70 | "Could not power vbus\n"); | ||
71 | } else if (!enabled && jz4740_ohci->vbus_enabled) { | ||
72 | ret = regulator_disable(jz4740_ohci->vbus); | ||
73 | } | ||
74 | |||
75 | if (ret == 0) | ||
76 | jz4740_ohci->vbus_enabled = enabled; | ||
77 | |||
78 | return ret; | ||
79 | } | ||
80 | |||
81 | static int ohci_jz4740_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, | ||
82 | u16 wIndex, char *buf, u16 wLength) | ||
83 | { | ||
84 | struct jz4740_ohci_hcd *jz4740_ohci = hcd_to_jz4740_hcd(hcd); | ||
85 | int ret; | ||
86 | |||
87 | switch (typeReq) { | ||
88 | case SetHubFeature: | ||
89 | if (wValue == USB_PORT_FEAT_POWER) | ||
90 | ret = ohci_jz4740_set_vbus_power(jz4740_ohci, true); | ||
91 | break; | ||
92 | case ClearHubFeature: | ||
93 | if (wValue == USB_PORT_FEAT_POWER) | ||
94 | ret = ohci_jz4740_set_vbus_power(jz4740_ohci, false); | ||
95 | break; | ||
96 | } | ||
97 | |||
98 | if (ret) | ||
99 | return ret; | ||
100 | |||
101 | return ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength); | ||
102 | } | ||
103 | |||
104 | |||
105 | static const struct hc_driver ohci_jz4740_hc_driver = { | ||
106 | .description = hcd_name, | ||
107 | .product_desc = "JZ4740 OHCI", | ||
108 | .hcd_priv_size = sizeof(struct jz4740_ohci_hcd), | ||
109 | |||
110 | /* | ||
111 | * generic hardware linkage | ||
112 | */ | ||
113 | .irq = ohci_irq, | ||
114 | .flags = HCD_USB11 | HCD_MEMORY, | ||
115 | |||
116 | /* | ||
117 | * basic lifecycle operations | ||
118 | */ | ||
119 | .start = ohci_jz4740_start, | ||
120 | .stop = ohci_stop, | ||
121 | .shutdown = ohci_shutdown, | ||
122 | |||
123 | /* | ||
124 | * managing i/o requests and associated device resources | ||
125 | */ | ||
126 | .urb_enqueue = ohci_urb_enqueue, | ||
127 | .urb_dequeue = ohci_urb_dequeue, | ||
128 | .endpoint_disable = ohci_endpoint_disable, | ||
129 | |||
130 | /* | ||
131 | * scheduling support | ||
132 | */ | ||
133 | .get_frame_number = ohci_get_frame, | ||
134 | |||
135 | /* | ||
136 | * root hub support | ||
137 | */ | ||
138 | .hub_status_data = ohci_hub_status_data, | ||
139 | .hub_control = ohci_jz4740_hub_control, | ||
140 | #ifdef CONFIG_PM | ||
141 | .bus_suspend = ohci_bus_suspend, | ||
142 | .bus_resume = ohci_bus_resume, | ||
143 | #endif | ||
144 | .start_port_reset = ohci_start_port_reset, | ||
145 | }; | ||
146 | |||
147 | |||
148 | static __devinit int jz4740_ohci_probe(struct platform_device *pdev) | ||
149 | { | ||
150 | int ret; | ||
151 | struct usb_hcd *hcd; | ||
152 | struct jz4740_ohci_hcd *jz4740_ohci; | ||
153 | struct resource *res; | ||
154 | int irq; | ||
155 | |||
156 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
157 | |||
158 | if (!res) { | ||
159 | dev_err(&pdev->dev, "Failed to get platform resource\n"); | ||
160 | return -ENOENT; | ||
161 | } | ||
162 | |||
163 | irq = platform_get_irq(pdev, 0); | ||
164 | if (irq < 0) { | ||
165 | dev_err(&pdev->dev, "Failed to get platform irq\n"); | ||
166 | return irq; | ||
167 | } | ||
168 | |||
169 | hcd = usb_create_hcd(&ohci_jz4740_hc_driver, &pdev->dev, "jz4740"); | ||
170 | if (!hcd) { | ||
171 | dev_err(&pdev->dev, "Failed to create hcd.\n"); | ||
172 | return -ENOMEM; | ||
173 | } | ||
174 | |||
175 | jz4740_ohci = hcd_to_jz4740_hcd(hcd); | ||
176 | |||
177 | res = request_mem_region(res->start, resource_size(res), hcd_name); | ||
178 | if (!res) { | ||
179 | dev_err(&pdev->dev, "Failed to request mem region.\n"); | ||
180 | ret = -EBUSY; | ||
181 | goto err_free; | ||
182 | } | ||
183 | |||
184 | hcd->rsrc_start = res->start; | ||
185 | hcd->rsrc_len = resource_size(res); | ||
186 | hcd->regs = ioremap(res->start, resource_size(res)); | ||
187 | |||
188 | if (!hcd->regs) { | ||
189 | dev_err(&pdev->dev, "Failed to ioremap registers.\n"); | ||
190 | ret = -EBUSY; | ||
191 | goto err_release_mem; | ||
192 | } | ||
193 | |||
194 | jz4740_ohci->clk = clk_get(&pdev->dev, "uhc"); | ||
195 | if (IS_ERR(jz4740_ohci->clk)) { | ||
196 | ret = PTR_ERR(jz4740_ohci->clk); | ||
197 | dev_err(&pdev->dev, "Failed to get clock: %d\n", ret); | ||
198 | goto err_iounmap; | ||
199 | } | ||
200 | |||
201 | jz4740_ohci->vbus = regulator_get(&pdev->dev, "vbus"); | ||
202 | if (IS_ERR(jz4740_ohci->vbus)) | ||
203 | jz4740_ohci->vbus = NULL; | ||
204 | |||
205 | |||
206 | clk_set_rate(jz4740_ohci->clk, 48000000); | ||
207 | clk_enable(jz4740_ohci->clk); | ||
208 | if (jz4740_ohci->vbus) | ||
209 | ohci_jz4740_set_vbus_power(jz4740_ohci, true); | ||
210 | |||
211 | platform_set_drvdata(pdev, hcd); | ||
212 | |||
213 | ohci_hcd_init(hcd_to_ohci(hcd)); | ||
214 | |||
215 | ret = usb_add_hcd(hcd, irq, 0); | ||
216 | if (ret) { | ||
217 | dev_err(&pdev->dev, "Failed to add hcd: %d\n", ret); | ||
218 | goto err_disable; | ||
219 | } | ||
220 | |||
221 | return 0; | ||
222 | |||
223 | err_disable: | ||
224 | platform_set_drvdata(pdev, NULL); | ||
225 | if (jz4740_ohci->vbus) { | ||
226 | regulator_disable(jz4740_ohci->vbus); | ||
227 | regulator_put(jz4740_ohci->vbus); | ||
228 | } | ||
229 | clk_disable(jz4740_ohci->clk); | ||
230 | |||
231 | clk_put(jz4740_ohci->clk); | ||
232 | err_iounmap: | ||
233 | iounmap(hcd->regs); | ||
234 | err_release_mem: | ||
235 | release_mem_region(res->start, resource_size(res)); | ||
236 | err_free: | ||
237 | usb_put_hcd(hcd); | ||
238 | |||
239 | return ret; | ||
240 | } | ||
241 | |||
242 | static __devexit int jz4740_ohci_remove(struct platform_device *pdev) | ||
243 | { | ||
244 | struct usb_hcd *hcd = platform_get_drvdata(pdev); | ||
245 | struct jz4740_ohci_hcd *jz4740_ohci = hcd_to_jz4740_hcd(hcd); | ||
246 | |||
247 | usb_remove_hcd(hcd); | ||
248 | |||
249 | platform_set_drvdata(pdev, NULL); | ||
250 | |||
251 | if (jz4740_ohci->vbus) { | ||
252 | regulator_disable(jz4740_ohci->vbus); | ||
253 | regulator_put(jz4740_ohci->vbus); | ||
254 | } | ||
255 | |||
256 | clk_disable(jz4740_ohci->clk); | ||
257 | clk_put(jz4740_ohci->clk); | ||
258 | |||
259 | iounmap(hcd->regs); | ||
260 | release_mem_region(hcd->rsrc_start, hcd->rsrc_len); | ||
261 | |||
262 | usb_put_hcd(hcd); | ||
263 | |||
264 | return 0; | ||
265 | } | ||
266 | |||
267 | static struct platform_driver ohci_hcd_jz4740_driver = { | ||
268 | .probe = jz4740_ohci_probe, | ||
269 | .remove = __devexit_p(jz4740_ohci_remove), | ||
270 | .driver = { | ||
271 | .name = "jz4740-ohci", | ||
272 | .owner = THIS_MODULE, | ||
273 | }, | ||
274 | }; | ||
275 | |||
276 | MODULE_ALIAS("platfrom:jz4740-ohci"); | ||
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 3d94a1471724..9e711a1d0d97 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig | |||
@@ -2229,6 +2229,15 @@ config FB_BROADSHEET | |||
2229 | and could also have been called by other names when coupled with | 2229 | and could also have been called by other names when coupled with |
2230 | a bridge adapter. | 2230 | a bridge adapter. |
2231 | 2231 | ||
2232 | config FB_JZ4740 | ||
2233 | tristate "JZ4740 LCD framebuffer support" | ||
2234 | depends on FB && MACH_JZ4740 | ||
2235 | select FB_SYS_FILLRECT | ||
2236 | select FB_SYS_COPYAREA | ||
2237 | select FB_SYS_IMAGEBLIT | ||
2238 | help | ||
2239 | Framebuffer support for the JZ4740 SoC. | ||
2240 | |||
2232 | source "drivers/video/omap/Kconfig" | 2241 | source "drivers/video/omap/Kconfig" |
2233 | source "drivers/video/omap2/Kconfig" | 2242 | source "drivers/video/omap2/Kconfig" |
2234 | 2243 | ||
diff --git a/drivers/video/Makefile b/drivers/video/Makefile index ddc2af2ba45b..f56a9cae2157 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile | |||
@@ -131,6 +131,7 @@ obj-$(CONFIG_FB_CARMINE) += carminefb.o | |||
131 | obj-$(CONFIG_FB_MB862XX) += mb862xx/ | 131 | obj-$(CONFIG_FB_MB862XX) += mb862xx/ |
132 | obj-$(CONFIG_FB_MSM) += msm/ | 132 | obj-$(CONFIG_FB_MSM) += msm/ |
133 | obj-$(CONFIG_FB_NUC900) += nuc900fb.o | 133 | obj-$(CONFIG_FB_NUC900) += nuc900fb.o |
134 | obj-$(CONFIG_FB_JZ4740) += jz4740_fb.o | ||
134 | 135 | ||
135 | # Platform or fallback drivers go here | 136 | # Platform or fallback drivers go here |
136 | obj-$(CONFIG_FB_UVESA) += uvesafb.o | 137 | obj-$(CONFIG_FB_UVESA) += uvesafb.o |
diff --git a/drivers/video/jz4740_fb.c b/drivers/video/jz4740_fb.c new file mode 100644 index 000000000000..670ecaa0385a --- /dev/null +++ b/drivers/video/jz4740_fb.c | |||
@@ -0,0 +1,847 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> | ||
3 | * JZ4740 SoC LCD framebuffer driver | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License as published by the | ||
7 | * Free Software Foundation; either version 2 of the License, or (at your | ||
8 | * option) any later version. | ||
9 | * | ||
10 | * You should have received a copy of the GNU General Public License along | ||
11 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
12 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/mutex.h> | ||
19 | #include <linux/platform_device.h> | ||
20 | |||
21 | #include <linux/clk.h> | ||
22 | #include <linux/delay.h> | ||
23 | |||
24 | #include <linux/console.h> | ||
25 | #include <linux/fb.h> | ||
26 | |||
27 | #include <linux/dma-mapping.h> | ||
28 | |||
29 | #include <asm/mach-jz4740/jz4740_fb.h> | ||
30 | #include <asm/mach-jz4740/gpio.h> | ||
31 | |||
32 | #define JZ_REG_LCD_CFG 0x00 | ||
33 | #define JZ_REG_LCD_VSYNC 0x04 | ||
34 | #define JZ_REG_LCD_HSYNC 0x08 | ||
35 | #define JZ_REG_LCD_VAT 0x0C | ||
36 | #define JZ_REG_LCD_DAH 0x10 | ||
37 | #define JZ_REG_LCD_DAV 0x14 | ||
38 | #define JZ_REG_LCD_PS 0x18 | ||
39 | #define JZ_REG_LCD_CLS 0x1C | ||
40 | #define JZ_REG_LCD_SPL 0x20 | ||
41 | #define JZ_REG_LCD_REV 0x24 | ||
42 | #define JZ_REG_LCD_CTRL 0x30 | ||
43 | #define JZ_REG_LCD_STATE 0x34 | ||
44 | #define JZ_REG_LCD_IID 0x38 | ||
45 | #define JZ_REG_LCD_DA0 0x40 | ||
46 | #define JZ_REG_LCD_SA0 0x44 | ||
47 | #define JZ_REG_LCD_FID0 0x48 | ||
48 | #define JZ_REG_LCD_CMD0 0x4C | ||
49 | #define JZ_REG_LCD_DA1 0x50 | ||
50 | #define JZ_REG_LCD_SA1 0x54 | ||
51 | #define JZ_REG_LCD_FID1 0x58 | ||
52 | #define JZ_REG_LCD_CMD1 0x5C | ||
53 | |||
54 | #define JZ_LCD_CFG_SLCD BIT(31) | ||
55 | #define JZ_LCD_CFG_PS_DISABLE BIT(23) | ||
56 | #define JZ_LCD_CFG_CLS_DISABLE BIT(22) | ||
57 | #define JZ_LCD_CFG_SPL_DISABLE BIT(21) | ||
58 | #define JZ_LCD_CFG_REV_DISABLE BIT(20) | ||
59 | #define JZ_LCD_CFG_HSYNCM BIT(19) | ||
60 | #define JZ_LCD_CFG_PCLKM BIT(18) | ||
61 | #define JZ_LCD_CFG_INV BIT(17) | ||
62 | #define JZ_LCD_CFG_SYNC_DIR BIT(16) | ||
63 | #define JZ_LCD_CFG_PS_POLARITY BIT(15) | ||
64 | #define JZ_LCD_CFG_CLS_POLARITY BIT(14) | ||
65 | #define JZ_LCD_CFG_SPL_POLARITY BIT(13) | ||
66 | #define JZ_LCD_CFG_REV_POLARITY BIT(12) | ||
67 | #define JZ_LCD_CFG_HSYNC_ACTIVE_LOW BIT(11) | ||
68 | #define JZ_LCD_CFG_PCLK_FALLING_EDGE BIT(10) | ||
69 | #define JZ_LCD_CFG_DE_ACTIVE_LOW BIT(9) | ||
70 | #define JZ_LCD_CFG_VSYNC_ACTIVE_LOW BIT(8) | ||
71 | #define JZ_LCD_CFG_18_BIT BIT(7) | ||
72 | #define JZ_LCD_CFG_PDW (BIT(5) | BIT(4)) | ||
73 | #define JZ_LCD_CFG_MODE_MASK 0xf | ||
74 | |||
75 | #define JZ_LCD_CTRL_BURST_4 (0x0 << 28) | ||
76 | #define JZ_LCD_CTRL_BURST_8 (0x1 << 28) | ||
77 | #define JZ_LCD_CTRL_BURST_16 (0x2 << 28) | ||
78 | #define JZ_LCD_CTRL_RGB555 BIT(27) | ||
79 | #define JZ_LCD_CTRL_OFUP BIT(26) | ||
80 | #define JZ_LCD_CTRL_FRC_GRAYSCALE_16 (0x0 << 24) | ||
81 | #define JZ_LCD_CTRL_FRC_GRAYSCALE_4 (0x1 << 24) | ||
82 | #define JZ_LCD_CTRL_FRC_GRAYSCALE_2 (0x2 << 24) | ||
83 | #define JZ_LCD_CTRL_PDD_MASK (0xff << 16) | ||
84 | #define JZ_LCD_CTRL_EOF_IRQ BIT(13) | ||
85 | #define JZ_LCD_CTRL_SOF_IRQ BIT(12) | ||
86 | #define JZ_LCD_CTRL_OFU_IRQ BIT(11) | ||
87 | #define JZ_LCD_CTRL_IFU0_IRQ BIT(10) | ||
88 | #define JZ_LCD_CTRL_IFU1_IRQ BIT(9) | ||
89 | #define JZ_LCD_CTRL_DD_IRQ BIT(8) | ||
90 | #define JZ_LCD_CTRL_QDD_IRQ BIT(7) | ||
91 | #define JZ_LCD_CTRL_REVERSE_ENDIAN BIT(6) | ||
92 | #define JZ_LCD_CTRL_LSB_FISRT BIT(5) | ||
93 | #define JZ_LCD_CTRL_DISABLE BIT(4) | ||
94 | #define JZ_LCD_CTRL_ENABLE BIT(3) | ||
95 | #define JZ_LCD_CTRL_BPP_1 0x0 | ||
96 | #define JZ_LCD_CTRL_BPP_2 0x1 | ||
97 | #define JZ_LCD_CTRL_BPP_4 0x2 | ||
98 | #define JZ_LCD_CTRL_BPP_8 0x3 | ||
99 | #define JZ_LCD_CTRL_BPP_15_16 0x4 | ||
100 | #define JZ_LCD_CTRL_BPP_18_24 0x5 | ||
101 | |||
102 | #define JZ_LCD_CMD_SOF_IRQ BIT(15) | ||
103 | #define JZ_LCD_CMD_EOF_IRQ BIT(16) | ||
104 | #define JZ_LCD_CMD_ENABLE_PAL BIT(12) | ||
105 | |||
106 | #define JZ_LCD_SYNC_MASK 0x3ff | ||
107 | |||
108 | #define JZ_LCD_STATE_DISABLED BIT(0) | ||
109 | |||
110 | struct jzfb_framedesc { | ||
111 | uint32_t next; | ||
112 | uint32_t addr; | ||
113 | uint32_t id; | ||
114 | uint32_t cmd; | ||
115 | } __packed; | ||
116 | |||
117 | struct jzfb { | ||
118 | struct fb_info *fb; | ||
119 | struct platform_device *pdev; | ||
120 | void __iomem *base; | ||
121 | struct resource *mem; | ||
122 | struct jz4740_fb_platform_data *pdata; | ||
123 | |||
124 | size_t vidmem_size; | ||
125 | void *vidmem; | ||
126 | dma_addr_t vidmem_phys; | ||
127 | struct jzfb_framedesc *framedesc; | ||
128 | dma_addr_t framedesc_phys; | ||
129 | |||
130 | struct clk *ldclk; | ||
131 | struct clk *lpclk; | ||
132 | |||
133 | unsigned is_enabled:1; | ||
134 | struct mutex lock; | ||
135 | |||
136 | uint32_t pseudo_palette[16]; | ||
137 | }; | ||
138 | |||
139 | static const struct fb_fix_screeninfo jzfb_fix __devinitdata = { | ||
140 | .id = "JZ4740 FB", | ||
141 | .type = FB_TYPE_PACKED_PIXELS, | ||
142 | .visual = FB_VISUAL_TRUECOLOR, | ||
143 | .xpanstep = 0, | ||
144 | .ypanstep = 0, | ||
145 | .ywrapstep = 0, | ||
146 | .accel = FB_ACCEL_NONE, | ||
147 | }; | ||
148 | |||
149 | static const struct jz_gpio_bulk_request jz_lcd_ctrl_pins[] = { | ||
150 | JZ_GPIO_BULK_PIN(LCD_PCLK), | ||
151 | JZ_GPIO_BULK_PIN(LCD_HSYNC), | ||
152 | JZ_GPIO_BULK_PIN(LCD_VSYNC), | ||
153 | JZ_GPIO_BULK_PIN(LCD_DE), | ||
154 | JZ_GPIO_BULK_PIN(LCD_PS), | ||
155 | JZ_GPIO_BULK_PIN(LCD_REV), | ||
156 | JZ_GPIO_BULK_PIN(LCD_CLS), | ||
157 | JZ_GPIO_BULK_PIN(LCD_SPL), | ||
158 | }; | ||
159 | |||
160 | static const struct jz_gpio_bulk_request jz_lcd_data_pins[] = { | ||
161 | JZ_GPIO_BULK_PIN(LCD_DATA0), | ||
162 | JZ_GPIO_BULK_PIN(LCD_DATA1), | ||
163 | JZ_GPIO_BULK_PIN(LCD_DATA2), | ||
164 | JZ_GPIO_BULK_PIN(LCD_DATA3), | ||
165 | JZ_GPIO_BULK_PIN(LCD_DATA4), | ||
166 | JZ_GPIO_BULK_PIN(LCD_DATA5), | ||
167 | JZ_GPIO_BULK_PIN(LCD_DATA6), | ||
168 | JZ_GPIO_BULK_PIN(LCD_DATA7), | ||
169 | JZ_GPIO_BULK_PIN(LCD_DATA8), | ||
170 | JZ_GPIO_BULK_PIN(LCD_DATA9), | ||
171 | JZ_GPIO_BULK_PIN(LCD_DATA10), | ||
172 | JZ_GPIO_BULK_PIN(LCD_DATA11), | ||
173 | JZ_GPIO_BULK_PIN(LCD_DATA12), | ||
174 | JZ_GPIO_BULK_PIN(LCD_DATA13), | ||
175 | JZ_GPIO_BULK_PIN(LCD_DATA14), | ||
176 | JZ_GPIO_BULK_PIN(LCD_DATA15), | ||
177 | JZ_GPIO_BULK_PIN(LCD_DATA16), | ||
178 | JZ_GPIO_BULK_PIN(LCD_DATA17), | ||
179 | }; | ||
180 | |||
181 | static unsigned int jzfb_num_ctrl_pins(struct jzfb *jzfb) | ||
182 | { | ||
183 | unsigned int num; | ||
184 | |||
185 | switch (jzfb->pdata->lcd_type) { | ||
186 | case JZ_LCD_TYPE_GENERIC_16_BIT: | ||
187 | num = 4; | ||
188 | break; | ||
189 | case JZ_LCD_TYPE_GENERIC_18_BIT: | ||
190 | num = 4; | ||
191 | break; | ||
192 | case JZ_LCD_TYPE_8BIT_SERIAL: | ||
193 | num = 3; | ||
194 | break; | ||
195 | case JZ_LCD_TYPE_SPECIAL_TFT_1: | ||
196 | case JZ_LCD_TYPE_SPECIAL_TFT_2: | ||
197 | case JZ_LCD_TYPE_SPECIAL_TFT_3: | ||
198 | num = 8; | ||
199 | break; | ||
200 | default: | ||
201 | num = 0; | ||
202 | break; | ||
203 | } | ||
204 | return num; | ||
205 | } | ||
206 | |||
207 | static unsigned int jzfb_num_data_pins(struct jzfb *jzfb) | ||
208 | { | ||
209 | unsigned int num; | ||
210 | |||
211 | switch (jzfb->pdata->lcd_type) { | ||
212 | case JZ_LCD_TYPE_GENERIC_16_BIT: | ||
213 | num = 16; | ||
214 | break; | ||
215 | case JZ_LCD_TYPE_GENERIC_18_BIT: | ||
216 | num = 18; | ||
217 | break; | ||
218 | case JZ_LCD_TYPE_8BIT_SERIAL: | ||
219 | num = 8; | ||
220 | break; | ||
221 | case JZ_LCD_TYPE_SPECIAL_TFT_1: | ||
222 | case JZ_LCD_TYPE_SPECIAL_TFT_2: | ||
223 | case JZ_LCD_TYPE_SPECIAL_TFT_3: | ||
224 | if (jzfb->pdata->bpp == 18) | ||
225 | num = 18; | ||
226 | else | ||
227 | num = 16; | ||
228 | break; | ||
229 | default: | ||
230 | num = 0; | ||
231 | break; | ||
232 | } | ||
233 | return num; | ||
234 | } | ||
235 | |||
236 | /* Based on CNVT_TOHW macro from skeletonfb.c */ | ||
237 | static inline uint32_t jzfb_convert_color_to_hw(unsigned val, | ||
238 | struct fb_bitfield *bf) | ||
239 | { | ||
240 | return (((val << bf->length) + 0x7FFF - val) >> 16) << bf->offset; | ||
241 | } | ||
242 | |||
243 | static int jzfb_setcolreg(unsigned regno, unsigned red, unsigned green, | ||
244 | unsigned blue, unsigned transp, struct fb_info *fb) | ||
245 | { | ||
246 | uint32_t color; | ||
247 | |||
248 | if (regno >= 16) | ||
249 | return -EINVAL; | ||
250 | |||
251 | color = jzfb_convert_color_to_hw(red, &fb->var.red); | ||
252 | color |= jzfb_convert_color_to_hw(green, &fb->var.green); | ||
253 | color |= jzfb_convert_color_to_hw(blue, &fb->var.blue); | ||
254 | color |= jzfb_convert_color_to_hw(transp, &fb->var.transp); | ||
255 | |||
256 | ((uint32_t *)(fb->pseudo_palette))[regno] = color; | ||
257 | |||
258 | return 0; | ||
259 | } | ||
260 | |||
261 | static int jzfb_get_controller_bpp(struct jzfb *jzfb) | ||
262 | { | ||
263 | switch (jzfb->pdata->bpp) { | ||
264 | case 18: | ||
265 | case 24: | ||
266 | return 32; | ||
267 | case 15: | ||
268 | return 16; | ||
269 | default: | ||
270 | return jzfb->pdata->bpp; | ||
271 | } | ||
272 | } | ||
273 | |||
274 | static struct fb_videomode *jzfb_get_mode(struct jzfb *jzfb, | ||
275 | struct fb_var_screeninfo *var) | ||
276 | { | ||
277 | size_t i; | ||
278 | struct fb_videomode *mode = jzfb->pdata->modes; | ||
279 | |||
280 | for (i = 0; i < jzfb->pdata->num_modes; ++i, ++mode) { | ||
281 | if (mode->xres == var->xres && mode->yres == var->yres) | ||
282 | return mode; | ||
283 | } | ||
284 | |||
285 | return NULL; | ||
286 | } | ||
287 | |||
288 | static int jzfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fb) | ||
289 | { | ||
290 | struct jzfb *jzfb = fb->par; | ||
291 | struct fb_videomode *mode; | ||
292 | |||
293 | if (var->bits_per_pixel != jzfb_get_controller_bpp(jzfb) && | ||
294 | var->bits_per_pixel != jzfb->pdata->bpp) | ||
295 | return -EINVAL; | ||
296 | |||
297 | mode = jzfb_get_mode(jzfb, var); | ||
298 | if (mode == NULL) | ||
299 | return -EINVAL; | ||
300 | |||
301 | fb_videomode_to_var(var, mode); | ||
302 | |||
303 | switch (jzfb->pdata->bpp) { | ||
304 | case 8: | ||
305 | break; | ||
306 | case 15: | ||
307 | var->red.offset = 10; | ||
308 | var->red.length = 5; | ||
309 | var->green.offset = 6; | ||
310 | var->green.length = 5; | ||
311 | var->blue.offset = 0; | ||
312 | var->blue.length = 5; | ||
313 | break; | ||
314 | case 16: | ||
315 | var->red.offset = 11; | ||
316 | var->red.length = 5; | ||
317 | var->green.offset = 5; | ||
318 | var->green.length = 6; | ||
319 | var->blue.offset = 0; | ||
320 | var->blue.length = 5; | ||
321 | break; | ||
322 | case 18: | ||
323 | var->red.offset = 16; | ||
324 | var->red.length = 6; | ||
325 | var->green.offset = 8; | ||
326 | var->green.length = 6; | ||
327 | var->blue.offset = 0; | ||
328 | var->blue.length = 6; | ||
329 | var->bits_per_pixel = 32; | ||
330 | break; | ||
331 | case 32: | ||
332 | case 24: | ||
333 | var->transp.offset = 24; | ||
334 | var->transp.length = 8; | ||
335 | var->red.offset = 16; | ||
336 | var->red.length = 8; | ||
337 | var->green.offset = 8; | ||
338 | var->green.length = 8; | ||
339 | var->blue.offset = 0; | ||
340 | var->blue.length = 8; | ||
341 | var->bits_per_pixel = 32; | ||
342 | break; | ||
343 | default: | ||
344 | break; | ||
345 | } | ||
346 | |||
347 | return 0; | ||
348 | } | ||
349 | |||
350 | static int jzfb_set_par(struct fb_info *info) | ||
351 | { | ||
352 | struct jzfb *jzfb = info->par; | ||
353 | struct jz4740_fb_platform_data *pdata = jzfb->pdata; | ||
354 | struct fb_var_screeninfo *var = &info->var; | ||
355 | struct fb_videomode *mode; | ||
356 | uint16_t hds, vds; | ||
357 | uint16_t hde, vde; | ||
358 | uint16_t ht, vt; | ||
359 | uint32_t ctrl; | ||
360 | uint32_t cfg; | ||
361 | unsigned long rate; | ||
362 | |||
363 | mode = jzfb_get_mode(jzfb, var); | ||
364 | if (mode == NULL) | ||
365 | return -EINVAL; | ||
366 | |||
367 | if (mode == info->mode) | ||
368 | return 0; | ||
369 | |||
370 | info->mode = mode; | ||
371 | |||
372 | hds = mode->hsync_len + mode->left_margin; | ||
373 | hde = hds + mode->xres; | ||
374 | ht = hde + mode->right_margin; | ||
375 | |||
376 | vds = mode->vsync_len + mode->upper_margin; | ||
377 | vde = vds + mode->yres; | ||
378 | vt = vde + mode->lower_margin; | ||
379 | |||
380 | ctrl = JZ_LCD_CTRL_OFUP | JZ_LCD_CTRL_BURST_16; | ||
381 | |||
382 | switch (pdata->bpp) { | ||
383 | case 1: | ||
384 | ctrl |= JZ_LCD_CTRL_BPP_1; | ||
385 | break; | ||
386 | case 2: | ||
387 | ctrl |= JZ_LCD_CTRL_BPP_2; | ||
388 | break; | ||
389 | case 4: | ||
390 | ctrl |= JZ_LCD_CTRL_BPP_4; | ||
391 | break; | ||
392 | case 8: | ||
393 | ctrl |= JZ_LCD_CTRL_BPP_8; | ||
394 | break; | ||
395 | case 15: | ||
396 | ctrl |= JZ_LCD_CTRL_RGB555; /* Falltrough */ | ||
397 | case 16: | ||
398 | ctrl |= JZ_LCD_CTRL_BPP_15_16; | ||
399 | break; | ||
400 | case 18: | ||
401 | case 24: | ||
402 | case 32: | ||
403 | ctrl |= JZ_LCD_CTRL_BPP_18_24; | ||
404 | break; | ||
405 | default: | ||
406 | break; | ||
407 | } | ||
408 | |||
409 | cfg = pdata->lcd_type & 0xf; | ||
410 | |||
411 | if (!(mode->sync & FB_SYNC_HOR_HIGH_ACT)) | ||
412 | cfg |= JZ_LCD_CFG_HSYNC_ACTIVE_LOW; | ||
413 | |||
414 | if (!(mode->sync & FB_SYNC_VERT_HIGH_ACT)) | ||
415 | cfg |= JZ_LCD_CFG_VSYNC_ACTIVE_LOW; | ||
416 | |||
417 | if (pdata->pixclk_falling_edge) | ||
418 | cfg |= JZ_LCD_CFG_PCLK_FALLING_EDGE; | ||
419 | |||
420 | if (pdata->date_enable_active_low) | ||
421 | cfg |= JZ_LCD_CFG_DE_ACTIVE_LOW; | ||
422 | |||
423 | if (pdata->lcd_type == JZ_LCD_TYPE_GENERIC_18_BIT) | ||
424 | cfg |= JZ_LCD_CFG_18_BIT; | ||
425 | |||
426 | if (mode->pixclock) { | ||
427 | rate = PICOS2KHZ(mode->pixclock) * 1000; | ||
428 | mode->refresh = rate / vt / ht; | ||
429 | } else { | ||
430 | if (pdata->lcd_type == JZ_LCD_TYPE_8BIT_SERIAL) | ||
431 | rate = mode->refresh * (vt + 2 * mode->xres) * ht; | ||
432 | else | ||
433 | rate = mode->refresh * vt * ht; | ||
434 | |||
435 | mode->pixclock = KHZ2PICOS(rate / 1000); | ||
436 | } | ||
437 | |||
438 | mutex_lock(&jzfb->lock); | ||
439 | if (!jzfb->is_enabled) | ||
440 | clk_enable(jzfb->ldclk); | ||
441 | else | ||
442 | ctrl |= JZ_LCD_CTRL_ENABLE; | ||
443 | |||
444 | switch (pdata->lcd_type) { | ||
445 | case JZ_LCD_TYPE_SPECIAL_TFT_1: | ||
446 | case JZ_LCD_TYPE_SPECIAL_TFT_2: | ||
447 | case JZ_LCD_TYPE_SPECIAL_TFT_3: | ||
448 | writel(pdata->special_tft_config.spl, jzfb->base + JZ_REG_LCD_SPL); | ||
449 | writel(pdata->special_tft_config.cls, jzfb->base + JZ_REG_LCD_CLS); | ||
450 | writel(pdata->special_tft_config.ps, jzfb->base + JZ_REG_LCD_PS); | ||
451 | writel(pdata->special_tft_config.ps, jzfb->base + JZ_REG_LCD_REV); | ||
452 | break; | ||
453 | default: | ||
454 | cfg |= JZ_LCD_CFG_PS_DISABLE; | ||
455 | cfg |= JZ_LCD_CFG_CLS_DISABLE; | ||
456 | cfg |= JZ_LCD_CFG_SPL_DISABLE; | ||
457 | cfg |= JZ_LCD_CFG_REV_DISABLE; | ||
458 | break; | ||
459 | } | ||
460 | |||
461 | writel(mode->hsync_len, jzfb->base + JZ_REG_LCD_HSYNC); | ||
462 | writel(mode->vsync_len, jzfb->base + JZ_REG_LCD_VSYNC); | ||
463 | |||
464 | writel((ht << 16) | vt, jzfb->base + JZ_REG_LCD_VAT); | ||
465 | |||
466 | writel((hds << 16) | hde, jzfb->base + JZ_REG_LCD_DAH); | ||
467 | writel((vds << 16) | vde, jzfb->base + JZ_REG_LCD_DAV); | ||
468 | |||
469 | writel(cfg, jzfb->base + JZ_REG_LCD_CFG); | ||
470 | |||
471 | writel(ctrl, jzfb->base + JZ_REG_LCD_CTRL); | ||
472 | |||
473 | if (!jzfb->is_enabled) | ||
474 | clk_disable(jzfb->ldclk); | ||
475 | |||
476 | mutex_unlock(&jzfb->lock); | ||
477 | |||
478 | clk_set_rate(jzfb->lpclk, rate); | ||
479 | clk_set_rate(jzfb->ldclk, rate * 3); | ||
480 | |||
481 | return 0; | ||
482 | } | ||
483 | |||
484 | static void jzfb_enable(struct jzfb *jzfb) | ||
485 | { | ||
486 | uint32_t ctrl; | ||
487 | |||
488 | clk_enable(jzfb->ldclk); | ||
489 | |||
490 | jz_gpio_bulk_resume(jz_lcd_ctrl_pins, jzfb_num_ctrl_pins(jzfb)); | ||
491 | jz_gpio_bulk_resume(jz_lcd_data_pins, jzfb_num_data_pins(jzfb)); | ||
492 | |||
493 | writel(0, jzfb->base + JZ_REG_LCD_STATE); | ||
494 | |||
495 | writel(jzfb->framedesc->next, jzfb->base + JZ_REG_LCD_DA0); | ||
496 | |||
497 | ctrl = readl(jzfb->base + JZ_REG_LCD_CTRL); | ||
498 | ctrl |= JZ_LCD_CTRL_ENABLE; | ||
499 | ctrl &= ~JZ_LCD_CTRL_DISABLE; | ||
500 | writel(ctrl, jzfb->base + JZ_REG_LCD_CTRL); | ||
501 | } | ||
502 | |||
503 | static void jzfb_disable(struct jzfb *jzfb) | ||
504 | { | ||
505 | uint32_t ctrl; | ||
506 | |||
507 | ctrl = readl(jzfb->base + JZ_REG_LCD_CTRL); | ||
508 | ctrl |= JZ_LCD_CTRL_DISABLE; | ||
509 | writel(ctrl, jzfb->base + JZ_REG_LCD_CTRL); | ||
510 | do { | ||
511 | ctrl = readl(jzfb->base + JZ_REG_LCD_STATE); | ||
512 | } while (!(ctrl & JZ_LCD_STATE_DISABLED)); | ||
513 | |||
514 | jz_gpio_bulk_suspend(jz_lcd_ctrl_pins, jzfb_num_ctrl_pins(jzfb)); | ||
515 | jz_gpio_bulk_suspend(jz_lcd_data_pins, jzfb_num_data_pins(jzfb)); | ||
516 | |||
517 | clk_disable(jzfb->ldclk); | ||
518 | } | ||
519 | |||
520 | static int jzfb_blank(int blank_mode, struct fb_info *info) | ||
521 | { | ||
522 | struct jzfb *jzfb = info->par; | ||
523 | |||
524 | switch (blank_mode) { | ||
525 | case FB_BLANK_UNBLANK: | ||
526 | mutex_lock(&jzfb->lock); | ||
527 | if (jzfb->is_enabled) { | ||
528 | mutex_unlock(&jzfb->lock); | ||
529 | return 0; | ||
530 | } | ||
531 | |||
532 | jzfb_enable(jzfb); | ||
533 | jzfb->is_enabled = 1; | ||
534 | |||
535 | mutex_unlock(&jzfb->lock); | ||
536 | break; | ||
537 | default: | ||
538 | mutex_lock(&jzfb->lock); | ||
539 | if (!jzfb->is_enabled) { | ||
540 | mutex_unlock(&jzfb->lock); | ||
541 | return 0; | ||
542 | } | ||
543 | |||
544 | jzfb_disable(jzfb); | ||
545 | jzfb->is_enabled = 0; | ||
546 | |||
547 | mutex_unlock(&jzfb->lock); | ||
548 | break; | ||
549 | } | ||
550 | |||
551 | return 0; | ||
552 | } | ||
553 | |||
554 | static int jzfb_alloc_devmem(struct jzfb *jzfb) | ||
555 | { | ||
556 | int max_videosize = 0; | ||
557 | struct fb_videomode *mode = jzfb->pdata->modes; | ||
558 | void *page; | ||
559 | int i; | ||
560 | |||
561 | for (i = 0; i < jzfb->pdata->num_modes; ++mode, ++i) { | ||
562 | if (max_videosize < mode->xres * mode->yres) | ||
563 | max_videosize = mode->xres * mode->yres; | ||
564 | } | ||
565 | |||
566 | max_videosize *= jzfb_get_controller_bpp(jzfb) >> 3; | ||
567 | |||
568 | jzfb->framedesc = dma_alloc_coherent(&jzfb->pdev->dev, | ||
569 | sizeof(*jzfb->framedesc), | ||
570 | &jzfb->framedesc_phys, GFP_KERNEL); | ||
571 | |||
572 | if (!jzfb->framedesc) | ||
573 | return -ENOMEM; | ||
574 | |||
575 | jzfb->vidmem_size = PAGE_ALIGN(max_videosize); | ||
576 | jzfb->vidmem = dma_alloc_coherent(&jzfb->pdev->dev, | ||
577 | jzfb->vidmem_size, | ||
578 | &jzfb->vidmem_phys, GFP_KERNEL); | ||
579 | |||
580 | if (!jzfb->vidmem) | ||
581 | goto err_free_framedesc; | ||
582 | |||
583 | for (page = jzfb->vidmem; | ||
584 | page < jzfb->vidmem + PAGE_ALIGN(jzfb->vidmem_size); | ||
585 | page += PAGE_SIZE) { | ||
586 | SetPageReserved(virt_to_page(page)); | ||
587 | } | ||
588 | |||
589 | jzfb->framedesc->next = jzfb->framedesc_phys; | ||
590 | jzfb->framedesc->addr = jzfb->vidmem_phys; | ||
591 | jzfb->framedesc->id = 0xdeafbead; | ||
592 | jzfb->framedesc->cmd = 0; | ||
593 | jzfb->framedesc->cmd |= max_videosize / 4; | ||
594 | |||
595 | return 0; | ||
596 | |||
597 | err_free_framedesc: | ||
598 | dma_free_coherent(&jzfb->pdev->dev, sizeof(*jzfb->framedesc), | ||
599 | jzfb->framedesc, jzfb->framedesc_phys); | ||
600 | return -ENOMEM; | ||
601 | } | ||
602 | |||
603 | static void jzfb_free_devmem(struct jzfb *jzfb) | ||
604 | { | ||
605 | dma_free_coherent(&jzfb->pdev->dev, jzfb->vidmem_size, | ||
606 | jzfb->vidmem, jzfb->vidmem_phys); | ||
607 | dma_free_coherent(&jzfb->pdev->dev, sizeof(*jzfb->framedesc), | ||
608 | jzfb->framedesc, jzfb->framedesc_phys); | ||
609 | } | ||
610 | |||
611 | static struct fb_ops jzfb_ops = { | ||
612 | .owner = THIS_MODULE, | ||
613 | .fb_check_var = jzfb_check_var, | ||
614 | .fb_set_par = jzfb_set_par, | ||
615 | .fb_blank = jzfb_blank, | ||
616 | .fb_fillrect = sys_fillrect, | ||
617 | .fb_copyarea = sys_copyarea, | ||
618 | .fb_imageblit = sys_imageblit, | ||
619 | .fb_setcolreg = jzfb_setcolreg, | ||
620 | }; | ||
621 | |||
622 | static int __devinit jzfb_probe(struct platform_device *pdev) | ||
623 | { | ||
624 | int ret; | ||
625 | struct jzfb *jzfb; | ||
626 | struct fb_info *fb; | ||
627 | struct jz4740_fb_platform_data *pdata = pdev->dev.platform_data; | ||
628 | struct resource *mem; | ||
629 | |||
630 | if (!pdata) { | ||
631 | dev_err(&pdev->dev, "Missing platform data\n"); | ||
632 | return -ENXIO; | ||
633 | } | ||
634 | |||
635 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
636 | if (!mem) { | ||
637 | dev_err(&pdev->dev, "Failed to get register memory resource\n"); | ||
638 | return -ENXIO; | ||
639 | } | ||
640 | |||
641 | mem = request_mem_region(mem->start, resource_size(mem), pdev->name); | ||
642 | if (!mem) { | ||
643 | dev_err(&pdev->dev, "Failed to request register memory region\n"); | ||
644 | return -EBUSY; | ||
645 | } | ||
646 | |||
647 | fb = framebuffer_alloc(sizeof(struct jzfb), &pdev->dev); | ||
648 | if (!fb) { | ||
649 | dev_err(&pdev->dev, "Failed to allocate framebuffer device\n"); | ||
650 | ret = -ENOMEM; | ||
651 | goto err_release_mem_region; | ||
652 | } | ||
653 | |||
654 | fb->fbops = &jzfb_ops; | ||
655 | fb->flags = FBINFO_DEFAULT; | ||
656 | |||
657 | jzfb = fb->par; | ||
658 | jzfb->pdev = pdev; | ||
659 | jzfb->pdata = pdata; | ||
660 | jzfb->mem = mem; | ||
661 | |||
662 | jzfb->ldclk = clk_get(&pdev->dev, "lcd"); | ||
663 | if (IS_ERR(jzfb->ldclk)) { | ||
664 | ret = PTR_ERR(jzfb->ldclk); | ||
665 | dev_err(&pdev->dev, "Failed to get lcd clock: %d\n", ret); | ||
666 | goto err_framebuffer_release; | ||
667 | } | ||
668 | |||
669 | jzfb->lpclk = clk_get(&pdev->dev, "lcd_pclk"); | ||
670 | if (IS_ERR(jzfb->lpclk)) { | ||
671 | ret = PTR_ERR(jzfb->lpclk); | ||
672 | dev_err(&pdev->dev, "Failed to get lcd pixel clock: %d\n", ret); | ||
673 | goto err_put_ldclk; | ||
674 | } | ||
675 | |||
676 | jzfb->base = ioremap(mem->start, resource_size(mem)); | ||
677 | if (!jzfb->base) { | ||
678 | dev_err(&pdev->dev, "Failed to ioremap register memory region\n"); | ||
679 | ret = -EBUSY; | ||
680 | goto err_put_lpclk; | ||
681 | } | ||
682 | |||
683 | platform_set_drvdata(pdev, jzfb); | ||
684 | |||
685 | mutex_init(&jzfb->lock); | ||
686 | |||
687 | fb_videomode_to_modelist(pdata->modes, pdata->num_modes, | ||
688 | &fb->modelist); | ||
689 | fb_videomode_to_var(&fb->var, pdata->modes); | ||
690 | fb->var.bits_per_pixel = pdata->bpp; | ||
691 | jzfb_check_var(&fb->var, fb); | ||
692 | |||
693 | ret = jzfb_alloc_devmem(jzfb); | ||
694 | if (ret) { | ||
695 | dev_err(&pdev->dev, "Failed to allocate video memory\n"); | ||
696 | goto err_iounmap; | ||
697 | } | ||
698 | |||
699 | fb->fix = jzfb_fix; | ||
700 | fb->fix.line_length = fb->var.bits_per_pixel * fb->var.xres / 8; | ||
701 | fb->fix.mmio_start = mem->start; | ||
702 | fb->fix.mmio_len = resource_size(mem); | ||
703 | fb->fix.smem_start = jzfb->vidmem_phys; | ||
704 | fb->fix.smem_len = fb->fix.line_length * fb->var.yres; | ||
705 | fb->screen_base = jzfb->vidmem; | ||
706 | fb->pseudo_palette = jzfb->pseudo_palette; | ||
707 | |||
708 | fb_alloc_cmap(&fb->cmap, 256, 0); | ||
709 | |||
710 | clk_enable(jzfb->ldclk); | ||
711 | jzfb->is_enabled = 1; | ||
712 | |||
713 | writel(jzfb->framedesc->next, jzfb->base + JZ_REG_LCD_DA0); | ||
714 | |||
715 | fb->mode = NULL; | ||
716 | jzfb_set_par(fb); | ||
717 | |||
718 | jz_gpio_bulk_request(jz_lcd_ctrl_pins, jzfb_num_ctrl_pins(jzfb)); | ||
719 | jz_gpio_bulk_request(jz_lcd_data_pins, jzfb_num_data_pins(jzfb)); | ||
720 | |||
721 | ret = register_framebuffer(fb); | ||
722 | if (ret) { | ||
723 | dev_err(&pdev->dev, "Failed to register framebuffer: %d\n", ret); | ||
724 | goto err_free_devmem; | ||
725 | } | ||
726 | |||
727 | jzfb->fb = fb; | ||
728 | |||
729 | return 0; | ||
730 | |||
731 | err_free_devmem: | ||
732 | jz_gpio_bulk_free(jz_lcd_ctrl_pins, jzfb_num_ctrl_pins(jzfb)); | ||
733 | jz_gpio_bulk_free(jz_lcd_data_pins, jzfb_num_data_pins(jzfb)); | ||
734 | |||
735 | fb_dealloc_cmap(&fb->cmap); | ||
736 | jzfb_free_devmem(jzfb); | ||
737 | err_iounmap: | ||
738 | iounmap(jzfb->base); | ||
739 | err_put_lpclk: | ||
740 | clk_put(jzfb->lpclk); | ||
741 | err_put_ldclk: | ||
742 | clk_put(jzfb->ldclk); | ||
743 | err_framebuffer_release: | ||
744 | framebuffer_release(fb); | ||
745 | err_release_mem_region: | ||
746 | release_mem_region(mem->start, resource_size(mem)); | ||
747 | return ret; | ||
748 | } | ||
749 | |||
750 | static int __devexit jzfb_remove(struct platform_device *pdev) | ||
751 | { | ||
752 | struct jzfb *jzfb = platform_get_drvdata(pdev); | ||
753 | |||
754 | jzfb_blank(FB_BLANK_POWERDOWN, jzfb->fb); | ||
755 | |||
756 | jz_gpio_bulk_free(jz_lcd_ctrl_pins, jzfb_num_ctrl_pins(jzfb)); | ||
757 | jz_gpio_bulk_free(jz_lcd_data_pins, jzfb_num_data_pins(jzfb)); | ||
758 | |||
759 | iounmap(jzfb->base); | ||
760 | release_mem_region(jzfb->mem->start, resource_size(jzfb->mem)); | ||
761 | |||
762 | fb_dealloc_cmap(&jzfb->fb->cmap); | ||
763 | jzfb_free_devmem(jzfb); | ||
764 | |||
765 | platform_set_drvdata(pdev, NULL); | ||
766 | |||
767 | clk_put(jzfb->lpclk); | ||
768 | clk_put(jzfb->ldclk); | ||
769 | |||
770 | framebuffer_release(jzfb->fb); | ||
771 | |||
772 | return 0; | ||
773 | } | ||
774 | |||
775 | #ifdef CONFIG_PM | ||
776 | |||
777 | static int jzfb_suspend(struct device *dev) | ||
778 | { | ||
779 | struct jzfb *jzfb = dev_get_drvdata(dev); | ||
780 | |||
781 | acquire_console_sem(); | ||
782 | fb_set_suspend(jzfb->fb, 1); | ||
783 | release_console_sem(); | ||
784 | |||
785 | mutex_lock(&jzfb->lock); | ||
786 | if (jzfb->is_enabled) | ||
787 | jzfb_disable(jzfb); | ||
788 | mutex_unlock(&jzfb->lock); | ||
789 | |||
790 | return 0; | ||
791 | } | ||
792 | |||
793 | static int jzfb_resume(struct device *dev) | ||
794 | { | ||
795 | struct jzfb *jzfb = dev_get_drvdata(dev); | ||
796 | clk_enable(jzfb->ldclk); | ||
797 | |||
798 | mutex_lock(&jzfb->lock); | ||
799 | if (jzfb->is_enabled) | ||
800 | jzfb_enable(jzfb); | ||
801 | mutex_unlock(&jzfb->lock); | ||
802 | |||
803 | acquire_console_sem(); | ||
804 | fb_set_suspend(jzfb->fb, 0); | ||
805 | release_console_sem(); | ||
806 | |||
807 | return 0; | ||
808 | } | ||
809 | |||
810 | static const struct dev_pm_ops jzfb_pm_ops = { | ||
811 | .suspend = jzfb_suspend, | ||
812 | .resume = jzfb_resume, | ||
813 | .poweroff = jzfb_suspend, | ||
814 | .restore = jzfb_resume, | ||
815 | }; | ||
816 | |||
817 | #define JZFB_PM_OPS (&jzfb_pm_ops) | ||
818 | |||
819 | #else | ||
820 | #define JZFB_PM_OPS NULL | ||
821 | #endif | ||
822 | |||
823 | static struct platform_driver jzfb_driver = { | ||
824 | .probe = jzfb_probe, | ||
825 | .remove = __devexit_p(jzfb_remove), | ||
826 | .driver = { | ||
827 | .name = "jz4740-fb", | ||
828 | .pm = JZFB_PM_OPS, | ||
829 | }, | ||
830 | }; | ||
831 | |||
832 | static int __init jzfb_init(void) | ||
833 | { | ||
834 | return platform_driver_register(&jzfb_driver); | ||
835 | } | ||
836 | module_init(jzfb_init); | ||
837 | |||
838 | static void __exit jzfb_exit(void) | ||
839 | { | ||
840 | platform_driver_unregister(&jzfb_driver); | ||
841 | } | ||
842 | module_exit(jzfb_exit); | ||
843 | |||
844 | MODULE_LICENSE("GPL"); | ||
845 | MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); | ||
846 | MODULE_DESCRIPTION("JZ4740 SoC LCD framebuffer driver"); | ||
847 | MODULE_ALIAS("platform:jz4740-fb"); | ||
diff --git a/drivers/video/tdfxfb.c b/drivers/video/tdfxfb.c index 980548390048..3ee5e63cfa4f 100644 --- a/drivers/video/tdfxfb.c +++ b/drivers/video/tdfxfb.c | |||
@@ -1571,8 +1571,8 @@ out_err_iobase: | |||
1571 | if (default_par->mtrr_handle >= 0) | 1571 | if (default_par->mtrr_handle >= 0) |
1572 | mtrr_del(default_par->mtrr_handle, info->fix.smem_start, | 1572 | mtrr_del(default_par->mtrr_handle, info->fix.smem_start, |
1573 | info->fix.smem_len); | 1573 | info->fix.smem_len); |
1574 | release_mem_region(pci_resource_start(pdev, 2), | 1574 | release_region(pci_resource_start(pdev, 2), |
1575 | pci_resource_len(pdev, 2)); | 1575 | pci_resource_len(pdev, 2)); |
1576 | out_err_screenbase: | 1576 | out_err_screenbase: |
1577 | if (info->screen_base) | 1577 | if (info->screen_base) |
1578 | iounmap(info->screen_base); | 1578 | iounmap(info->screen_base); |
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index afcfacc9bbe2..b04b18468932 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig | |||
@@ -875,6 +875,24 @@ config TXX9_WDT | |||
875 | help | 875 | help |
876 | Hardware driver for the built-in watchdog timer on TXx9 MIPS SoCs. | 876 | Hardware driver for the built-in watchdog timer on TXx9 MIPS SoCs. |
877 | 877 | ||
878 | config OCTEON_WDT | ||
879 | tristate "Cavium OCTEON SOC family Watchdog Timer" | ||
880 | depends on CPU_CAVIUM_OCTEON | ||
881 | default y | ||
882 | select EXPORT_UASM if OCTEON_WDT = m | ||
883 | help | ||
884 | Hardware driver for OCTEON's on chip watchdog timer. | ||
885 | Enables the watchdog for all cores running Linux. It | ||
886 | installs a NMI handler and pokes the watchdog based on an | ||
887 | interrupt. On first expiration of the watchdog, the | ||
888 | interrupt handler pokes it. The second expiration causes an | ||
889 | NMI that prints a message. The third expiration causes a | ||
890 | global soft reset. | ||
891 | |||
892 | When userspace has /dev/watchdog open, no poking is done | ||
893 | from the first interrupt, it is then only poked when the | ||
894 | device is written. | ||
895 | |||
878 | # PARISC Architecture | 896 | # PARISC Architecture |
879 | 897 | ||
880 | # POWERPC Architecture | 898 | # POWERPC Architecture |
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 72f3e2073f8e..e30289a5e367 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile | |||
@@ -114,6 +114,8 @@ obj-$(CONFIG_PNX833X_WDT) += pnx833x_wdt.o | |||
114 | obj-$(CONFIG_SIBYTE_WDOG) += sb_wdog.o | 114 | obj-$(CONFIG_SIBYTE_WDOG) += sb_wdog.o |
115 | obj-$(CONFIG_AR7_WDT) += ar7_wdt.o | 115 | obj-$(CONFIG_AR7_WDT) += ar7_wdt.o |
116 | obj-$(CONFIG_TXX9_WDT) += txx9wdt.o | 116 | obj-$(CONFIG_TXX9_WDT) += txx9wdt.o |
117 | obj-$(CONFIG_OCTEON_WDT) += octeon-wdt.o | ||
118 | octeon-wdt-y := octeon-wdt-main.o octeon-wdt-nmi.o | ||
117 | 119 | ||
118 | # PARISC Architecture | 120 | # PARISC Architecture |
119 | 121 | ||
diff --git a/drivers/watchdog/octeon-wdt-main.c b/drivers/watchdog/octeon-wdt-main.c new file mode 100644 index 000000000000..2a410170eca6 --- /dev/null +++ b/drivers/watchdog/octeon-wdt-main.c | |||
@@ -0,0 +1,745 @@ | |||
1 | /* | ||
2 | * Octeon Watchdog driver | ||
3 | * | ||
4 | * Copyright (C) 2007, 2008, 2009, 2010 Cavium Networks | ||
5 | * | ||
6 | * Some parts derived from wdt.c | ||
7 | * | ||
8 | * (c) Copyright 1996-1997 Alan Cox <alan@lxorguk.ukuu.org.uk>, | ||
9 | * All Rights Reserved. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or | ||
12 | * modify it under the terms of the GNU General Public License | ||
13 | * as published by the Free Software Foundation; either version | ||
14 | * 2 of the License, or (at your option) any later version. | ||
15 | * | ||
16 | * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide | ||
17 | * warranty for any of this software. This material is provided | ||
18 | * "AS-IS" and at no charge. | ||
19 | * | ||
20 | * (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk> | ||
21 | * | ||
22 | * This file is subject to the terms and conditions of the GNU General Public | ||
23 | * License. See the file "COPYING" in the main directory of this archive | ||
24 | * for more details. | ||
25 | * | ||
26 | * | ||
27 | * The OCTEON watchdog has a maximum timeout of 2^32 * io_clock. | ||
28 | * For most systems this is less than 10 seconds, so to allow for | ||
29 | * software to request longer watchdog heartbeats, we maintain software | ||
30 | * counters to count multiples of the base rate. If the system locks | ||
31 | * up in such a manner that we can not run the software counters, the | ||
32 | * only result is a watchdog reset sooner than was requested. But | ||
33 | * that is OK, because in this case userspace would likely not be able | ||
34 | * to do anything anyhow. | ||
35 | * | ||
36 | * The hardware watchdog interval we call the period. The OCTEON | ||
37 | * watchdog goes through several stages, after the first period an | ||
38 | * irq is asserted, then if it is not reset, after the next period NMI | ||
39 | * is asserted, then after an additional period a chip wide soft reset. | ||
40 | * So for the software counters, we reset watchdog after each period | ||
41 | * and decrement the counter. But for the last two periods we need to | ||
42 | * let the watchdog progress to the NMI stage so we disable the irq | ||
43 | * and let it proceed. Once in the NMI, we print the register state | ||
44 | * to the serial port and then wait for the reset. | ||
45 | * | ||
46 | * A watchdog is maintained for each CPU in the system, that way if | ||
47 | * one CPU suffers a lockup, we also get a register dump and reset. | ||
48 | * The userspace ping resets the watchdog on all CPUs. | ||
49 | * | ||
50 | * Before userspace opens the watchdog device, we still run the | ||
51 | * watchdogs to catch any lockups that may be kernel related. | ||
52 | * | ||
53 | */ | ||
54 | |||
55 | #include <linux/miscdevice.h> | ||
56 | #include <linux/interrupt.h> | ||
57 | #include <linux/watchdog.h> | ||
58 | #include <linux/cpumask.h> | ||
59 | #include <linux/bitops.h> | ||
60 | #include <linux/kernel.h> | ||
61 | #include <linux/module.h> | ||
62 | #include <linux/string.h> | ||
63 | #include <linux/delay.h> | ||
64 | #include <linux/cpu.h> | ||
65 | #include <linux/smp.h> | ||
66 | #include <linux/fs.h> | ||
67 | |||
68 | #include <asm/mipsregs.h> | ||
69 | #include <asm/uasm.h> | ||
70 | |||
71 | #include <asm/octeon/octeon.h> | ||
72 | |||
73 | /* The count needed to achieve timeout_sec. */ | ||
74 | static unsigned int timeout_cnt; | ||
75 | |||
76 | /* The maximum period supported. */ | ||
77 | static unsigned int max_timeout_sec; | ||
78 | |||
79 | /* The current period. */ | ||
80 | static unsigned int timeout_sec; | ||
81 | |||
82 | /* Set to non-zero when userspace countdown mode active */ | ||
83 | static int do_coundown; | ||
84 | static unsigned int countdown_reset; | ||
85 | static unsigned int per_cpu_countdown[NR_CPUS]; | ||
86 | |||
87 | static cpumask_t irq_enabled_cpus; | ||
88 | |||
89 | #define WD_TIMO 60 /* Default heartbeat = 60 seconds */ | ||
90 | |||
91 | static int heartbeat = WD_TIMO; | ||
92 | module_param(heartbeat, int, S_IRUGO); | ||
93 | MODULE_PARM_DESC(heartbeat, | ||
94 | "Watchdog heartbeat in seconds. (0 < heartbeat, default=" | ||
95 | __MODULE_STRING(WD_TIMO) ")"); | ||
96 | |||
97 | static int nowayout = WATCHDOG_NOWAYOUT; | ||
98 | module_param(nowayout, int, S_IRUGO); | ||
99 | MODULE_PARM_DESC(nowayout, | ||
100 | "Watchdog cannot be stopped once started (default=" | ||
101 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | ||
102 | |||
103 | static unsigned long octeon_wdt_is_open; | ||
104 | static char expect_close; | ||
105 | |||
106 | static u32 __initdata nmi_stage1_insns[64]; | ||
107 | /* We need one branch and therefore one relocation per target label. */ | ||
108 | static struct uasm_label __initdata labels[5]; | ||
109 | static struct uasm_reloc __initdata relocs[5]; | ||
110 | |||
111 | enum lable_id { | ||
112 | label_enter_bootloader = 1 | ||
113 | }; | ||
114 | |||
115 | /* Some CP0 registers */ | ||
116 | #define K0 26 | ||
117 | #define C0_CVMMEMCTL 11, 7 | ||
118 | #define C0_STATUS 12, 0 | ||
119 | #define C0_EBASE 15, 1 | ||
120 | #define C0_DESAVE 31, 0 | ||
121 | |||
122 | void octeon_wdt_nmi_stage2(void); | ||
123 | |||
124 | static void __init octeon_wdt_build_stage1(void) | ||
125 | { | ||
126 | int i; | ||
127 | int len; | ||
128 | u32 *p = nmi_stage1_insns; | ||
129 | #ifdef CONFIG_HOTPLUG_CPU | ||
130 | struct uasm_label *l = labels; | ||
131 | struct uasm_reloc *r = relocs; | ||
132 | #endif | ||
133 | |||
134 | /* | ||
135 | * For the next few instructions running the debugger may | ||
136 | * cause corruption of k0 in the saved registers. Since we're | ||
137 | * about to crash, nobody probably cares. | ||
138 | * | ||
139 | * Save K0 into the debug scratch register | ||
140 | */ | ||
141 | uasm_i_dmtc0(&p, K0, C0_DESAVE); | ||
142 | |||
143 | uasm_i_mfc0(&p, K0, C0_STATUS); | ||
144 | #ifdef CONFIG_HOTPLUG_CPU | ||
145 | uasm_il_bbit0(&p, &r, K0, ilog2(ST0_NMI), label_enter_bootloader); | ||
146 | #endif | ||
147 | /* Force 64-bit addressing enabled */ | ||
148 | uasm_i_ori(&p, K0, K0, ST0_UX | ST0_SX | ST0_KX); | ||
149 | uasm_i_mtc0(&p, K0, C0_STATUS); | ||
150 | |||
151 | #ifdef CONFIG_HOTPLUG_CPU | ||
152 | uasm_i_mfc0(&p, K0, C0_EBASE); | ||
153 | /* Coreid number in K0 */ | ||
154 | uasm_i_andi(&p, K0, K0, 0xf); | ||
155 | /* 8 * coreid in bits 16-31 */ | ||
156 | uasm_i_dsll_safe(&p, K0, K0, 3 + 16); | ||
157 | uasm_i_ori(&p, K0, K0, 0x8001); | ||
158 | uasm_i_dsll_safe(&p, K0, K0, 16); | ||
159 | uasm_i_ori(&p, K0, K0, 0x0700); | ||
160 | uasm_i_drotr_safe(&p, K0, K0, 32); | ||
161 | /* | ||
162 | * Should result in: 0x8001,0700,0000,8*coreid which is | ||
163 | * CVMX_CIU_WDOGX(coreid) - 0x0500 | ||
164 | * | ||
165 | * Now ld K0, CVMX_CIU_WDOGX(coreid) | ||
166 | */ | ||
167 | uasm_i_ld(&p, K0, 0x500, K0); | ||
168 | /* | ||
169 | * If bit one set handle the NMI as a watchdog event. | ||
170 | * otherwise transfer control to bootloader. | ||
171 | */ | ||
172 | uasm_il_bbit0(&p, &r, K0, 1, label_enter_bootloader); | ||
173 | uasm_i_nop(&p); | ||
174 | #endif | ||
175 | |||
176 | /* Clear Dcache so cvmseg works right. */ | ||
177 | uasm_i_cache(&p, 1, 0, 0); | ||
178 | |||
179 | /* Use K0 to do a read/modify/write of CVMMEMCTL */ | ||
180 | uasm_i_dmfc0(&p, K0, C0_CVMMEMCTL); | ||
181 | /* Clear out the size of CVMSEG */ | ||
182 | uasm_i_dins(&p, K0, 0, 0, 6); | ||
183 | /* Set CVMSEG to its largest value */ | ||
184 | uasm_i_ori(&p, K0, K0, 0x1c0 | 54); | ||
185 | /* Store the CVMMEMCTL value */ | ||
186 | uasm_i_dmtc0(&p, K0, C0_CVMMEMCTL); | ||
187 | |||
188 | /* Load the address of the second stage handler */ | ||
189 | UASM_i_LA(&p, K0, (long)octeon_wdt_nmi_stage2); | ||
190 | uasm_i_jr(&p, K0); | ||
191 | uasm_i_dmfc0(&p, K0, C0_DESAVE); | ||
192 | |||
193 | #ifdef CONFIG_HOTPLUG_CPU | ||
194 | uasm_build_label(&l, p, label_enter_bootloader); | ||
195 | /* Jump to the bootloader and restore K0 */ | ||
196 | UASM_i_LA(&p, K0, (long)octeon_bootloader_entry_addr); | ||
197 | uasm_i_jr(&p, K0); | ||
198 | uasm_i_dmfc0(&p, K0, C0_DESAVE); | ||
199 | #endif | ||
200 | uasm_resolve_relocs(relocs, labels); | ||
201 | |||
202 | len = (int)(p - nmi_stage1_insns); | ||
203 | pr_debug("Synthesized NMI stage 1 handler (%d instructions).\n", len); | ||
204 | |||
205 | pr_debug("\t.set push\n"); | ||
206 | pr_debug("\t.set noreorder\n"); | ||
207 | for (i = 0; i < len; i++) | ||
208 | pr_debug("\t.word 0x%08x\n", nmi_stage1_insns[i]); | ||
209 | pr_debug("\t.set pop\n"); | ||
210 | |||
211 | if (len > 32) | ||
212 | panic("NMI stage 1 handler exceeds 32 instructions, was %d\n", len); | ||
213 | } | ||
214 | |||
215 | static int cpu2core(int cpu) | ||
216 | { | ||
217 | #ifdef CONFIG_SMP | ||
218 | return cpu_logical_map(cpu); | ||
219 | #else | ||
220 | return cvmx_get_core_num(); | ||
221 | #endif | ||
222 | } | ||
223 | |||
224 | static int core2cpu(int coreid) | ||
225 | { | ||
226 | #ifdef CONFIG_SMP | ||
227 | return cpu_number_map(coreid); | ||
228 | #else | ||
229 | return 0; | ||
230 | #endif | ||
231 | } | ||
232 | |||
233 | /** | ||
234 | * Poke the watchdog when an interrupt is received | ||
235 | * | ||
236 | * @cpl: | ||
237 | * @dev_id: | ||
238 | * | ||
239 | * Returns | ||
240 | */ | ||
241 | static irqreturn_t octeon_wdt_poke_irq(int cpl, void *dev_id) | ||
242 | { | ||
243 | unsigned int core = cvmx_get_core_num(); | ||
244 | int cpu = core2cpu(core); | ||
245 | |||
246 | if (do_coundown) { | ||
247 | if (per_cpu_countdown[cpu] > 0) { | ||
248 | /* We're alive, poke the watchdog */ | ||
249 | cvmx_write_csr(CVMX_CIU_PP_POKEX(core), 1); | ||
250 | per_cpu_countdown[cpu]--; | ||
251 | } else { | ||
252 | /* Bad news, you are about to reboot. */ | ||
253 | disable_irq_nosync(cpl); | ||
254 | cpumask_clear_cpu(cpu, &irq_enabled_cpus); | ||
255 | } | ||
256 | } else { | ||
257 | /* Not open, just ping away... */ | ||
258 | cvmx_write_csr(CVMX_CIU_PP_POKEX(core), 1); | ||
259 | } | ||
260 | return IRQ_HANDLED; | ||
261 | } | ||
262 | |||
263 | /* From setup.c */ | ||
264 | extern int prom_putchar(char c); | ||
265 | |||
266 | /** | ||
267 | * Write a string to the uart | ||
268 | * | ||
269 | * @str: String to write | ||
270 | */ | ||
271 | static void octeon_wdt_write_string(const char *str) | ||
272 | { | ||
273 | /* Just loop writing one byte at a time */ | ||
274 | while (*str) | ||
275 | prom_putchar(*str++); | ||
276 | } | ||
277 | |||
278 | /** | ||
279 | * Write a hex number out of the uart | ||
280 | * | ||
281 | * @value: Number to display | ||
282 | * @digits: Number of digits to print (1 to 16) | ||
283 | */ | ||
284 | static void octeon_wdt_write_hex(u64 value, int digits) | ||
285 | { | ||
286 | int d; | ||
287 | int v; | ||
288 | for (d = 0; d < digits; d++) { | ||
289 | v = (value >> ((digits - d - 1) * 4)) & 0xf; | ||
290 | if (v >= 10) | ||
291 | prom_putchar('a' + v - 10); | ||
292 | else | ||
293 | prom_putchar('0' + v); | ||
294 | } | ||
295 | } | ||
296 | |||
297 | const char *reg_name[] = { | ||
298 | "$0", "at", "v0", "v1", "a0", "a1", "a2", "a3", | ||
299 | "a4", "a5", "a6", "a7", "t0", "t1", "t2", "t3", | ||
300 | "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", | ||
301 | "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra" | ||
302 | }; | ||
303 | |||
304 | /** | ||
305 | * NMI stage 3 handler. NMIs are handled in the following manner: | ||
306 | * 1) The first NMI handler enables CVMSEG and transfers from | ||
307 | * the bootbus region into normal memory. It is careful to not | ||
308 | * destroy any registers. | ||
309 | * 2) The second stage handler uses CVMSEG to save the registers | ||
310 | * and create a stack for C code. It then calls the third level | ||
311 | * handler with one argument, a pointer to the register values. | ||
312 | * 3) The third, and final, level handler is the following C | ||
313 | * function that prints out some useful infomration. | ||
314 | * | ||
315 | * @reg: Pointer to register state before the NMI | ||
316 | */ | ||
317 | void octeon_wdt_nmi_stage3(u64 reg[32]) | ||
318 | { | ||
319 | u64 i; | ||
320 | |||
321 | unsigned int coreid = cvmx_get_core_num(); | ||
322 | /* | ||
323 | * Save status and cause early to get them before any changes | ||
324 | * might happen. | ||
325 | */ | ||
326 | u64 cp0_cause = read_c0_cause(); | ||
327 | u64 cp0_status = read_c0_status(); | ||
328 | u64 cp0_error_epc = read_c0_errorepc(); | ||
329 | u64 cp0_epc = read_c0_epc(); | ||
330 | |||
331 | /* Delay so output from all cores output is not jumbled together. */ | ||
332 | __delay(100000000ull * coreid); | ||
333 | |||
334 | octeon_wdt_write_string("\r\n*** NMI Watchdog interrupt on Core 0x"); | ||
335 | octeon_wdt_write_hex(coreid, 1); | ||
336 | octeon_wdt_write_string(" ***\r\n"); | ||
337 | for (i = 0; i < 32; i++) { | ||
338 | octeon_wdt_write_string("\t"); | ||
339 | octeon_wdt_write_string(reg_name[i]); | ||
340 | octeon_wdt_write_string("\t0x"); | ||
341 | octeon_wdt_write_hex(reg[i], 16); | ||
342 | if (i & 1) | ||
343 | octeon_wdt_write_string("\r\n"); | ||
344 | } | ||
345 | octeon_wdt_write_string("\terr_epc\t0x"); | ||
346 | octeon_wdt_write_hex(cp0_error_epc, 16); | ||
347 | |||
348 | octeon_wdt_write_string("\tepc\t0x"); | ||
349 | octeon_wdt_write_hex(cp0_epc, 16); | ||
350 | octeon_wdt_write_string("\r\n"); | ||
351 | |||
352 | octeon_wdt_write_string("\tstatus\t0x"); | ||
353 | octeon_wdt_write_hex(cp0_status, 16); | ||
354 | octeon_wdt_write_string("\tcause\t0x"); | ||
355 | octeon_wdt_write_hex(cp0_cause, 16); | ||
356 | octeon_wdt_write_string("\r\n"); | ||
357 | |||
358 | octeon_wdt_write_string("\tsum0\t0x"); | ||
359 | octeon_wdt_write_hex(cvmx_read_csr(CVMX_CIU_INTX_SUM0(coreid * 2)), 16); | ||
360 | octeon_wdt_write_string("\ten0\t0x"); | ||
361 | octeon_wdt_write_hex(cvmx_read_csr(CVMX_CIU_INTX_EN0(coreid * 2)), 16); | ||
362 | octeon_wdt_write_string("\r\n"); | ||
363 | |||
364 | octeon_wdt_write_string("*** Chip soft reset soon ***\r\n"); | ||
365 | } | ||
366 | |||
367 | static void octeon_wdt_disable_interrupt(int cpu) | ||
368 | { | ||
369 | unsigned int core; | ||
370 | unsigned int irq; | ||
371 | union cvmx_ciu_wdogx ciu_wdog; | ||
372 | |||
373 | core = cpu2core(cpu); | ||
374 | |||
375 | irq = OCTEON_IRQ_WDOG0 + core; | ||
376 | |||
377 | /* Poke the watchdog to clear out its state */ | ||
378 | cvmx_write_csr(CVMX_CIU_PP_POKEX(core), 1); | ||
379 | |||
380 | /* Disable the hardware. */ | ||
381 | ciu_wdog.u64 = 0; | ||
382 | cvmx_write_csr(CVMX_CIU_WDOGX(core), ciu_wdog.u64); | ||
383 | |||
384 | free_irq(irq, octeon_wdt_poke_irq); | ||
385 | } | ||
386 | |||
387 | static void octeon_wdt_setup_interrupt(int cpu) | ||
388 | { | ||
389 | unsigned int core; | ||
390 | unsigned int irq; | ||
391 | union cvmx_ciu_wdogx ciu_wdog; | ||
392 | |||
393 | core = cpu2core(cpu); | ||
394 | |||
395 | /* Disable it before doing anything with the interrupts. */ | ||
396 | ciu_wdog.u64 = 0; | ||
397 | cvmx_write_csr(CVMX_CIU_WDOGX(core), ciu_wdog.u64); | ||
398 | |||
399 | per_cpu_countdown[cpu] = countdown_reset; | ||
400 | |||
401 | irq = OCTEON_IRQ_WDOG0 + core; | ||
402 | |||
403 | if (request_irq(irq, octeon_wdt_poke_irq, | ||
404 | IRQF_DISABLED, "octeon_wdt", octeon_wdt_poke_irq)) | ||
405 | panic("octeon_wdt: Couldn't obtain irq %d", irq); | ||
406 | |||
407 | cpumask_set_cpu(cpu, &irq_enabled_cpus); | ||
408 | |||
409 | /* Poke the watchdog to clear out its state */ | ||
410 | cvmx_write_csr(CVMX_CIU_PP_POKEX(core), 1); | ||
411 | |||
412 | /* Finally enable the watchdog now that all handlers are installed */ | ||
413 | ciu_wdog.u64 = 0; | ||
414 | ciu_wdog.s.len = timeout_cnt; | ||
415 | ciu_wdog.s.mode = 3; /* 3 = Interrupt + NMI + Soft-Reset */ | ||
416 | cvmx_write_csr(CVMX_CIU_WDOGX(core), ciu_wdog.u64); | ||
417 | } | ||
418 | |||
419 | static int octeon_wdt_cpu_callback(struct notifier_block *nfb, | ||
420 | unsigned long action, void *hcpu) | ||
421 | { | ||
422 | unsigned int cpu = (unsigned long)hcpu; | ||
423 | |||
424 | switch (action) { | ||
425 | case CPU_DOWN_PREPARE: | ||
426 | octeon_wdt_disable_interrupt(cpu); | ||
427 | break; | ||
428 | case CPU_ONLINE: | ||
429 | case CPU_DOWN_FAILED: | ||
430 | octeon_wdt_setup_interrupt(cpu); | ||
431 | break; | ||
432 | default: | ||
433 | break; | ||
434 | } | ||
435 | return NOTIFY_OK; | ||
436 | } | ||
437 | |||
438 | static void octeon_wdt_ping(void) | ||
439 | { | ||
440 | int cpu; | ||
441 | int coreid; | ||
442 | |||
443 | for_each_online_cpu(cpu) { | ||
444 | coreid = cpu2core(cpu); | ||
445 | cvmx_write_csr(CVMX_CIU_PP_POKEX(coreid), 1); | ||
446 | per_cpu_countdown[cpu] = countdown_reset; | ||
447 | if ((countdown_reset || !do_coundown) && | ||
448 | !cpumask_test_cpu(cpu, &irq_enabled_cpus)) { | ||
449 | /* We have to enable the irq */ | ||
450 | int irq = OCTEON_IRQ_WDOG0 + coreid; | ||
451 | enable_irq(irq); | ||
452 | cpumask_set_cpu(cpu, &irq_enabled_cpus); | ||
453 | } | ||
454 | } | ||
455 | } | ||
456 | |||
457 | static void octeon_wdt_calc_parameters(int t) | ||
458 | { | ||
459 | unsigned int periods; | ||
460 | |||
461 | timeout_sec = max_timeout_sec; | ||
462 | |||
463 | |||
464 | /* | ||
465 | * Find the largest interrupt period, that can evenly divide | ||
466 | * the requested heartbeat time. | ||
467 | */ | ||
468 | while ((t % timeout_sec) != 0) | ||
469 | timeout_sec--; | ||
470 | |||
471 | periods = t / timeout_sec; | ||
472 | |||
473 | /* | ||
474 | * The last two periods are after the irq is disabled, and | ||
475 | * then to the nmi, so we subtract them off. | ||
476 | */ | ||
477 | |||
478 | countdown_reset = periods > 2 ? periods - 2 : 0; | ||
479 | heartbeat = t; | ||
480 | timeout_cnt = ((octeon_get_clock_rate() >> 8) * timeout_sec) >> 8; | ||
481 | } | ||
482 | |||
483 | static int octeon_wdt_set_heartbeat(int t) | ||
484 | { | ||
485 | int cpu; | ||
486 | int coreid; | ||
487 | union cvmx_ciu_wdogx ciu_wdog; | ||
488 | |||
489 | if (t <= 0) | ||
490 | return -1; | ||
491 | |||
492 | octeon_wdt_calc_parameters(t); | ||
493 | |||
494 | for_each_online_cpu(cpu) { | ||
495 | coreid = cpu2core(cpu); | ||
496 | cvmx_write_csr(CVMX_CIU_PP_POKEX(coreid), 1); | ||
497 | ciu_wdog.u64 = 0; | ||
498 | ciu_wdog.s.len = timeout_cnt; | ||
499 | ciu_wdog.s.mode = 3; /* 3 = Interrupt + NMI + Soft-Reset */ | ||
500 | cvmx_write_csr(CVMX_CIU_WDOGX(coreid), ciu_wdog.u64); | ||
501 | cvmx_write_csr(CVMX_CIU_PP_POKEX(coreid), 1); | ||
502 | } | ||
503 | octeon_wdt_ping(); /* Get the irqs back on. */ | ||
504 | return 0; | ||
505 | } | ||
506 | |||
507 | /** | ||
508 | * octeon_wdt_write: | ||
509 | * @file: file handle to the watchdog | ||
510 | * @buf: buffer to write (unused as data does not matter here | ||
511 | * @count: count of bytes | ||
512 | * @ppos: pointer to the position to write. No seeks allowed | ||
513 | * | ||
514 | * A write to a watchdog device is defined as a keepalive signal. Any | ||
515 | * write of data will do, as we we don't define content meaning. | ||
516 | */ | ||
517 | |||
518 | static ssize_t octeon_wdt_write(struct file *file, const char __user *buf, | ||
519 | size_t count, loff_t *ppos) | ||
520 | { | ||
521 | if (count) { | ||
522 | if (!nowayout) { | ||
523 | size_t i; | ||
524 | |||
525 | /* In case it was set long ago */ | ||
526 | expect_close = 0; | ||
527 | |||
528 | for (i = 0; i != count; i++) { | ||
529 | char c; | ||
530 | if (get_user(c, buf + i)) | ||
531 | return -EFAULT; | ||
532 | if (c == 'V') | ||
533 | expect_close = 1; | ||
534 | } | ||
535 | } | ||
536 | octeon_wdt_ping(); | ||
537 | } | ||
538 | return count; | ||
539 | } | ||
540 | |||
541 | /** | ||
542 | * octeon_wdt_ioctl: | ||
543 | * @file: file handle to the device | ||
544 | * @cmd: watchdog command | ||
545 | * @arg: argument pointer | ||
546 | * | ||
547 | * The watchdog API defines a common set of functions for all | ||
548 | * watchdogs according to their available features. We only | ||
549 | * actually usefully support querying capabilities and setting | ||
550 | * the timeout. | ||
551 | */ | ||
552 | |||
553 | static long octeon_wdt_ioctl(struct file *file, unsigned int cmd, | ||
554 | unsigned long arg) | ||
555 | { | ||
556 | void __user *argp = (void __user *)arg; | ||
557 | int __user *p = argp; | ||
558 | int new_heartbeat; | ||
559 | |||
560 | static struct watchdog_info ident = { | ||
561 | .options = WDIOF_SETTIMEOUT| | ||
562 | WDIOF_MAGICCLOSE| | ||
563 | WDIOF_KEEPALIVEPING, | ||
564 | .firmware_version = 1, | ||
565 | .identity = "OCTEON", | ||
566 | }; | ||
567 | |||
568 | switch (cmd) { | ||
569 | case WDIOC_GETSUPPORT: | ||
570 | return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0; | ||
571 | case WDIOC_GETSTATUS: | ||
572 | case WDIOC_GETBOOTSTATUS: | ||
573 | return put_user(0, p); | ||
574 | case WDIOC_KEEPALIVE: | ||
575 | octeon_wdt_ping(); | ||
576 | return 0; | ||
577 | case WDIOC_SETTIMEOUT: | ||
578 | if (get_user(new_heartbeat, p)) | ||
579 | return -EFAULT; | ||
580 | if (octeon_wdt_set_heartbeat(new_heartbeat)) | ||
581 | return -EINVAL; | ||
582 | /* Fall through. */ | ||
583 | case WDIOC_GETTIMEOUT: | ||
584 | return put_user(heartbeat, p); | ||
585 | default: | ||
586 | return -ENOTTY; | ||
587 | } | ||
588 | } | ||
589 | |||
590 | /** | ||
591 | * octeon_wdt_open: | ||
592 | * @inode: inode of device | ||
593 | * @file: file handle to device | ||
594 | * | ||
595 | * The watchdog device has been opened. The watchdog device is single | ||
596 | * open and on opening we do a ping to reset the counters. | ||
597 | */ | ||
598 | |||
599 | static int octeon_wdt_open(struct inode *inode, struct file *file) | ||
600 | { | ||
601 | if (test_and_set_bit(0, &octeon_wdt_is_open)) | ||
602 | return -EBUSY; | ||
603 | /* | ||
604 | * Activate | ||
605 | */ | ||
606 | octeon_wdt_ping(); | ||
607 | do_coundown = 1; | ||
608 | return nonseekable_open(inode, file); | ||
609 | } | ||
610 | |||
611 | /** | ||
612 | * octeon_wdt_release: | ||
613 | * @inode: inode to board | ||
614 | * @file: file handle to board | ||
615 | * | ||
616 | * The watchdog has a configurable API. There is a religious dispute | ||
617 | * between people who want their watchdog to be able to shut down and | ||
618 | * those who want to be sure if the watchdog manager dies the machine | ||
619 | * reboots. In the former case we disable the counters, in the latter | ||
620 | * case you have to open it again very soon. | ||
621 | */ | ||
622 | |||
623 | static int octeon_wdt_release(struct inode *inode, struct file *file) | ||
624 | { | ||
625 | if (expect_close) { | ||
626 | do_coundown = 0; | ||
627 | octeon_wdt_ping(); | ||
628 | } else { | ||
629 | pr_crit("octeon_wdt: WDT device closed unexpectedly. WDT will not stop!\n"); | ||
630 | } | ||
631 | clear_bit(0, &octeon_wdt_is_open); | ||
632 | expect_close = 0; | ||
633 | return 0; | ||
634 | } | ||
635 | |||
636 | static const struct file_operations octeon_wdt_fops = { | ||
637 | .owner = THIS_MODULE, | ||
638 | .llseek = no_llseek, | ||
639 | .write = octeon_wdt_write, | ||
640 | .unlocked_ioctl = octeon_wdt_ioctl, | ||
641 | .open = octeon_wdt_open, | ||
642 | .release = octeon_wdt_release, | ||
643 | }; | ||
644 | |||
645 | static struct miscdevice octeon_wdt_miscdev = { | ||
646 | .minor = WATCHDOG_MINOR, | ||
647 | .name = "watchdog", | ||
648 | .fops = &octeon_wdt_fops, | ||
649 | }; | ||
650 | |||
651 | static struct notifier_block octeon_wdt_cpu_notifier = { | ||
652 | .notifier_call = octeon_wdt_cpu_callback, | ||
653 | }; | ||
654 | |||
655 | |||
656 | /** | ||
657 | * Module/ driver initialization. | ||
658 | * | ||
659 | * Returns Zero on success | ||
660 | */ | ||
661 | static int __init octeon_wdt_init(void) | ||
662 | { | ||
663 | int i; | ||
664 | int ret; | ||
665 | int cpu; | ||
666 | u64 *ptr; | ||
667 | |||
668 | /* | ||
669 | * Watchdog time expiration length = The 16 bits of LEN | ||
670 | * represent the most significant bits of a 24 bit decrementer | ||
671 | * that decrements every 256 cycles. | ||
672 | * | ||
673 | * Try for a timeout of 5 sec, if that fails a smaller number | ||
674 | * of even seconds, | ||
675 | */ | ||
676 | max_timeout_sec = 6; | ||
677 | do { | ||
678 | max_timeout_sec--; | ||
679 | timeout_cnt = ((octeon_get_clock_rate() >> 8) * max_timeout_sec) >> 8; | ||
680 | } while (timeout_cnt > 65535); | ||
681 | |||
682 | BUG_ON(timeout_cnt == 0); | ||
683 | |||
684 | octeon_wdt_calc_parameters(heartbeat); | ||
685 | |||
686 | pr_info("octeon_wdt: Initial granularity %d Sec.\n", timeout_sec); | ||
687 | |||
688 | ret = misc_register(&octeon_wdt_miscdev); | ||
689 | if (ret) { | ||
690 | pr_err("octeon_wdt: cannot register miscdev on minor=%d (err=%d)\n", | ||
691 | WATCHDOG_MINOR, ret); | ||
692 | goto out; | ||
693 | } | ||
694 | |||
695 | /* Build the NMI handler ... */ | ||
696 | octeon_wdt_build_stage1(); | ||
697 | |||
698 | /* ... and install it. */ | ||
699 | ptr = (u64 *) nmi_stage1_insns; | ||
700 | for (i = 0; i < 16; i++) { | ||
701 | cvmx_write_csr(CVMX_MIO_BOOT_LOC_ADR, i * 8); | ||
702 | cvmx_write_csr(CVMX_MIO_BOOT_LOC_DAT, ptr[i]); | ||
703 | } | ||
704 | cvmx_write_csr(CVMX_MIO_BOOT_LOC_CFGX(0), 0x81fc0000); | ||
705 | |||
706 | cpumask_clear(&irq_enabled_cpus); | ||
707 | |||
708 | for_each_online_cpu(cpu) | ||
709 | octeon_wdt_setup_interrupt(cpu); | ||
710 | |||
711 | register_hotcpu_notifier(&octeon_wdt_cpu_notifier); | ||
712 | out: | ||
713 | return ret; | ||
714 | } | ||
715 | |||
716 | /** | ||
717 | * Module / driver shutdown | ||
718 | */ | ||
719 | static void __exit octeon_wdt_cleanup(void) | ||
720 | { | ||
721 | int cpu; | ||
722 | |||
723 | misc_deregister(&octeon_wdt_miscdev); | ||
724 | |||
725 | unregister_hotcpu_notifier(&octeon_wdt_cpu_notifier); | ||
726 | |||
727 | for_each_online_cpu(cpu) { | ||
728 | int core = cpu2core(cpu); | ||
729 | /* Disable the watchdog */ | ||
730 | cvmx_write_csr(CVMX_CIU_WDOGX(core), 0); | ||
731 | /* Free the interrupt handler */ | ||
732 | free_irq(OCTEON_IRQ_WDOG0 + core, octeon_wdt_poke_irq); | ||
733 | } | ||
734 | /* | ||
735 | * Disable the boot-bus memory, the code it points to is soon | ||
736 | * to go missing. | ||
737 | */ | ||
738 | cvmx_write_csr(CVMX_MIO_BOOT_LOC_CFGX(0), 0); | ||
739 | } | ||
740 | |||
741 | MODULE_LICENSE("GPL"); | ||
742 | MODULE_AUTHOR("Cavium Networks <support@caviumnetworks.com>"); | ||
743 | MODULE_DESCRIPTION("Cavium Networks Octeon Watchdog driver."); | ||
744 | module_init(octeon_wdt_init); | ||
745 | module_exit(octeon_wdt_cleanup); | ||
diff --git a/drivers/watchdog/octeon-wdt-nmi.S b/drivers/watchdog/octeon-wdt-nmi.S new file mode 100644 index 000000000000..8a900a5e3233 --- /dev/null +++ b/drivers/watchdog/octeon-wdt-nmi.S | |||
@@ -0,0 +1,64 @@ | |||
1 | /* | ||
2 | * This file is subject to the terms and conditions of the GNU General Public | ||
3 | * License. See the file "COPYING" in the main directory of this archive | ||
4 | * for more details. | ||
5 | * | ||
6 | * Copyright (C) 2007 Cavium Networks | ||
7 | */ | ||
8 | #include <asm/asm.h> | ||
9 | #include <asm/regdef.h> | ||
10 | |||
11 | #define SAVE_REG(r) sd $r, -32768+6912-(32-r)*8($0) | ||
12 | |||
13 | NESTED(octeon_wdt_nmi_stage2, 0, sp) | ||
14 | .set push | ||
15 | .set noreorder | ||
16 | .set noat | ||
17 | /* Save all registers to the top CVMSEG. This shouldn't | ||
18 | * corrupt any state used by the kernel. Also all registers | ||
19 | * should have the value right before the NMI. */ | ||
20 | SAVE_REG(0) | ||
21 | SAVE_REG(1) | ||
22 | SAVE_REG(2) | ||
23 | SAVE_REG(3) | ||
24 | SAVE_REG(4) | ||
25 | SAVE_REG(5) | ||
26 | SAVE_REG(6) | ||
27 | SAVE_REG(7) | ||
28 | SAVE_REG(8) | ||
29 | SAVE_REG(9) | ||
30 | SAVE_REG(10) | ||
31 | SAVE_REG(11) | ||
32 | SAVE_REG(12) | ||
33 | SAVE_REG(13) | ||
34 | SAVE_REG(14) | ||
35 | SAVE_REG(15) | ||
36 | SAVE_REG(16) | ||
37 | SAVE_REG(17) | ||
38 | SAVE_REG(18) | ||
39 | SAVE_REG(19) | ||
40 | SAVE_REG(20) | ||
41 | SAVE_REG(21) | ||
42 | SAVE_REG(22) | ||
43 | SAVE_REG(23) | ||
44 | SAVE_REG(24) | ||
45 | SAVE_REG(25) | ||
46 | SAVE_REG(26) | ||
47 | SAVE_REG(27) | ||
48 | SAVE_REG(28) | ||
49 | SAVE_REG(29) | ||
50 | SAVE_REG(30) | ||
51 | SAVE_REG(31) | ||
52 | /* Set the stack to begin right below the registers */ | ||
53 | li sp, -32768+6912-32*8 | ||
54 | /* Load the address of the third stage handler */ | ||
55 | dla a0, octeon_wdt_nmi_stage3 | ||
56 | /* Call the third stage handler */ | ||
57 | jal a0 | ||
58 | /* a0 is the address of the saved registers */ | ||
59 | move a0, sp | ||
60 | /* Loop forvever if we get here. */ | ||
61 | 1: b 1b | ||
62 | nop | ||
63 | .set pop | ||
64 | END(octeon_wdt_nmi_stage2) | ||