162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * i2sbus driver -- bus control routines
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/kernel.h>
962306a36Sopenharmony_ci#include <linux/delay.h>
1062306a36Sopenharmony_ci#include <linux/slab.h>
1162306a36Sopenharmony_ci#include <linux/io.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <asm/prom.h>
1462306a36Sopenharmony_ci#include <asm/macio.h>
1562306a36Sopenharmony_ci#include <asm/pmac_feature.h>
1662306a36Sopenharmony_ci#include <asm/pmac_pfunc.h>
1762306a36Sopenharmony_ci#include <asm/keylargo.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include "i2sbus.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ciint i2sbus_control_init(struct macio_dev* dev, struct i2sbus_control **c)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	*c = kzalloc(sizeof(struct i2sbus_control), GFP_KERNEL);
2462306a36Sopenharmony_ci	if (!*c)
2562306a36Sopenharmony_ci		return -ENOMEM;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	INIT_LIST_HEAD(&(*c)->list);
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	(*c)->macio = dev->bus->chip;
3062306a36Sopenharmony_ci	return 0;
3162306a36Sopenharmony_ci}
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_civoid i2sbus_control_destroy(struct i2sbus_control *c)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	kfree(c);
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/* this is serialised externally */
3962306a36Sopenharmony_ciint i2sbus_control_add_dev(struct i2sbus_control *c,
4062306a36Sopenharmony_ci			   struct i2sbus_dev *i2sdev)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	struct device_node *np;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	np = i2sdev->sound.ofdev.dev.of_node;
4562306a36Sopenharmony_ci	i2sdev->enable = pmf_find_function(np, "enable");
4662306a36Sopenharmony_ci	i2sdev->cell_enable = pmf_find_function(np, "cell-enable");
4762306a36Sopenharmony_ci	i2sdev->clock_enable = pmf_find_function(np, "clock-enable");
4862306a36Sopenharmony_ci	i2sdev->cell_disable = pmf_find_function(np, "cell-disable");
4962306a36Sopenharmony_ci	i2sdev->clock_disable = pmf_find_function(np, "clock-disable");
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	/* if the bus number is not 0 or 1 we absolutely need to use
5262306a36Sopenharmony_ci	 * the platform functions -- there's nothing in Darwin that
5362306a36Sopenharmony_ci	 * would allow seeing a system behind what the FCRs are then,
5462306a36Sopenharmony_ci	 * and I don't want to go parsing a bunch of platform functions
5562306a36Sopenharmony_ci	 * by hand to try finding a system... */
5662306a36Sopenharmony_ci	if (i2sdev->bus_number != 0 && i2sdev->bus_number != 1 &&
5762306a36Sopenharmony_ci	    (!i2sdev->enable ||
5862306a36Sopenharmony_ci	     !i2sdev->cell_enable || !i2sdev->clock_enable ||
5962306a36Sopenharmony_ci	     !i2sdev->cell_disable || !i2sdev->clock_disable)) {
6062306a36Sopenharmony_ci		pmf_put_function(i2sdev->enable);
6162306a36Sopenharmony_ci		pmf_put_function(i2sdev->cell_enable);
6262306a36Sopenharmony_ci		pmf_put_function(i2sdev->clock_enable);
6362306a36Sopenharmony_ci		pmf_put_function(i2sdev->cell_disable);
6462306a36Sopenharmony_ci		pmf_put_function(i2sdev->clock_disable);
6562306a36Sopenharmony_ci		return -ENODEV;
6662306a36Sopenharmony_ci	}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	list_add(&i2sdev->item, &c->list);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	return 0;
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_civoid i2sbus_control_remove_dev(struct i2sbus_control *c,
7462306a36Sopenharmony_ci			       struct i2sbus_dev *i2sdev)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	/* this is serialised externally */
7762306a36Sopenharmony_ci	list_del(&i2sdev->item);
7862306a36Sopenharmony_ci	if (list_empty(&c->list))
7962306a36Sopenharmony_ci		i2sbus_control_destroy(c);
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ciint i2sbus_control_enable(struct i2sbus_control *c,
8362306a36Sopenharmony_ci			  struct i2sbus_dev *i2sdev)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	struct pmf_args args = { .count = 0 };
8662306a36Sopenharmony_ci	struct macio_chip *macio = c->macio;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	if (i2sdev->enable)
8962306a36Sopenharmony_ci		return pmf_call_one(i2sdev->enable, &args);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	if (macio == NULL || macio->base == NULL)
9262306a36Sopenharmony_ci		return -ENODEV;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	switch (i2sdev->bus_number) {
9562306a36Sopenharmony_ci	case 0:
9662306a36Sopenharmony_ci		/* these need to be locked or done through
9762306a36Sopenharmony_ci		 * newly created feature calls! */
9862306a36Sopenharmony_ci		MACIO_BIS(KEYLARGO_FCR1, KL1_I2S0_ENABLE);
9962306a36Sopenharmony_ci		break;
10062306a36Sopenharmony_ci	case 1:
10162306a36Sopenharmony_ci		MACIO_BIS(KEYLARGO_FCR1, KL1_I2S1_ENABLE);
10262306a36Sopenharmony_ci		break;
10362306a36Sopenharmony_ci	default:
10462306a36Sopenharmony_ci		return -ENODEV;
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci	return 0;
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ciint i2sbus_control_cell(struct i2sbus_control *c,
11062306a36Sopenharmony_ci			struct i2sbus_dev *i2sdev,
11162306a36Sopenharmony_ci			int enable)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	struct pmf_args args = { .count = 0 };
11462306a36Sopenharmony_ci	struct macio_chip *macio = c->macio;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	switch (enable) {
11762306a36Sopenharmony_ci	case 0:
11862306a36Sopenharmony_ci		if (i2sdev->cell_disable)
11962306a36Sopenharmony_ci			return pmf_call_one(i2sdev->cell_disable, &args);
12062306a36Sopenharmony_ci		break;
12162306a36Sopenharmony_ci	case 1:
12262306a36Sopenharmony_ci		if (i2sdev->cell_enable)
12362306a36Sopenharmony_ci			return pmf_call_one(i2sdev->cell_enable, &args);
12462306a36Sopenharmony_ci		break;
12562306a36Sopenharmony_ci	default:
12662306a36Sopenharmony_ci		printk(KERN_ERR "i2sbus: INVALID CELL ENABLE VALUE\n");
12762306a36Sopenharmony_ci		return -ENODEV;
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	if (macio == NULL || macio->base == NULL)
13162306a36Sopenharmony_ci		return -ENODEV;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	switch (i2sdev->bus_number) {
13462306a36Sopenharmony_ci	case 0:
13562306a36Sopenharmony_ci		if (enable)
13662306a36Sopenharmony_ci			MACIO_BIS(KEYLARGO_FCR1, KL1_I2S0_CELL_ENABLE);
13762306a36Sopenharmony_ci		else
13862306a36Sopenharmony_ci			MACIO_BIC(KEYLARGO_FCR1, KL1_I2S0_CELL_ENABLE);
13962306a36Sopenharmony_ci		break;
14062306a36Sopenharmony_ci	case 1:
14162306a36Sopenharmony_ci		if (enable)
14262306a36Sopenharmony_ci			MACIO_BIS(KEYLARGO_FCR1, KL1_I2S1_CELL_ENABLE);
14362306a36Sopenharmony_ci		else
14462306a36Sopenharmony_ci			MACIO_BIC(KEYLARGO_FCR1, KL1_I2S1_CELL_ENABLE);
14562306a36Sopenharmony_ci		break;
14662306a36Sopenharmony_ci	default:
14762306a36Sopenharmony_ci		return -ENODEV;
14862306a36Sopenharmony_ci	}
14962306a36Sopenharmony_ci	return 0;
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ciint i2sbus_control_clock(struct i2sbus_control *c,
15362306a36Sopenharmony_ci			 struct i2sbus_dev *i2sdev,
15462306a36Sopenharmony_ci			 int enable)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	struct pmf_args args = { .count = 0 };
15762306a36Sopenharmony_ci	struct macio_chip *macio = c->macio;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	switch (enable) {
16062306a36Sopenharmony_ci	case 0:
16162306a36Sopenharmony_ci		if (i2sdev->clock_disable)
16262306a36Sopenharmony_ci			return pmf_call_one(i2sdev->clock_disable, &args);
16362306a36Sopenharmony_ci		break;
16462306a36Sopenharmony_ci	case 1:
16562306a36Sopenharmony_ci		if (i2sdev->clock_enable)
16662306a36Sopenharmony_ci			return pmf_call_one(i2sdev->clock_enable, &args);
16762306a36Sopenharmony_ci		break;
16862306a36Sopenharmony_ci	default:
16962306a36Sopenharmony_ci		printk(KERN_ERR "i2sbus: INVALID CLOCK ENABLE VALUE\n");
17062306a36Sopenharmony_ci		return -ENODEV;
17162306a36Sopenharmony_ci	}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	if (macio == NULL || macio->base == NULL)
17462306a36Sopenharmony_ci		return -ENODEV;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	switch (i2sdev->bus_number) {
17762306a36Sopenharmony_ci	case 0:
17862306a36Sopenharmony_ci		if (enable)
17962306a36Sopenharmony_ci			MACIO_BIS(KEYLARGO_FCR1, KL1_I2S0_CLK_ENABLE_BIT);
18062306a36Sopenharmony_ci		else
18162306a36Sopenharmony_ci			MACIO_BIC(KEYLARGO_FCR1, KL1_I2S0_CLK_ENABLE_BIT);
18262306a36Sopenharmony_ci		break;
18362306a36Sopenharmony_ci	case 1:
18462306a36Sopenharmony_ci		if (enable)
18562306a36Sopenharmony_ci			MACIO_BIS(KEYLARGO_FCR1, KL1_I2S1_CLK_ENABLE_BIT);
18662306a36Sopenharmony_ci		else
18762306a36Sopenharmony_ci			MACIO_BIC(KEYLARGO_FCR1, KL1_I2S1_CLK_ENABLE_BIT);
18862306a36Sopenharmony_ci		break;
18962306a36Sopenharmony_ci	default:
19062306a36Sopenharmony_ci		return -ENODEV;
19162306a36Sopenharmony_ci	}
19262306a36Sopenharmony_ci	return 0;
19362306a36Sopenharmony_ci}
194