diff options
-rw-r--r-- | core/Makefile.inc | 2 | ||||
-rw-r--r-- | core/i2c.c | 116 | ||||
-rw-r--r-- | hw/p8-i2c.c | 126 | ||||
-rw-r--r-- | include/i2c.h | 28 | ||||
-rw-r--r-- | platforms/astbmc/common.c | 2 |
5 files changed, 152 insertions, 122 deletions
diff --git a/core/Makefile.inc b/core/Makefile.inc index 5098d2d..475e2c7 100644 --- a/core/Makefile.inc +++ b/core/Makefile.inc @@ -7,7 +7,7 @@ CORE_OBJS += timebase.o opal-msg.o pci.o pci-opal.o fast-reboot.o CORE_OBJS += device.o exceptions.o trace.o affinity.o vpd.o CORE_OBJS += hostservices.o platform.o nvram.o flash-nvram.o hmi.o CORE_OBJS += console-log.o ipmi.o time-utils.o pel.o pool.o errorlog.o -CORE_OBJS += timer.o +CORE_OBJS += timer.o i2c.o CORE=core/built-in.o $(CORE): $(CORE_OBJS:%=core/%) diff --git a/core/i2c.c b/core/i2c.c new file mode 100644 index 0000000..2eb77db --- /dev/null +++ b/core/i2c.c @@ -0,0 +1,116 @@ +/* Copyright 2013-2014 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <skiboot.h> +#include <i2c.h> +#include <opal.h> +#include <device.h> +#include <opal-msg.h> + +LIST_HEAD(i2c_bus_list); + +/* Used to assign OPAL IDs */ +static uint32_t i2c_next_bus; + +void i2c_add_bus(struct i2c_bus *bus) +{ + bus->opal_id = ++i2c_next_bus; + dt_add_property_cells(bus->dt_node, "ibm,opal-id", bus->opal_id); + + list_add_tail(&i2c_bus_list, &bus->link); +} + +struct i2c_bus *i2c_find_bus_by_id(uint32_t opal_id) +{ + struct i2c_bus *bus; + + list_for_each(&i2c_bus_list, bus, link) { + if (bus->opal_id == opal_id) + return bus; + } + return NULL; +} + +static void opal_i2c_request_complete(int rc, struct i2c_request *req) +{ + uint64_t token = (uint64_t)(unsigned long)req->user_data; + + opal_queue_msg(OPAL_MSG_ASYNC_COMP, NULL, NULL, token, rc); + i2c_free_req(req); +} + +static int opal_i2c_request(uint64_t async_token, uint32_t bus_id, + struct opal_i2c_request *oreq) +{ + struct i2c_bus *bus = NULL; + struct i2c_request *req; + int rc; + + if (oreq->flags & OPAL_I2C_ADDR_10) + return OPAL_UNSUPPORTED; + + bus = i2c_find_bus_by_id(bus_id); + if (!bus) { + prlog(PR_ERR, "I2C: Invalid 'bus_id' passed to the OPAL\n"); + return OPAL_PARAMETER; + } + + req = i2c_alloc_req(bus); + if (!req) { + prlog(PR_ERR, "I2C: Failed to allocate 'i2c_request'\n"); + return OPAL_NO_MEM; + } + + switch(oreq->type) { + case OPAL_I2C_RAW_READ: + req->op = I2C_READ; + break; + case OPAL_I2C_RAW_WRITE: + req->op = I2C_WRITE; + break; + case OPAL_I2C_SM_READ: + req->op = SMBUS_READ; + req->offset = oreq->subaddr; + req->offset_bytes = oreq->subaddr_sz; + break; + case OPAL_I2C_SM_WRITE: + req->op = SMBUS_WRITE; + req->offset = oreq->subaddr; + req->offset_bytes = oreq->subaddr_sz; + break; + default: + bus->free_req(req); + return OPAL_PARAMETER; + } + req->dev_addr = oreq->addr; + req->rw_len = oreq->size; + req->rw_buf = (void *)oreq->buffer_ra; + req->completion = opal_i2c_request_complete; + req->user_data = (void *)(unsigned long)async_token; + req->bus = bus; + + /* Finally, queue the OPAL i2c request and return */ + rc = i2c_queue_req(req); + if (rc) { + i2c_free_req(req); + return rc; + } + + return OPAL_ASYNC_COMPLETION; +} +opal_call(OPAL_I2C_REQUEST, opal_i2c_request, 3); + + diff --git a/hw/p8-i2c.c b/hw/p8-i2c.c index ef17c40..e3f35b1 100644 --- a/hw/p8-i2c.c +++ b/hw/p8-i2c.c @@ -16,7 +16,6 @@ #undef DEBUG -#include <fsp.h> #include <opal.h> #include <lock.h> #include <chip.h> @@ -24,7 +23,6 @@ #include <xscom.h> #include <timebase.h> #include <timer.h> -#include <opal-msg.h> #ifdef DEBUG #define DBG(fmt...) prlog(PR_ERR, "I2C: " fmt) @@ -192,7 +190,6 @@ struct p8_i2c_master { struct p8_i2c_master_port { struct i2c_bus bus; /* Abstract bus struct for the client */ struct p8_i2c_master *common; - uint32_t bus_id; uint32_t port_num; }; @@ -202,8 +199,6 @@ struct p8_i2c_request { uint64_t timeout; }; -static LIST_HEAD(i2c_bus_list); - static bool p8_i2c_has_irqs(void) { struct proc_chip *chip = next_chip(NULL); @@ -830,8 +825,9 @@ static void p8_i2c_check_work(struct p8_i2c_master *master) } } -static int p8_i2c_queue_request(struct i2c_bus *bus, struct i2c_request *req) +static int p8_i2c_queue_request(struct i2c_request *req) { + struct i2c_bus *bus = req->bus; struct p8_i2c_master_port *port = container_of(bus, struct p8_i2c_master_port, bus); struct p8_i2c_master *master = port->common; @@ -1014,85 +1010,11 @@ void p8_i2c_interrupt(void) p8_i2c_poll_each_master(true); } -static void opal_p8_i2c_request_complete(int rc, struct i2c_request *req) -{ - uint64_t token = (uint64_t)(unsigned long)req->user_data; - - opal_queue_msg(OPAL_MSG_ASYNC_COMP, NULL, NULL, token, rc); - req->bus->free_req(req); -} - -static int opal_p8_i2c_request(uint64_t async_token, uint32_t bus_id, - struct opal_i2c_request *oreq) -{ - struct p8_i2c_master_port *port; - struct i2c_bus *bus = NULL; - struct i2c_request *req; - int rc; - - if (oreq->flags & OPAL_I2C_ADDR_10) - return OPAL_UNSUPPORTED; - - list_for_each(&i2c_bus_list, bus, link) { - port = container_of(bus, struct p8_i2c_master_port, bus); - if (port->bus_id == bus_id) - break; - } - if (!bus) { - prlog(PR_ERR, "I2C: Invalid 'bus_id' passed to the OPAL\n"); - return OPAL_PARAMETER; - } - - req = bus->alloc_req(bus); - if (!req) { - prlog(PR_ERR, "I2C: Failed to allocate 'i2c_request'\n"); - return OPAL_NO_MEM; - } - - switch(oreq->type) { - case OPAL_I2C_RAW_READ: - req->op = I2C_READ; - break; - case OPAL_I2C_RAW_WRITE: - req->op = I2C_WRITE; - break; - case OPAL_I2C_SM_READ: - req->op = SMBUS_READ; - req->offset = oreq->subaddr; - req->offset_bytes = oreq->subaddr_sz; - break; - case OPAL_I2C_SM_WRITE: - req->op = SMBUS_WRITE; - req->offset = oreq->subaddr; - req->offset_bytes = oreq->subaddr_sz; - break; - default: - bus->free_req(req); - return OPAL_PARAMETER; - } - req->dev_addr = oreq->addr; - req->rw_len = oreq->size; - req->rw_buf = (void *)oreq->buffer_ra; - req->completion = opal_p8_i2c_request_complete; - req->user_data = (void *)(unsigned long)async_token; - req->bus = bus; - - /* Finally, queue the OPAL i2c request and return */ - rc = bus->queue_req(bus, req); - if (rc) { - bus->free_req(req); - return rc; - } - - return OPAL_ASYNC_COMPLETION; -} - void p8_i2c_init(void) { - struct p8_i2c_master_port *port, *prev_port; + struct p8_i2c_master_port *port; uint32_t bus_speed, lb_freq, count; struct dt_node *i2cm, *i2cm_port; - struct i2c_bus *bus, *next_bus; struct p8_i2c_master *master; uint64_t ex_stat; static bool irq_printed; @@ -1102,7 +1024,7 @@ void p8_i2c_init(void) master = zalloc(sizeof(*master)); if (!master) { prlog(PR_ERR,"I2C: Failed to allocate p8_i2c_master\n"); - goto exit_free_list; + break; } /* Bus speed in KHz */ @@ -1119,7 +1041,8 @@ void p8_i2c_init(void) I2C_EXTD_STAT_REG, &ex_stat); if (rc) { prlog(PR_ERR, "I2C: Failed to read EXTD_STAT_REG\n"); - goto exit_free_master; + free(master); + break; } master->fifo_size = GETFIELD(I2C_EXTD_STAT_FIFO_SIZE, ex_stat); @@ -1145,19 +1068,18 @@ void p8_i2c_init(void) port = zalloc(sizeof(*port) * count); if (!port) { prlog(PR_ERR, "I2C: Insufficient memory\n"); - goto exit_free_master; + free(master); + break; } dt_for_each_child(i2cm, i2cm_port) { - port->bus_id = dt_prop_get_u32(i2cm_port, - "ibm,opal-id"); port->port_num = dt_prop_get_u32(i2cm_port, "reg"); port->common = master; - port->bus.i2c_port = i2cm_port; + port->bus.dt_node = i2cm_port; port->bus.queue_req = p8_i2c_queue_request; port->bus.alloc_req = p8_i2c_alloc_request; port->bus.free_req = p8_i2c_free_request; - list_add_tail(&i2c_bus_list, &port->bus.link); + i2c_add_bus(&port->bus); port++; } } @@ -1167,32 +1089,4 @@ void p8_i2c_init(void) * and thus no interrupts */ opal_add_poller(p8_i2c_opal_poll, NULL); - - /* Register the OPAL interface */ - opal_register(OPAL_I2C_REQUEST, opal_p8_i2c_request, 3); - - return; - -exit_free_master: - free(master); -exit_free_list: - prev_port = NULL; - list_for_each_safe(&i2c_bus_list, bus, next_bus, link) { - port = container_of(bus, struct p8_i2c_master_port, bus); - if (!prev_port) { - prev_port = port; - continue; - } else if (prev_port->common == port->common) { - continue; - } else { - free(prev_port->common); - free(prev_port); - prev_port = NULL; - } - } - - if (prev_port) { /* Last node left */ - free(prev_port->common); - free(prev_port); - } } diff --git a/include/i2c.h b/include/i2c.h index af88ba0..2647bdc 100644 --- a/include/i2c.h +++ b/include/i2c.h @@ -21,9 +21,9 @@ struct i2c_request; struct i2c_bus { struct list_node link; - struct dt_node *i2c_port; - int (*queue_req)(struct i2c_bus *bus, - struct i2c_request *req); + struct dt_node *dt_node; + uint32_t opal_id; + int (*queue_req)(struct i2c_request *req); struct i2c_request *(*alloc_req)(struct i2c_bus *bus); void (*free_req)(struct i2c_request *req); }; @@ -47,6 +47,28 @@ struct i2c_request { void *user_data; /* Client data */ }; +extern struct list_head i2c_bus_list; + +/* Generic i2c */ +extern void i2c_add_bus(struct i2c_bus *bus); +extern struct i2c_bus *i2c_find_bus_by_id(uint32_t opal_id); + +static inline struct i2c_request *i2c_alloc_req(struct i2c_bus *bus) +{ + return bus->alloc_req(bus); +} + +static inline void i2c_free_req(struct i2c_request *req) +{ + req->bus->free_req(req); +} + +static inline int i2c_queue_req(struct i2c_request *req) +{ + return req->bus->queue_req(req); +} + +/* P8 implementation details */ extern void p8_i2c_init(void); extern void p8_i2c_interrupt(void); diff --git a/platforms/astbmc/common.c b/platforms/astbmc/common.c index f79a5c5..e2d8629 100644 --- a/platforms/astbmc/common.c +++ b/platforms/astbmc/common.c @@ -174,7 +174,6 @@ static struct dt_node *dt_create_i2c_bus(struct dt_node *i2cm, const char *port_ uint32_t port_id) { static struct dt_node *port; - static uint32_t bus_id = 0; port = dt_new_addr(i2cm, "i2c-bus", port_id); if (!port) @@ -183,7 +182,6 @@ static struct dt_node *dt_create_i2c_bus(struct dt_node *i2cm, const char *port_ dt_add_property_string(port, "compatible", "ibm,power8-i2c-port"); dt_add_property_string(port, "port-name", port_name); dt_add_property_cells(port, "reg", port_id); - dt_add_property_cells(port, "ibm,opal-id", ++bus_id); dt_add_property_cells(port, "#address-cells", 1); dt_add_property_cells(port, "#size-cells", 0); |