1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2003-2012 Broadcom Corporation
5 * All Rights Reserved
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in
15 *    the documentation and/or other materials provided with the
16 *    distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY BROADCOM ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL BROADCOM OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
25 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
27 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
28 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include <sys/cdefs.h>
32__FBSDID("$FreeBSD$");
33#include <sys/param.h>
34#include <sys/endian.h>
35#include <sys/systm.h>
36#include <sys/kernel.h>
37#include <sys/limits.h>
38#include <sys/bus.h>
39
40#include <dev/iicbus/iicoc.h>
41
42#include <mips/nlm/hal/haldefs.h>
43#include <mips/nlm/hal/iomap.h>
44#include <mips/nlm/hal/mips-extns.h> /* needed by board.h */
45
46#include <mips/nlm/board.h>
47
48/*
49 * We have to read the EEPROM in early boot (now only for MAC addr)
50 * but later for board information.  Use simple polled mode driver
51 * for I2C
52 */
53#define	oc_read_reg(reg)	nlm_read_reg(eeprom_i2c_base, reg)
54#define	oc_write_reg(reg, val)	nlm_write_reg(eeprom_i2c_base, reg, val)
55
56static uint64_t eeprom_i2c_base;
57
58static int
59oc_wait_on_status(uint8_t bit)
60{
61	int tries = I2C_TIMEOUT;
62	uint8_t status;
63
64	do {
65		status = oc_read_reg(OC_I2C_STATUS_REG);
66	} while ((status & bit) != 0 && --tries > 0);
67
68	return (tries == 0 ? -1: 0);
69}
70
71static int
72oc_rd_cmd(uint8_t cmd)
73{
74	uint8_t data;
75
76		oc_write_reg(OC_I2C_CMD_REG, cmd);
77		if (oc_wait_on_status(OC_STATUS_TIP) < 0)
78			return (-1);
79
80	data = oc_read_reg(OC_I2C_DATA_REG);
81	return (data);
82}
83
84static int
85oc_wr_cmd(uint8_t data, uint8_t cmd)
86{
87	oc_write_reg(OC_I2C_DATA_REG, data);
88	oc_write_reg(OC_I2C_CMD_REG, cmd);
89
90	if (oc_wait_on_status(OC_STATUS_TIP) < 0)
91		return (-1);
92	return (0);
93}
94
95int
96nlm_board_eeprom_read(int node, int bus, int addr, int offs, uint8_t *buf,
97    int sz)
98{
99	int rd, i;
100	char *err = NULL;
101
102	eeprom_i2c_base = nlm_pcicfg_base(XLP_IO_I2C_OFFSET(node, bus)) +
103	    XLP_IO_PCI_HDRSZ;
104
105	if (oc_wait_on_status(OC_STATUS_BUSY) < 0) {
106		err = "Not idle";
107		goto err_exit;
108	}
109
110	/* write start */
111	if (oc_wr_cmd(addr, OC_COMMAND_START)) {
112		err = "I2C write start failed.";
113		goto err_exit;
114	}
115
116	if (oc_read_reg(OC_I2C_STATUS_REG) & OC_STATUS_NACK) {
117		err = "No ack after start";
118		goto err_exit_stop;
119	}
120
121	if (oc_read_reg(OC_I2C_STATUS_REG) & OC_STATUS_AL) {
122		err = "I2C Bus Arbitration Lost";
123		goto err_exit_stop;
124	}
125
126	/* Write offset */
127	if (oc_wr_cmd(offs, OC_COMMAND_WRITE)) {
128		err = "I2C write slave offset failed.";
129		goto err_exit_stop;
130	}
131
132	if (oc_read_reg(OC_I2C_STATUS_REG) & OC_STATUS_NACK) {
133		err = "No ack after write";
134		goto err_exit_stop;
135	}
136
137	/* read start */
138	if (oc_wr_cmd(addr | 1, OC_COMMAND_START)) {
139		err = "I2C read start failed.";
140		goto err_exit_stop;
141	}
142
143	if (oc_read_reg(OC_I2C_STATUS_REG) & OC_STATUS_NACK) {
144		err = "No ack after read start";
145		goto err_exit_stop;
146	}
147
148	for (i = 0; i < sz - 1; i++) {
149		if ((rd = oc_rd_cmd(OC_COMMAND_READ)) < 0) {
150			err = "I2C read data byte failed.";
151			goto err_exit_stop;
152		}
153	buf[i] = rd;
154	}
155
156	/* last byte */
157	if ((rd = oc_rd_cmd(OC_COMMAND_RDNACK)) < 0) {
158		err = "I2C read last data byte failed.";
159		goto err_exit_stop;
160	}
161	buf[sz - 1] = rd;
162
163err_exit_stop:
164	oc_write_reg(OC_I2C_CMD_REG, OC_COMMAND_STOP);
165	if (oc_wait_on_status(OC_STATUS_BUSY) < 0)
166		printf("%s: stop failed", __func__);
167
168err_exit:
169	if (err) {
170		printf("%s: Failed (%s)\n", __func__, err);
171		return (-1);
172	}
173	return (0);
174}
175