aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@armlinux.org.uk>2016-08-31 03:49:45 -0400
committerRussell King <rmk+kernel@armlinux.org.uk>2017-12-31 19:50:05 -0500
commitf85fac0efa9087a9311f3d2bdc227ded3ac95db5 (patch)
treead17d2593ee712df0affb8ae6c80582dfef9dbf7
parentb955153bfa68d7a9fa2be40bb606cf9578b66511 (diff)
ARM: sa1100/neponset: add GPIO drivers for control and modem registers
The NCR, MDM_CTL* and AUD registers manipulate the state of external signals (eg, the RTS, DTR signals and the ethernet oscillator enable signal) or indicate the state of external signals (eg, CTS, DSR). Where these registers can be written, the current value can be read back, which relieves us from having to maintain a software copy of the current state. Model these registers as fixed-direction GPIO registers. Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
-rw-r--r--arch/arm/mach-sa1100/neponset.c159
1 files changed, 96 insertions, 63 deletions
diff --git a/arch/arm/mach-sa1100/neponset.c b/arch/arm/mach-sa1100/neponset.c
index a61a2432766b..b1823f445358 100644
--- a/arch/arm/mach-sa1100/neponset.c
+++ b/arch/arm/mach-sa1100/neponset.c
@@ -3,6 +3,8 @@
3 * linux/arch/arm/mach-sa1100/neponset.c 3 * linux/arch/arm/mach-sa1100/neponset.c
4 */ 4 */
5#include <linux/err.h> 5#include <linux/err.h>
6#include <linux/gpio/driver.h>
7#include <linux/gpio/gpio-reg.h>
6#include <linux/init.h> 8#include <linux/init.h>
7#include <linux/ioport.h> 9#include <linux/ioport.h>
8#include <linux/irq.h> 10#include <linux/irq.h>
@@ -45,10 +47,13 @@
45#define IRR_USAR (1 << 1) 47#define IRR_USAR (1 << 1)
46#define IRR_SA1111 (1 << 2) 48#define IRR_SA1111 (1 << 2)
47 49
50#define NCR_NGPIO 7
51
48#define MDM_CTL0_RTS1 (1 << 0) 52#define MDM_CTL0_RTS1 (1 << 0)
49#define MDM_CTL0_DTR1 (1 << 1) 53#define MDM_CTL0_DTR1 (1 << 1)
50#define MDM_CTL0_RTS2 (1 << 2) 54#define MDM_CTL0_RTS2 (1 << 2)
51#define MDM_CTL0_DTR2 (1 << 3) 55#define MDM_CTL0_DTR2 (1 << 3)
56#define MDM_CTL0_NGPIO 4
52 57
53#define MDM_CTL1_CTS1 (1 << 0) 58#define MDM_CTL1_CTS1 (1 << 0)
54#define MDM_CTL1_DSR1 (1 << 1) 59#define MDM_CTL1_DSR1 (1 << 1)
@@ -56,80 +61,87 @@
56#define MDM_CTL1_CTS2 (1 << 3) 61#define MDM_CTL1_CTS2 (1 << 3)
57#define MDM_CTL1_DSR2 (1 << 4) 62#define MDM_CTL1_DSR2 (1 << 4)
58#define MDM_CTL1_DCD2 (1 << 5) 63#define MDM_CTL1_DCD2 (1 << 5)
64#define MDM_CTL1_NGPIO 6
59 65
60#define AUD_SEL_1341 (1 << 0) 66#define AUD_SEL_1341 (1 << 0)
61#define AUD_MUTE_1341 (1 << 1) 67#define AUD_MUTE_1341 (1 << 1)
68#define AUD_NGPIO 2
62 69
63extern void sa1110_mb_disable(void); 70extern void sa1110_mb_disable(void);
64 71
72#define to_neponset_gpio_chip(x) container_of(x, struct neponset_gpio_chip, gc)
73
74static const char *neponset_ncr_names[] = {
75 "gp01_off", "tp_power", "ms_power", "enet_osc",
76 "spi_kb_wk_up", "a0vpp", "a1vpp"
77};
78
79static const char *neponset_mdmctl0_names[] = {
80 "rts3", "dtr3", "rts1", "dtr1",
81};
82
83static const char *neponset_mdmctl1_names[] = {
84 "cts3", "dsr3", "dcd3", "cts1", "dsr1", "dcd1"
85};
86
87static const char *neponset_aud_names[] = {
88 "sel_1341", "mute_1341",
89};
90
65struct neponset_drvdata { 91struct neponset_drvdata {
66 void __iomem *base; 92 void __iomem *base;
67 struct platform_device *sa1111; 93 struct platform_device *sa1111;
68 struct platform_device *smc91x; 94 struct platform_device *smc91x;
69 unsigned irq_base; 95 unsigned irq_base;
70#ifdef CONFIG_PM_SLEEP 96 struct gpio_chip *gpio[4];
71 u32 ncr0;
72 u32 mdm_ctl_0;
73#endif
74}; 97};
75 98
76static void __iomem *nep_base; 99static struct neponset_drvdata *nep;
77 100
78void neponset_ncr_frob(unsigned int mask, unsigned int val) 101void neponset_ncr_frob(unsigned int mask, unsigned int val)
79{ 102{
80 void __iomem *base = nep_base; 103 struct neponset_drvdata *n = nep;
81 104 unsigned long m = mask, v = val;
82 if (base) { 105
83 unsigned long flags; 106 if (nep)
84 unsigned v; 107 n->gpio[0]->set_multiple(n->gpio[0], &m, &v);
85 108 else
86 local_irq_save(flags); 109 WARN(1, "nep unset\n");
87 v = readb_relaxed(base + NCR_0);
88 writeb_relaxed((v & ~mask) | val, base + NCR_0);
89 local_irq_restore(flags);
90 } else {
91 WARN(1, "nep_base unset\n");
92 }
93} 110}
94EXPORT_SYMBOL(neponset_ncr_frob); 111EXPORT_SYMBOL(neponset_ncr_frob);
95 112
96static void neponset_set_mctrl(struct uart_port *port, u_int mctrl) 113static void neponset_set_mctrl(struct uart_port *port, u_int mctrl)
97{ 114{
98 void __iomem *base = nep_base; 115 struct neponset_drvdata *n = nep;
99 u_int mdm_ctl0; 116 unsigned long mask, val = 0;
100 117
101 if (!base) 118 if (!n)
102 return; 119 return;
103 120
104 mdm_ctl0 = readb_relaxed(base + MDM_CTL_0);
105 if (port->mapbase == _Ser1UTCR0) { 121 if (port->mapbase == _Ser1UTCR0) {
106 if (mctrl & TIOCM_RTS) 122 mask = MDM_CTL0_RTS2 | MDM_CTL0_DTR2;
107 mdm_ctl0 &= ~MDM_CTL0_RTS2; 123
108 else 124 if (!(mctrl & TIOCM_RTS))
109 mdm_ctl0 |= MDM_CTL0_RTS2; 125 val |= MDM_CTL0_RTS2;
110 126
111 if (mctrl & TIOCM_DTR) 127 if (!(mctrl & TIOCM_DTR))
112 mdm_ctl0 &= ~MDM_CTL0_DTR2; 128 val |= MDM_CTL0_DTR2;
113 else
114 mdm_ctl0 |= MDM_CTL0_DTR2;
115 } else if (port->mapbase == _Ser3UTCR0) { 129 } else if (port->mapbase == _Ser3UTCR0) {
116 if (mctrl & TIOCM_RTS) 130 mask = MDM_CTL0_RTS1 | MDM_CTL0_DTR1;
117 mdm_ctl0 &= ~MDM_CTL0_RTS1; 131
118 else 132 if (!(mctrl & TIOCM_RTS))
119 mdm_ctl0 |= MDM_CTL0_RTS1; 133 val |= MDM_CTL0_RTS1;
120 134
121 if (mctrl & TIOCM_DTR) 135 if (!(mctrl & TIOCM_DTR))
122 mdm_ctl0 &= ~MDM_CTL0_DTR1; 136 val |= MDM_CTL0_DTR1;
123 else
124 mdm_ctl0 |= MDM_CTL0_DTR1;
125 } 137 }
126 138
127 writeb_relaxed(mdm_ctl0, base + MDM_CTL_0); 139 n->gpio[1]->set_multiple(n->gpio[1], &mask, &val);
128} 140}
129 141
130static u_int neponset_get_mctrl(struct uart_port *port) 142static u_int neponset_get_mctrl(struct uart_port *port)
131{ 143{
132 void __iomem *base = nep_base; 144 void __iomem *base = nep->base;
133 u_int ret = TIOCM_CD | TIOCM_CTS | TIOCM_DSR; 145 u_int ret = TIOCM_CD | TIOCM_CTS | TIOCM_DSR;
134 u_int mdm_ctl1; 146 u_int mdm_ctl1;
135 147
@@ -231,6 +243,22 @@ static struct irq_chip nochip = {
231 .irq_unmask = nochip_noop, 243 .irq_unmask = nochip_noop,
232}; 244};
233 245
246static int neponset_init_gpio(struct gpio_chip **gcp,
247 struct device *dev, const char *label, void __iomem *reg,
248 unsigned num, bool in, const char *const * names)
249{
250 struct gpio_chip *gc;
251
252 gc = gpio_reg_init(dev, reg, -1, num, label, in ? 0xffffffff : 0,
253 readl_relaxed(reg), names, NULL, NULL);
254 if (IS_ERR(gc))
255 return PTR_ERR(gc);
256
257 *gcp = gc;
258
259 return 0;
260}
261
234static struct sa1111_platform_data sa1111_info = { 262static struct sa1111_platform_data sa1111_info = {
235 .disable_devs = SA1111_DEVID_PS2_MSE, 263 .disable_devs = SA1111_DEVID_PS2_MSE,
236}; 264};
@@ -274,7 +302,7 @@ static int neponset_probe(struct platform_device *dev)
274 }; 302 };
275 int ret, irq; 303 int ret, irq;
276 304
277 if (nep_base) 305 if (nep)
278 return -EBUSY; 306 return -EBUSY;
279 307
280 irq = ret = platform_get_irq(dev, 0); 308 irq = ret = platform_get_irq(dev, 0);
@@ -330,6 +358,22 @@ static int neponset_probe(struct platform_device *dev)
330 irq_set_irq_type(irq, IRQ_TYPE_EDGE_RISING); 358 irq_set_irq_type(irq, IRQ_TYPE_EDGE_RISING);
331 irq_set_chained_handler_and_data(irq, neponset_irq_handler, d); 359 irq_set_chained_handler_and_data(irq, neponset_irq_handler, d);
332 360
361 /* Disable GPIO 0/1 drivers so the buttons work on the Assabet */
362 writeb_relaxed(NCR_GP01_OFF, d->base + NCR_0);
363
364 neponset_init_gpio(&d->gpio[0], &dev->dev, "neponset-ncr",
365 d->base + NCR_0, NCR_NGPIO, false,
366 neponset_ncr_names);
367 neponset_init_gpio(&d->gpio[1], &dev->dev, "neponset-mdm-ctl0",
368 d->base + MDM_CTL_0, MDM_CTL0_NGPIO, false,
369 neponset_mdmctl0_names);
370 neponset_init_gpio(&d->gpio[2], &dev->dev, "neponset-mdm-ctl1",
371 d->base + MDM_CTL_1, MDM_CTL1_NGPIO, true,
372 neponset_mdmctl1_names);
373 neponset_init_gpio(&d->gpio[3], &dev->dev, "neponset-aud-ctl",
374 d->base + AUD_CTL, AUD_NGPIO, false,
375 neponset_aud_names);
376
333 /* 377 /*
334 * We would set IRQ_GPIO25 to be a wake-up IRQ, but unfortunately 378 * We would set IRQ_GPIO25 to be a wake-up IRQ, but unfortunately
335 * something on the Neponset activates this IRQ on sleep (eth?) 379 * something on the Neponset activates this IRQ on sleep (eth?)
@@ -340,16 +384,13 @@ static int neponset_probe(struct platform_device *dev)
340 384
341 dev_info(&dev->dev, "Neponset daughter board, providing IRQ%u-%u\n", 385 dev_info(&dev->dev, "Neponset daughter board, providing IRQ%u-%u\n",
342 d->irq_base, d->irq_base + NEP_IRQ_NR - 1); 386 d->irq_base, d->irq_base + NEP_IRQ_NR - 1);
343 nep_base = d->base; 387 nep = d;
344 388
345 sa1100_register_uart_fns(&neponset_port_fns); 389 sa1100_register_uart_fns(&neponset_port_fns);
346 390
347 /* Ensure that the memory bus request/grant signals are setup */ 391 /* Ensure that the memory bus request/grant signals are setup */
348 sa1110_mb_disable(); 392 sa1110_mb_disable();
349 393
350 /* Disable GPIO 0/1 drivers so the buttons work on the Assabet */
351 writeb_relaxed(NCR_GP01_OFF, d->base + NCR_0);
352
353 sa1111_resources[0].parent = sa1111_res; 394 sa1111_resources[0].parent = sa1111_res;
354 sa1111_resources[1].start = d->irq_base + NEP_IRQ_SA1111; 395 sa1111_resources[1].start = d->irq_base + NEP_IRQ_SA1111;
355 sa1111_resources[1].end = d->irq_base + NEP_IRQ_SA1111; 396 sa1111_resources[1].end = d->irq_base + NEP_IRQ_SA1111;
@@ -385,7 +426,7 @@ static int neponset_remove(struct platform_device *dev)
385 platform_device_unregister(d->smc91x); 426 platform_device_unregister(d->smc91x);
386 irq_set_chained_handler(irq, NULL); 427 irq_set_chained_handler(irq, NULL);
387 irq_free_descs(d->irq_base, NEP_IRQ_NR); 428 irq_free_descs(d->irq_base, NEP_IRQ_NR);
388 nep_base = NULL; 429 nep = NULL;
389 iounmap(d->base); 430 iounmap(d->base);
390 kfree(d); 431 kfree(d);
391 432
@@ -393,30 +434,22 @@ static int neponset_remove(struct platform_device *dev)
393} 434}
394 435
395#ifdef CONFIG_PM_SLEEP 436#ifdef CONFIG_PM_SLEEP
396static int neponset_suspend(struct device *dev)
397{
398 struct neponset_drvdata *d = dev_get_drvdata(dev);
399
400 d->ncr0 = readb_relaxed(d->base + NCR_0);
401 d->mdm_ctl_0 = readb_relaxed(d->base + MDM_CTL_0);
402
403 return 0;
404}
405
406static int neponset_resume(struct device *dev) 437static int neponset_resume(struct device *dev)
407{ 438{
408 struct neponset_drvdata *d = dev_get_drvdata(dev); 439 struct neponset_drvdata *d = dev_get_drvdata(dev);
440 int i, ret = 0;
409 441
410 writeb_relaxed(d->ncr0, d->base + NCR_0); 442 for (i = 0; i < ARRAY_SIZE(d->gpio); i++) {
411 writeb_relaxed(d->mdm_ctl_0, d->base + MDM_CTL_0); 443 ret = gpio_reg_resume(d->gpio[i]);
444 if (ret)
445 break;
446 }
412 447
413 return 0; 448 return ret;
414} 449}
415 450
416static const struct dev_pm_ops neponset_pm_ops = { 451static const struct dev_pm_ops neponset_pm_ops = {
417 .suspend_noirq = neponset_suspend,
418 .resume_noirq = neponset_resume, 452 .resume_noirq = neponset_resume,
419 .freeze_noirq = neponset_suspend,
420 .restore_noirq = neponset_resume, 453 .restore_noirq = neponset_resume,
421}; 454};
422#define PM_OPS &neponset_pm_ops 455#define PM_OPS &neponset_pm_ops