Commit | Line | Data |
---|---|---|
935c500c JC |
1 | /* |
2 | * This program is free software; you can redistribute it and/or modify it | |
3 | * under the terms of the GNU General Public License version 2 as published | |
4 | * by the Free Software Foundation. | |
5 | * | |
6 | * Copyright (C) 2010 John Crispin <blogic@openwrt.org> | |
7 | */ | |
8 | ||
9 | #include <linux/init.h> | |
4af92e7a | 10 | #include <linux/export.h> |
935c500c JC |
11 | #include <linux/types.h> |
12 | #include <linux/platform_device.h> | |
13 | #include <linux/mutex.h> | |
14 | #include <linux/gpio.h> | |
15 | #include <linux/io.h> | |
16 | ||
17 | #include <lantiq_soc.h> | |
18 | ||
19 | /* | |
20 | * By attaching hardware latches to the EBU it is possible to create output | |
21 | * only gpios. This driver configures a special memory address, which when | |
22 | * written to outputs 16 bit to the latches. | |
23 | */ | |
24 | ||
25 | #define LTQ_EBU_BUSCON 0x1e7ff /* 16 bit access, slowest timing */ | |
26 | #define LTQ_EBU_WP 0x80000000 /* write protect bit */ | |
27 | ||
28 | /* we keep a shadow value of the last value written to the ebu */ | |
29 | static int ltq_ebu_gpio_shadow = 0x0; | |
30 | static void __iomem *ltq_ebu_gpio_membase; | |
31 | ||
32 | static void ltq_ebu_apply(void) | |
33 | { | |
34 | unsigned long flags; | |
35 | ||
36 | spin_lock_irqsave(&ebu_lock, flags); | |
37 | ltq_ebu_w32(LTQ_EBU_BUSCON, LTQ_EBU_BUSCON1); | |
38 | *((__u16 *)ltq_ebu_gpio_membase) = ltq_ebu_gpio_shadow; | |
39 | ltq_ebu_w32(LTQ_EBU_BUSCON | LTQ_EBU_WP, LTQ_EBU_BUSCON1); | |
40 | spin_unlock_irqrestore(&ebu_lock, flags); | |
41 | } | |
42 | ||
43 | static void ltq_ebu_set(struct gpio_chip *chip, unsigned offset, int value) | |
44 | { | |
45 | if (value) | |
46 | ltq_ebu_gpio_shadow |= (1 << offset); | |
47 | else | |
48 | ltq_ebu_gpio_shadow &= ~(1 << offset); | |
49 | ltq_ebu_apply(); | |
50 | } | |
51 | ||
52 | static int ltq_ebu_direction_output(struct gpio_chip *chip, unsigned offset, | |
53 | int value) | |
54 | { | |
55 | ltq_ebu_set(chip, offset, value); | |
56 | ||
57 | return 0; | |
58 | } | |
59 | ||
60 | static struct gpio_chip ltq_ebu_chip = { | |
61 | .label = "ltq_ebu", | |
62 | .direction_output = ltq_ebu_direction_output, | |
63 | .set = ltq_ebu_set, | |
64 | .base = 72, | |
65 | .ngpio = 16, | |
66 | .can_sleep = 1, | |
67 | .owner = THIS_MODULE, | |
68 | }; | |
69 | ||
70 | static int ltq_ebu_probe(struct platform_device *pdev) | |
71 | { | |
72 | int ret = 0; | |
73 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
74 | ||
75 | if (!res) { | |
76 | dev_err(&pdev->dev, "failed to get memory resource\n"); | |
77 | return -ENOENT; | |
78 | } | |
79 | ||
80 | res = devm_request_mem_region(&pdev->dev, res->start, | |
81 | resource_size(res), dev_name(&pdev->dev)); | |
82 | if (!res) { | |
83 | dev_err(&pdev->dev, "failed to request memory resource\n"); | |
84 | return -EBUSY; | |
85 | } | |
86 | ||
87 | ltq_ebu_gpio_membase = devm_ioremap_nocache(&pdev->dev, res->start, | |
88 | resource_size(res)); | |
89 | if (!ltq_ebu_gpio_membase) { | |
90 | dev_err(&pdev->dev, "Failed to ioremap mem region\n"); | |
91 | return -ENOMEM; | |
92 | } | |
93 | ||
94 | /* grab the default shadow value passed form the platform code */ | |
95 | ltq_ebu_gpio_shadow = (unsigned int) pdev->dev.platform_data; | |
96 | ||
97 | /* tell the ebu controller which memory address we will be using */ | |
98 | ltq_ebu_w32(pdev->resource->start | 0x1, LTQ_EBU_ADDRSEL1); | |
99 | ||
100 | /* write protect the region */ | |
101 | ltq_ebu_w32(LTQ_EBU_BUSCON | LTQ_EBU_WP, LTQ_EBU_BUSCON1); | |
102 | ||
103 | ret = gpiochip_add(<q_ebu_chip); | |
104 | if (!ret) | |
105 | ltq_ebu_apply(); | |
106 | return ret; | |
107 | } | |
108 | ||
109 | static struct platform_driver ltq_ebu_driver = { | |
110 | .probe = ltq_ebu_probe, | |
111 | .driver = { | |
112 | .name = "ltq_ebu", | |
113 | .owner = THIS_MODULE, | |
114 | }, | |
115 | }; | |
116 | ||
117 | static int __init ltq_ebu_init(void) | |
118 | { | |
119 | int ret = platform_driver_register(<q_ebu_driver); | |
120 | ||
121 | if (ret) | |
122 | pr_info("ltq_ebu : Error registering platfom driver!"); | |
123 | return ret; | |
124 | } | |
125 | ||
126 | postcore_initcall(ltq_ebu_init); |