summaryrefslogtreecommitdiff
path: root/sys/dev/i2c/ac100.c
blob: 6f0547359aeecb5d6745820af7efd9bc20d61838 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
/* $NetBSD: ac100.c,v 1.7 2021/01/27 02:29:48 thorpej Exp $ */

/*-
 * Copyright (c) 2014 Jared D. McNeill <jmcneill@invisible.ca>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "opt_fdt.h"

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ac100.c,v 1.7 2021/01/27 02:29:48 thorpej Exp $");

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <sys/conf.h>
#include <sys/bus.h>
#include <sys/kmem.h>

#include <dev/clock_subr.h>

#include <dev/i2c/i2cvar.h>

#ifdef FDT
#include <dev/fdt/fdtvar.h>
#endif

#define AC100_CHIP_AUDIO_RST_REG	0x00
#define AC100_PLL_CTRL1_REG		0x02
#define AC100_PLL_CTRL2_REG		0x03
#define AC100_SYSCLK_CTRL_REG		0x04
#define AC100_MOD_RST_CTRL_REG		0x05
#define AC100_ADDA_SR_CTRL_REG		0x06
#define AC100_I2S1_LCK_CTRL_REG		0x10
#define AC100_I2S1_SDIN_CTRL_REG	0x11
#define AC100_I2S1_SDOUT_CTRL_REG	0x12
#define AC100_I2S1_DIG_MIXER_REG	0x13
#define AC100_I2S1_VOL_CTRL1_REG	0x14
#define AC100_I2S1_VOL_CTRL2_REG	0x15
#define AC100_I2S1_VOL_CTRL3_REG	0x16
#define AC100_I2S1_VOL_CTRL4_REG	0x17
#define AC100_I2S1_MXR_GAIN_REG		0x18
#define AC100_I2S2_CLK_CTRL_REG		0x20
#define AC100_I2S2_SDIN_CTRL_REG	0x21
#define AC100_I2S2_SDOUT_CTRL_REG	0x22
#define AC100_I2S2_DIG_MIXER_REG	0x23
#define AC100_I2S2_VOL_CTRL1_REG	0x24
#define AC100_I2S2_VOL_CTRL2_REG	0x26
#define AC100_I2S2_MXR_GAIN_REG		0x28
#define AC100_I2S3_CLK_CTRL_REG		0x30
#define AC100_I2S3_SDIN_CTRL_REG	0x31
#define AC100_I2S3_SDOUT_CTRL_REG	0x32
#define AC100_I2S3_SGP_CTRL_REG		0x33
#define AC100_ADC_DIG_CTRL_REG		0x40

#define AC100_RTC_RESET_REG		0xc6
#define AC100_RTC_CTRL_REG		0xc7
#define AC100_RTC_SEC_REG		0xc8
#define AC100_RTC_MIN_REG		0xc9
#define AC100_RTC_HOU_REG		0xca
#define AC100_RTC_WEE_REG		0xcb
#define AC100_RTC_DAY_REG		0xcc
#define AC100_RTC_MON_REG		0xcd
#define AC100_RTC_YEA_REG		0xce
#define AC100_RTC_UPD_TRIG_REG		0xcf

#define AC100_RTC_GP_REG(n)		(0xe0 + (n))

#define AC100_RTC_CTRL_12H_24H_MODE	__BIT(0)

#define AC100_RTC_UPD_TRIG_WRITE	__BIT(15)

static const struct device_compatible_entry compat_data[] = {
	{ .compat = "x-powers,ac100" },
	DEVICE_COMPAT_EOL
};

struct ac100_softc {
	device_t	sc_dev;
	i2c_tag_t	sc_i2c;
	i2c_addr_t	sc_addr;

	struct todr_chip_handle sc_todr;
};

static int	ac100_match(device_t, cfdata_t, void *);
static void	ac100_attach(device_t, device_t, void *);

static int	ac100_rtc_gettime(todr_chip_handle_t, struct clock_ymdhms *);
static int	ac100_rtc_settime(todr_chip_handle_t, struct clock_ymdhms *);

static int	ac100_read(struct ac100_softc *, uint8_t, uint16_t *);
static int	ac100_write(struct ac100_softc *, uint8_t, uint16_t);

CFATTACH_DECL_NEW(ac100ic, sizeof(struct ac100_softc),
    ac100_match, ac100_attach, NULL, NULL);

static int
ac100_match(device_t parent, cfdata_t match, void *aux)
{
	struct i2c_attach_args *ia = aux;
	int match_result;

	if (iic_use_direct_match(ia, match, compat_data, &match_result))
		return match_result;

	/* This device is direct-config only. */

	return 0;
}

static void
ac100_attach(device_t parent, device_t self, void *aux)
{
	struct ac100_softc *sc = device_private(self);
	struct i2c_attach_args *ia = aux;

	sc->sc_dev = self;
	sc->sc_i2c = ia->ia_tag;
	sc->sc_addr = ia->ia_addr;

	aprint_naive("\n");
	aprint_normal(": CODEC/RTC\n");

	iic_acquire_bus(sc->sc_i2c, 0);
	ac100_write(sc, AC100_RTC_CTRL_REG, AC100_RTC_CTRL_12H_24H_MODE);
	iic_release_bus(sc->sc_i2c, 0);

	sc->sc_todr.todr_gettime_ymdhms = ac100_rtc_gettime;
	sc->sc_todr.todr_settime_ymdhms = ac100_rtc_settime;
	sc->sc_todr.cookie = sc;

#ifdef FDT
	const int phandle = ia->ia_cookie;
	const int rtc_phandle = of_find_firstchild_byname(phandle, "rtc");
	if (rtc_phandle > 0)
		fdtbus_todr_attach(self, rtc_phandle, &sc->sc_todr);
#else
	todr_attach(&sc->sc_todr);
#endif
}

static int
ac100_read(struct ac100_softc *sc, uint8_t reg, uint16_t *val)
{
	return iic_smbus_read_word(sc->sc_i2c, sc->sc_addr, reg, val, 0);
}

static int
ac100_write(struct ac100_softc *sc, uint8_t reg, uint16_t val)
{
	return iic_smbus_write_word(sc->sc_i2c, sc->sc_addr, reg, val, 0);
}

static int
ac100_rtc_gettime(todr_chip_handle_t tch, struct clock_ymdhms *dt)
{
	struct ac100_softc *sc = tch->cookie;
	uint16_t sec, min, hou, wee, day, mon, yea;

	iic_acquire_bus(sc->sc_i2c, 0);
	ac100_read(sc, AC100_RTC_SEC_REG, &sec);
	ac100_read(sc, AC100_RTC_MIN_REG, &min);
	ac100_read(sc, AC100_RTC_HOU_REG, &hou);
	ac100_read(sc, AC100_RTC_WEE_REG, &wee);
	ac100_read(sc, AC100_RTC_DAY_REG, &day);
	ac100_read(sc, AC100_RTC_MON_REG, &mon);
	ac100_read(sc, AC100_RTC_YEA_REG, &yea);
	iic_release_bus(sc->sc_i2c, 0);

	dt->dt_year = POSIX_BASE_YEAR + bcdtobin(yea & 0xff);
	dt->dt_mon = bcdtobin(mon & 0x1f);
	dt->dt_day = bcdtobin(day & 0x3f);
	dt->dt_wday = bcdtobin(wee & 0x7);
	dt->dt_hour = bcdtobin(hou & 0x3f);
	dt->dt_min = bcdtobin(min & 0x7f);
	dt->dt_sec = bcdtobin(sec & 0x7f);

	return 0;
}

static int
ac100_rtc_settime(todr_chip_handle_t tch, struct clock_ymdhms *dt)
{
	struct ac100_softc *sc = tch->cookie;

	iic_acquire_bus(sc->sc_i2c, 0);
	ac100_write(sc, AC100_RTC_SEC_REG, bintobcd(dt->dt_sec) & 0x7f);
	ac100_write(sc, AC100_RTC_MIN_REG, bintobcd(dt->dt_min) & 0x7f);
	ac100_write(sc, AC100_RTC_HOU_REG, bintobcd(dt->dt_hour) & 0x3f);
	ac100_write(sc, AC100_RTC_WEE_REG, bintobcd(dt->dt_wday) & 0x7);
	ac100_write(sc, AC100_RTC_DAY_REG, bintobcd(dt->dt_day) & 0x3f);
	ac100_write(sc, AC100_RTC_MON_REG, bintobcd(dt->dt_mon) & 0x1f);
	ac100_write(sc, AC100_RTC_YEA_REG,
	    bintobcd(dt->dt_year - POSIX_BASE_YEAR) & 0xff);
	ac100_write(sc, AC100_RTC_UPD_TRIG_REG, AC100_RTC_UPD_TRIG_WRITE);
	iic_release_bus(sc->sc_i2c, 0);

	return 0;
}