1/*
2 * i2sbus driver -- bus control routines
3 *
4 * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
5 *
6 * GPL v2, can be found in COPYING.
7 */
8
9#include <linux/kernel.h>
10#include <linux/delay.h>
11
12#include <asm/io.h>
13#include <asm/prom.h>
14#include <asm/macio.h>
15#include <asm/pmac_feature.h>
16#include <asm/pmac_pfunc.h>
17#include <asm/keylargo.h>
18
19#include "i2sbus.h"
20
21int i2sbus_control_init(struct macio_dev* dev, struct i2sbus_control **c)
22{
23	*c = kzalloc(sizeof(struct i2sbus_control), GFP_KERNEL);
24	if (!*c)
25		return -ENOMEM;
26
27	INIT_LIST_HEAD(&(*c)->list);
28
29	(*c)->macio = dev->bus->chip;
30	return 0;
31}
32
33void i2sbus_control_destroy(struct i2sbus_control *c)
34{
35	kfree(c);
36}
37
38/* this is serialised externally */
39int i2sbus_control_add_dev(struct i2sbus_control *c,
40			   struct i2sbus_dev *i2sdev)
41{
42	struct device_node *np;
43
44	np = i2sdev->sound.ofdev.node;
45	i2sdev->enable = pmf_find_function(np, "enable");
46	i2sdev->cell_enable = pmf_find_function(np, "cell-enable");
47	i2sdev->clock_enable = pmf_find_function(np, "clock-enable");
48	i2sdev->cell_disable = pmf_find_function(np, "cell-disable");
49	i2sdev->clock_disable = pmf_find_function(np, "clock-disable");
50
51	/* if the bus number is not 0 or 1 we absolutely need to use
52	 * the platform functions -- there's nothing in Darwin that
53	 * would allow seeing a system behind what the FCRs are then,
54	 * and I don't want to go parsing a bunch of platform functions
55	 * by hand to try finding a system... */
56	if (i2sdev->bus_number != 0 && i2sdev->bus_number != 1 &&
57	    (!i2sdev->enable ||
58	     !i2sdev->cell_enable || !i2sdev->clock_enable ||
59	     !i2sdev->cell_disable || !i2sdev->clock_disable)) {
60		pmf_put_function(i2sdev->enable);
61		pmf_put_function(i2sdev->cell_enable);
62		pmf_put_function(i2sdev->clock_enable);
63		pmf_put_function(i2sdev->cell_disable);
64		pmf_put_function(i2sdev->clock_disable);
65		return -ENODEV;
66	}
67
68	list_add(&i2sdev->item, &c->list);
69
70	return 0;
71}
72
73void i2sbus_control_remove_dev(struct i2sbus_control *c,
74			       struct i2sbus_dev *i2sdev)
75{
76	/* this is serialised externally */
77	list_del(&i2sdev->item);
78	if (list_empty(&c->list))
79		i2sbus_control_destroy(c);
80}
81
82int i2sbus_control_enable(struct i2sbus_control *c,
83			  struct i2sbus_dev *i2sdev)
84{
85	struct pmf_args args = { .count = 0 };
86	struct macio_chip *macio = c->macio;
87
88	if (i2sdev->enable)
89		return pmf_call_one(i2sdev->enable, &args);
90
91	if (macio == NULL || macio->base == NULL)
92		return -ENODEV;
93
94	switch (i2sdev->bus_number) {
95	case 0:
96		/* these need to be locked or done through
97		 * newly created feature calls! */
98		MACIO_BIS(KEYLARGO_FCR1, KL1_I2S0_ENABLE);
99		break;
100	case 1:
101		MACIO_BIS(KEYLARGO_FCR1, KL1_I2S1_ENABLE);
102		break;
103	default:
104		return -ENODEV;
105	}
106	return 0;
107}
108
109int i2sbus_control_cell(struct i2sbus_control *c,
110			struct i2sbus_dev *i2sdev,
111			int enable)
112{
113	struct pmf_args args = { .count = 0 };
114	struct macio_chip *macio = c->macio;
115
116	switch (enable) {
117	case 0:
118		if (i2sdev->cell_disable)
119			return pmf_call_one(i2sdev->cell_disable, &args);
120		break;
121	case 1:
122		if (i2sdev->cell_enable)
123			return pmf_call_one(i2sdev->cell_enable, &args);
124		break;
125	default:
126		printk(KERN_ERR "i2sbus: INVALID CELL ENABLE VALUE\n");
127		return -ENODEV;
128	}
129
130	if (macio == NULL || macio->base == NULL)
131		return -ENODEV;
132
133	switch (i2sdev->bus_number) {
134	case 0:
135		if (enable)
136			MACIO_BIS(KEYLARGO_FCR1, KL1_I2S0_CELL_ENABLE);
137		else
138			MACIO_BIC(KEYLARGO_FCR1, KL1_I2S0_CELL_ENABLE);
139		break;
140	case 1:
141		if (enable)
142			MACIO_BIS(KEYLARGO_FCR1, KL1_I2S1_CELL_ENABLE);
143		else
144			MACIO_BIC(KEYLARGO_FCR1, KL1_I2S1_CELL_ENABLE);
145		break;
146	default:
147		return -ENODEV;
148	}
149	return 0;
150}
151
152int i2sbus_control_clock(struct i2sbus_control *c,
153			 struct i2sbus_dev *i2sdev,
154			 int enable)
155{
156	struct pmf_args args = { .count = 0 };
157	struct macio_chip *macio = c->macio;
158
159	switch (enable) {
160	case 0:
161		if (i2sdev->clock_disable)
162			return pmf_call_one(i2sdev->clock_disable, &args);
163		break;
164	case 1:
165		if (i2sdev->clock_enable)
166			return pmf_call_one(i2sdev->clock_enable, &args);
167		break;
168	default:
169		printk(KERN_ERR "i2sbus: INVALID CLOCK ENABLE VALUE\n");
170		return -ENODEV;
171	}
172
173	if (macio == NULL || macio->base == NULL)
174		return -ENODEV;
175
176	switch (i2sdev->bus_number) {
177	case 0:
178		if (enable)
179			MACIO_BIS(KEYLARGO_FCR1, KL1_I2S0_CLK_ENABLE_BIT);
180		else
181			MACIO_BIC(KEYLARGO_FCR1, KL1_I2S0_CLK_ENABLE_BIT);
182		break;
183	case 1:
184		if (enable)
185			MACIO_BIS(KEYLARGO_FCR1, KL1_I2S1_CLK_ENABLE_BIT);
186		else
187			MACIO_BIC(KEYLARGO_FCR1, KL1_I2S1_CLK_ENABLE_BIT);
188		break;
189	default:
190		return -ENODEV;
191	}
192	return 0;
193}
194